Update to spec v0.11 (#959)
* Update process_final_updates() hysteresis computation * Update core to v0.11.1 * Bump tags to v0.11.1 * Update docs and deposit contract * Add compute_fork_digest * Address review comments Co-authored-by: Herman Alonso Junge <alonso.junge@gmail.com>
This commit is contained in:
parent
e04fc8ddb4
commit
26bdc2927b
@ -39,7 +39,7 @@ Like all Ethereum 2.0 clients, Lighthouse is a work-in-progress.
|
||||
|
||||
Current development overview:
|
||||
|
||||
- Specification `v0.10.1` implemented, optimized and passing test vectors.
|
||||
- Specification `v0.11.1` implemented, optimized and passing test vectors.
|
||||
- Rust-native libp2p with Gossipsub and Discv5.
|
||||
- RESTful JSON API via HTTP server.
|
||||
- Events via WebSocket.
|
||||
|
@ -153,6 +153,8 @@ pub struct HeadInfo {
|
||||
pub current_justified_checkpoint: types::Checkpoint,
|
||||
pub finalized_checkpoint: types::Checkpoint,
|
||||
pub fork: Fork,
|
||||
pub genesis_time: u64,
|
||||
pub genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub trait BeaconChainTypes: Send + Sync + 'static {
|
||||
@ -492,6 +494,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
current_justified_checkpoint: head.beacon_state.current_justified_checkpoint.clone(),
|
||||
finalized_checkpoint: head.beacon_state.finalized_checkpoint.clone(),
|
||||
fork: head.beacon_state.fork.clone(),
|
||||
genesis_time: head.beacon_state.genesis_time,
|
||||
genesis_validators_root: head.beacon_state.genesis_validators_root,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1031,17 +1035,23 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
})
|
||||
.collect::<Result<Vec<&PublicKey>, Error>>()?;
|
||||
|
||||
let fork = self
|
||||
let (fork, genesis_validators_root) = self
|
||||
.canonical_head
|
||||
.try_read_for(HEAD_LOCK_TIMEOUT)
|
||||
.ok_or_else(|| Error::CanonicalHeadLockTimeout)
|
||||
.map(|head| head.beacon_state.fork.clone())?;
|
||||
.map(|head| {
|
||||
(
|
||||
head.beacon_state.fork.clone(),
|
||||
head.beacon_state.genesis_validators_root,
|
||||
)
|
||||
})?;
|
||||
|
||||
let signature_set = indexed_attestation_signature_set_from_pubkeys(
|
||||
pubkeys,
|
||||
&attestation.signature,
|
||||
&indexed_attestation,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
&self.spec,
|
||||
)
|
||||
.map_err(Error::SignatureSetError)?;
|
||||
@ -1074,8 +1084,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// Provide the valid attestation to op pool, which may choose to retain the
|
||||
// attestation for inclusion in a future block.
|
||||
if self.eth1_chain.is_some() {
|
||||
self.op_pool
|
||||
.insert_attestation(attestation, &fork, &self.spec)?;
|
||||
self.op_pool.insert_attestation(
|
||||
attestation,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
&self.spec,
|
||||
)?;
|
||||
};
|
||||
|
||||
Ok(AttestationProcessingOutcome::Processed)
|
||||
@ -1547,6 +1561,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let mut block = SignedBeaconBlock {
|
||||
message: BeaconBlock {
|
||||
slot: state.slot,
|
||||
proposer_index: state.get_beacon_proposer_index(state.slot, &self.spec)? as u64,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBody {
|
||||
|
@ -313,7 +313,9 @@ where
|
||||
|
||||
let randao_reveal = {
|
||||
let epoch = slot.epoch(E::slots_per_epoch());
|
||||
let domain = self.spec.get_domain(epoch, Domain::Randao, fork);
|
||||
let domain =
|
||||
self.spec
|
||||
.get_domain(epoch, Domain::Randao, fork, state.genesis_validators_root);
|
||||
let message = epoch.signing_root(domain);
|
||||
Signature::new(message.as_bytes(), sk)
|
||||
};
|
||||
@ -323,7 +325,7 @@ where
|
||||
.produce_block_on_state(state, slot, randao_reveal)
|
||||
.expect("should produce block");
|
||||
|
||||
let signed_block = block.sign(sk, &state.fork, &self.spec);
|
||||
let signed_block = block.sign(sk, &state.fork, state.genesis_validators_root, &self.spec);
|
||||
|
||||
(signed_block, state)
|
||||
}
|
||||
@ -408,6 +410,7 @@ where
|
||||
attestation.data.target.epoch,
|
||||
Domain::BeaconAttester,
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = attestation.data.signing_root(domain);
|
||||
|
@ -494,7 +494,15 @@ pub fn get_genesis_time<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
) -> ApiResult {
|
||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head()?.beacon_state.genesis_time)
|
||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_time)
|
||||
}
|
||||
|
||||
/// Read the `genesis_validators_root` from the current beacon chain state.
|
||||
pub fn get_genesis_validators_root<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
) -> ApiResult {
|
||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_validators_root)
|
||||
}
|
||||
|
||||
pub fn proposer_slashing<T: BeaconChainTypes>(
|
||||
|
@ -37,13 +37,13 @@ pub struct VoteCount {
|
||||
impl Into<VoteCount> for TotalBalances {
|
||||
fn into(self) -> VoteCount {
|
||||
VoteCount {
|
||||
current_epoch_active_gwei: self.current_epoch,
|
||||
previous_epoch_active_gwei: self.previous_epoch,
|
||||
current_epoch_attesting_gwei: self.current_epoch_attesters,
|
||||
current_epoch_target_attesting_gwei: self.current_epoch_target_attesters,
|
||||
previous_epoch_attesting_gwei: self.previous_epoch_attesters,
|
||||
previous_epoch_target_attesting_gwei: self.previous_epoch_target_attesters,
|
||||
previous_epoch_head_attesting_gwei: self.previous_epoch_head_attesters,
|
||||
current_epoch_active_gwei: self.current_epoch(),
|
||||
previous_epoch_active_gwei: self.previous_epoch(),
|
||||
current_epoch_attesting_gwei: self.current_epoch_attesters(),
|
||||
current_epoch_target_attesting_gwei: self.current_epoch_target_attesters(),
|
||||
previous_epoch_attesting_gwei: self.previous_epoch_attesters(),
|
||||
previous_epoch_target_attesting_gwei: self.previous_epoch_target_attesters(),
|
||||
previous_epoch_head_attesting_gwei: self.previous_epoch_head_attesters(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,9 @@ pub fn route<T: BeaconChainTypes>(
|
||||
(&Method::GET, "/beacon/genesis_time") => {
|
||||
into_boxfut(beacon::get_genesis_time::<T>(req, beacon_chain))
|
||||
}
|
||||
(&Method::GET, "/beacon/genesis_validators_root") => {
|
||||
into_boxfut(beacon::get_genesis_validators_root::<T>(req, beacon_chain))
|
||||
}
|
||||
(&Method::GET, "/beacon/validators") => {
|
||||
into_boxfut(beacon::get_validators::<T>(req, beacon_chain))
|
||||
}
|
||||
|
@ -47,17 +47,15 @@ fn get_randao_reveal<T: BeaconChainTypes>(
|
||||
slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Signature {
|
||||
let fork = beacon_chain
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_state
|
||||
.fork;
|
||||
let head = beacon_chain.head().expect("should get head");
|
||||
let fork = head.beacon_state.fork;
|
||||
let genesis_validators_root = head.beacon_state.genesis_validators_root;
|
||||
let proposer_index = beacon_chain
|
||||
.block_proposer(slot)
|
||||
.expect("should get proposer index");
|
||||
let keypair = generate_deterministic_keypair(proposer_index);
|
||||
let epoch = slot.epoch(E::slots_per_epoch());
|
||||
let domain = spec.get_domain(epoch, Domain::Randao, &fork);
|
||||
let domain = spec.get_domain(epoch, Domain::Randao, &fork, genesis_validators_root);
|
||||
let message = epoch.signing_root(domain);
|
||||
Signature::new(message.as_bytes(), &keypair.sk)
|
||||
}
|
||||
@ -68,16 +66,14 @@ fn sign_block<T: BeaconChainTypes>(
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
spec: &ChainSpec,
|
||||
) -> SignedBeaconBlock<T::EthSpec> {
|
||||
let fork = beacon_chain
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_state
|
||||
.fork;
|
||||
let head = beacon_chain.head().expect("should get head");
|
||||
let fork = head.beacon_state.fork;
|
||||
let genesis_validators_root = head.beacon_state.genesis_validators_root;
|
||||
let proposer_index = beacon_chain
|
||||
.block_proposer(block.slot)
|
||||
.expect("should get proposer index");
|
||||
let keypair = generate_deterministic_keypair(proposer_index);
|
||||
block.sign(&keypair.sk, &fork, spec)
|
||||
block.sign(&keypair.sk, &fork, genesis_validators_root, spec)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -156,6 +152,7 @@ fn validator_produce_attestation() {
|
||||
.attestation_committee_position
|
||||
.expect("should have committee position"),
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
.expect("should sign attestation");
|
||||
@ -564,6 +561,31 @@ fn genesis_time() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_validators_root() {
|
||||
let mut env = build_env();
|
||||
|
||||
let node = build_node(&mut env, testing_client_config());
|
||||
let remote_node = node.remote_node().expect("should produce remote node");
|
||||
|
||||
let genesis_validators_root = env
|
||||
.runtime()
|
||||
.block_on(remote_node.http.beacon().get_genesis_validators_root())
|
||||
.expect("should fetch genesis time from http api");
|
||||
|
||||
assert_eq!(
|
||||
node.client
|
||||
.beacon_chain()
|
||||
.expect("should have beacon chain")
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_state
|
||||
.genesis_validators_root,
|
||||
genesis_validators_root,
|
||||
"should match genesis time from head state"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fork() {
|
||||
let mut env = build_env();
|
||||
@ -903,6 +925,7 @@ fn proposer_slashing() {
|
||||
proposer_index as u64,
|
||||
&key,
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
@ -927,6 +950,7 @@ fn proposer_slashing() {
|
||||
proposer_index as u64,
|
||||
&key,
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
invalid_proposer_slashing.signed_header_2 = invalid_proposer_slashing.signed_header_1.clone();
|
||||
@ -981,6 +1005,7 @@ fn attester_slashing() {
|
||||
&validator_indices[..],
|
||||
&secret_keys[..],
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
@ -1006,6 +1031,7 @@ fn attester_slashing() {
|
||||
&validator_indices[..],
|
||||
&secret_keys[..],
|
||||
fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
invalid_attester_slashing.attestation_2 = invalid_attester_slashing.attestation_1.clone();
|
||||
|
@ -11,7 +11,7 @@ use types::*;
|
||||
///
|
||||
/// Utilises lazy-loading from separate storage for its vector fields.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Encode, Decode)]
|
||||
pub struct PartialBeaconState<T>
|
||||
where
|
||||
@ -19,6 +19,7 @@ where
|
||||
{
|
||||
// Versioning
|
||||
pub genesis_time: u64,
|
||||
pub genesis_validators_root: Hash256,
|
||||
pub slot: Slot,
|
||||
pub fork: Fork,
|
||||
|
||||
@ -72,6 +73,7 @@ impl<T: EthSpec> PartialBeaconState<T> {
|
||||
// TODO: could use references/Cow for fields to avoid cloning
|
||||
PartialBeaconState {
|
||||
genesis_time: s.genesis_time,
|
||||
genesis_validators_root: s.genesis_validators_root,
|
||||
slot: s.slot,
|
||||
fork: s.fork.clone(),
|
||||
|
||||
@ -181,6 +183,7 @@ impl<E: EthSpec> TryInto<BeaconState<E>> for PartialBeaconState<E> {
|
||||
|
||||
Ok(BeaconState {
|
||||
genesis_time: self.genesis_time,
|
||||
genesis_validators_root: self.genesis_validators_root,
|
||||
slot: self.slot,
|
||||
fork: self.fork,
|
||||
|
||||
|
@ -166,6 +166,7 @@ Returns an object containing a single [`SignedBeaconBlock`](https://github.com/e
|
||||
"beacon_block": {
|
||||
"message": {
|
||||
"slot": 0,
|
||||
"proposer_index": 14,
|
||||
"parent_root": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"state_root": "0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f",
|
||||
"body": {
|
||||
@ -444,7 +445,7 @@ canonical chain.
|
||||
### Returns
|
||||
|
||||
Returns an object containing a single
|
||||
[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beaconstate)
|
||||
[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#beaconstate)
|
||||
and its tree hash root.
|
||||
|
||||
### Example Response
|
||||
@ -454,6 +455,7 @@ and its tree hash root.
|
||||
"root": "0x528e54ca5d4c957729a73f40fc513ae312e054c7295775c4a2b21f423416a72b",
|
||||
"beacon_state": {
|
||||
"genesis_time": 1575652800,
|
||||
"genesis_validators_root": "0xa8a9226edee1b2627fb4117d7dea4996e64dec2998f37f6e824f74f2ce39a538",
|
||||
"slot": 18478
|
||||
}
|
||||
}
|
||||
@ -505,7 +507,7 @@ Typical Responses | 200
|
||||
### Returns
|
||||
|
||||
Returns an object containing the genesis
|
||||
[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beaconstate).
|
||||
[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#beaconstate).
|
||||
|
||||
### Example Response
|
||||
|
||||
@ -565,7 +567,7 @@ Typical Responses | 200
|
||||
|
||||
### Returns
|
||||
|
||||
Returns an object containing the [`Fork`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#fork) of the current head.
|
||||
Returns an object containing the [`Fork`](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#fork) of the current head.
|
||||
|
||||
### Example Response
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
use int_to_bytes::int_to_bytes8;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz::ssz_encode;
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use types::{AttestationData, ChainSpec, Domain, Epoch, Fork};
|
||||
use types::{AttestationData, ChainSpec, Domain, Epoch, Fork, Hash256};
|
||||
|
||||
/// Serialized `AttestationData` augmented with a domain to encode the fork info.
|
||||
#[derive(
|
||||
@ -13,21 +12,34 @@ pub struct AttestationId {
|
||||
}
|
||||
|
||||
/// Number of domain bytes that the end of an attestation ID is padded with.
|
||||
const DOMAIN_BYTES_LEN: usize = 8;
|
||||
const DOMAIN_BYTES_LEN: usize = std::mem::size_of::<Hash256>();
|
||||
|
||||
impl AttestationId {
|
||||
pub fn from_data(attestation: &AttestationData, fork: &Fork, spec: &ChainSpec) -> Self {
|
||||
pub fn from_data(
|
||||
attestation: &AttestationData,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let mut bytes = ssz_encode(attestation);
|
||||
let epoch = attestation.target.epoch;
|
||||
bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, fork, spec));
|
||||
bytes.extend_from_slice(
|
||||
AttestationId::compute_domain_bytes(epoch, fork, genesis_validators_root, spec)
|
||||
.as_bytes(),
|
||||
);
|
||||
AttestationId { v: bytes }
|
||||
}
|
||||
|
||||
pub fn compute_domain_bytes(epoch: Epoch, fork: &Fork, spec: &ChainSpec) -> Vec<u8> {
|
||||
int_to_bytes8(spec.get_domain(epoch, Domain::BeaconAttester, fork))
|
||||
pub fn compute_domain_bytes(
|
||||
epoch: Epoch,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Hash256 {
|
||||
spec.get_domain(epoch, Domain::BeaconAttester, fork, genesis_validators_root)
|
||||
}
|
||||
|
||||
pub fn domain_bytes_match(&self, domain_bytes: &[u8]) -> bool {
|
||||
&self.v[self.v.len() - DOMAIN_BYTES_LEN..] == domain_bytes
|
||||
pub fn domain_bytes_match(&self, domain_bytes: &Hash256) -> bool {
|
||||
&self.v[self.v.len() - DOMAIN_BYTES_LEN..] == domain_bytes.as_bytes()
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use std::collections::{hash_map, HashMap, HashSet};
|
||||
use std::marker::PhantomData;
|
||||
use types::{
|
||||
typenum::Unsigned, Attestation, AttesterSlashing, BeaconState, BeaconStateError, ChainSpec,
|
||||
EthSpec, Fork, ProposerSlashing, RelativeEpoch, SignedVoluntaryExit, Validator,
|
||||
EthSpec, Fork, Hash256, ProposerSlashing, RelativeEpoch, SignedVoluntaryExit, Validator,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
@ -58,9 +58,10 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
&self,
|
||||
attestation: Attestation<T>,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), AttestationValidationError> {
|
||||
let id = AttestationId::from_data(&attestation.data, fork, spec);
|
||||
let id = AttestationId::from_data(&attestation.data, fork, genesis_validators_root, spec);
|
||||
|
||||
// Take a write lock on the attestations map.
|
||||
let mut attestations = self.attestations.write();
|
||||
@ -106,9 +107,18 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
// Attestations for the current fork, which may be from the current or previous epoch.
|
||||
let prev_epoch = state.previous_epoch();
|
||||
let current_epoch = state.current_epoch();
|
||||
let prev_domain_bytes = AttestationId::compute_domain_bytes(prev_epoch, &state.fork, spec);
|
||||
let curr_domain_bytes =
|
||||
AttestationId::compute_domain_bytes(current_epoch, &state.fork, spec);
|
||||
let prev_domain_bytes = AttestationId::compute_domain_bytes(
|
||||
prev_epoch,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
let curr_domain_bytes = AttestationId::compute_domain_bytes(
|
||||
current_epoch,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
let reader = self.attestations.read();
|
||||
let active_indices = state
|
||||
.get_cached_active_validator_indices(RelativeEpoch::Current)
|
||||
@ -168,7 +178,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
verify_proposer_slashing(&slashing, state, VerifySignatures::True, spec)?;
|
||||
self.proposer_slashings
|
||||
.write()
|
||||
.insert(slashing.proposer_index, slashing);
|
||||
.insert(slashing.signed_header_1.message.proposer_index, slashing);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -181,8 +191,18 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
spec: &ChainSpec,
|
||||
) -> (AttestationId, AttestationId) {
|
||||
(
|
||||
AttestationId::from_data(&slashing.attestation_1.data, &state.fork, spec),
|
||||
AttestationId::from_data(&slashing.attestation_2.data, &state.fork, spec),
|
||||
AttestationId::from_data(
|
||||
&slashing.attestation_1.data,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
),
|
||||
AttestationId::from_data(
|
||||
&slashing.attestation_2.data,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -214,7 +234,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
|slashing| {
|
||||
state
|
||||
.validators
|
||||
.get(slashing.proposer_index as usize)
|
||||
.get(slashing.signed_header_1.message.proposer_index as usize)
|
||||
.map_or(false, |validator| !validator.slashed)
|
||||
},
|
||||
T::MaxProposerSlashings::to_usize(),
|
||||
@ -224,7 +244,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
// slashings.
|
||||
let mut to_be_slashed = proposer_slashings
|
||||
.iter()
|
||||
.map(|s| s.proposer_index)
|
||||
.map(|s| s.signed_header_1.message.proposer_index)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let epoch = state.current_epoch();
|
||||
@ -427,6 +447,7 @@ mod release_tests {
|
||||
signers,
|
||||
&committee_keys,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
extra_signer.map(|c_idx| {
|
||||
@ -436,6 +457,7 @@ mod release_tests {
|
||||
&[validator_index],
|
||||
&[&keypairs[validator_index].sk],
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
});
|
||||
@ -548,7 +570,9 @@ mod release_tests {
|
||||
spec,
|
||||
None,
|
||||
);
|
||||
op_pool.insert_attestation(att, &state.fork, spec).unwrap();
|
||||
op_pool
|
||||
.insert_attestation(att, &state.fork, state.genesis_validators_root, spec)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -617,9 +641,16 @@ mod release_tests {
|
||||
None,
|
||||
);
|
||||
op_pool
|
||||
.insert_attestation(att.clone(), &state.fork, spec)
|
||||
.insert_attestation(
|
||||
att.clone(),
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
.unwrap();
|
||||
op_pool
|
||||
.insert_attestation(att, &state.fork, state.genesis_validators_root, spec)
|
||||
.unwrap();
|
||||
op_pool.insert_attestation(att, &state.fork, spec).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(op_pool.num_attestations(), committees.len());
|
||||
@ -656,7 +687,9 @@ mod release_tests {
|
||||
spec,
|
||||
None,
|
||||
);
|
||||
op_pool.insert_attestation(att, &state.fork, spec).unwrap();
|
||||
op_pool
|
||||
.insert_attestation(att, &state.fork, state.genesis_validators_root, spec)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -704,7 +737,9 @@ mod release_tests {
|
||||
spec,
|
||||
if i == 0 { None } else { Some(0) },
|
||||
);
|
||||
op_pool.insert_attestation(att, &state.fork, spec).unwrap();
|
||||
op_pool
|
||||
.insert_attestation(att, &state.fork, state.genesis_validators_root, spec)
|
||||
.unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
@ -777,7 +812,9 @@ mod release_tests {
|
||||
spec,
|
||||
if i == 0 { None } else { Some(0) },
|
||||
);
|
||||
op_pool.insert_attestation(att, &state.fork, spec).unwrap();
|
||||
op_pool
|
||||
.insert_attestation(att, &state.fork, state.genesis_validators_root, spec)
|
||||
.unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -82,7 +82,7 @@ impl<T: EthSpec> PersistedOperationPool<T> {
|
||||
let proposer_slashings = RwLock::new(
|
||||
self.proposer_slashings
|
||||
.into_iter()
|
||||
.map(|slashing| (slashing.proposer_index, slashing))
|
||||
.map(|slashing| (slashing.signed_header_1.message.proposer_index, slashing))
|
||||
.collect(),
|
||||
);
|
||||
let voluntary_exits = RwLock::new(
|
||||
|
@ -3,7 +3,7 @@ use types::*;
|
||||
|
||||
/// Returns validator indices which participated in the attestation, sorted by increasing index.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_attesting_indices<T: EthSpec>(
|
||||
committee: &[usize],
|
||||
bitlist: &BitList<T::MaxValidatorsPerCommittee>,
|
||||
|
@ -3,7 +3,7 @@ use types::*;
|
||||
|
||||
/// Returns the base reward for some validator.
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_base_reward<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
index: usize,
|
||||
|
@ -6,7 +6,7 @@ type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
|
||||
|
||||
/// Convert `attestation` to (almost) indexed-verifiable form.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_indexed_attestation<T: EthSpec>(
|
||||
committee: &[usize],
|
||||
attestation: &Attestation<T>,
|
||||
|
@ -3,7 +3,7 @@ use types::{BeaconStateError as Error, *};
|
||||
|
||||
/// Initiate the exit of the validator of the given `index`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn initiate_validator_exit<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
index: usize,
|
||||
|
@ -4,7 +4,7 @@ use types::{BeaconStateError as Error, *};
|
||||
|
||||
/// Slash the validator with index ``index``.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn slash_validator<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
slashed_index: usize,
|
||||
|
@ -6,7 +6,7 @@ use types::*;
|
||||
|
||||
/// Initialize a `BeaconState` from genesis data.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
// TODO: this is quite inefficient and we probably want to rethink how we do this
|
||||
pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
|
||||
eth1_block_hash: Hash256,
|
||||
@ -42,12 +42,15 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
|
||||
// Now that we have our validators, initialize the caches (including the committees)
|
||||
state.build_all_caches(spec)?;
|
||||
|
||||
// Set genesis validators root for domain separation and chain versioning
|
||||
state.genesis_validators_root = state.update_validators_tree_hash_cache()?;
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
/// Determine whether a candidate genesis state is suitable for starting the chain.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn is_valid_genesis_state<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSpec) -> bool {
|
||||
state.genesis_time >= spec.min_genesis_time
|
||||
&& state.get_active_validator_indices(T::genesis_epoch()).len() as u64
|
||||
@ -56,7 +59,7 @@ pub fn is_valid_genesis_state<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSp
|
||||
|
||||
/// Activate genesis validators, if their balance is acceptable.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_activations<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainSpec) {
|
||||
for (index, validator) in state.validators.iter_mut().enumerate() {
|
||||
let balance = state.balances[index];
|
||||
|
@ -70,7 +70,7 @@ impl VerifySignatures {
|
||||
/// tree hash root of the block, NOT the signing root of the block. This function takes
|
||||
/// care of mixing in the domain.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn per_block_processing<T: EthSpec>(
|
||||
mut state: &mut BeaconState<T>,
|
||||
signed_block: &SignedBeaconBlock<T>,
|
||||
@ -136,14 +136,26 @@ pub fn per_block_processing<T: EthSpec>(
|
||||
|
||||
/// Processes the block header.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_block_header<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
block: &BeaconBlock<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockOperationError<HeaderInvalid>> {
|
||||
// Verify that the slots match
|
||||
verify!(block.slot == state.slot, HeaderInvalid::StateSlotMismatch);
|
||||
|
||||
// Verify that proposer index is the correct index
|
||||
let proposer_index = block.proposer_index as usize;
|
||||
let state_proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
|
||||
verify!(
|
||||
proposer_index == state_proposer_index,
|
||||
HeaderInvalid::ProposerIndexMismatch {
|
||||
block_proposer_index: proposer_index,
|
||||
state_proposer_index,
|
||||
}
|
||||
);
|
||||
|
||||
let expected_previous_block_root = state.latest_block_header.tree_hash_root();
|
||||
verify!(
|
||||
block.parent_root == expected_previous_block_root,
|
||||
@ -156,11 +168,10 @@ pub fn process_block_header<T: EthSpec>(
|
||||
state.latest_block_header = block.temporary_block_header();
|
||||
|
||||
// Verify proposer is not slashed
|
||||
let proposer_idx = state.get_beacon_proposer_index(block.slot, spec)?;
|
||||
let proposer = &state.validators[proposer_idx];
|
||||
let proposer = &state.validators[proposer_index];
|
||||
verify!(
|
||||
!proposer.slashed,
|
||||
HeaderInvalid::ProposerSlashed(proposer_idx)
|
||||
HeaderInvalid::ProposerSlashed(proposer_index)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@ -168,7 +179,7 @@ pub fn process_block_header<T: EthSpec>(
|
||||
|
||||
/// Verifies the signature of a block.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_block_signature<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
block: &SignedBeaconBlock<T>,
|
||||
@ -186,7 +197,7 @@ pub fn verify_block_signature<T: EthSpec>(
|
||||
/// Verifies the `randao_reveal` against the block's proposer pubkey and updates
|
||||
/// `state.latest_randao_mixes`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_randao<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
block: &BeaconBlock<T>,
|
||||
@ -209,7 +220,7 @@ pub fn process_randao<T: EthSpec>(
|
||||
|
||||
/// Update the `state.eth1_data_votes` based upon the `eth1_data` provided.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_eth1_data<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
eth1_data: &Eth1Data,
|
||||
@ -226,7 +237,7 @@ pub fn process_eth1_data<T: EthSpec>(
|
||||
/// Returns `Some(eth1_data)` if adding the given `eth1_data` to `state.eth1_data_votes` would
|
||||
/// result in a change to `state.eth1_data`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_new_eth1_data<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
eth1_data: &Eth1Data,
|
||||
@ -250,7 +261,7 @@ pub fn get_new_eth1_data<T: EthSpec>(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_proposer_slashings<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
proposer_slashings: &[ProposerSlashing],
|
||||
@ -268,7 +279,12 @@ pub fn process_proposer_slashings<T: EthSpec>(
|
||||
|
||||
// Update the state.
|
||||
for proposer_slashing in proposer_slashings {
|
||||
slash_validator(state, proposer_slashing.proposer_index as usize, None, spec)?;
|
||||
slash_validator(
|
||||
state,
|
||||
proposer_slashing.signed_header_1.message.proposer_index as usize,
|
||||
None,
|
||||
spec,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -279,7 +295,7 @@ pub fn process_proposer_slashings<T: EthSpec>(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_attester_slashings<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
attester_slashings: &[AttesterSlashing<T>],
|
||||
@ -333,7 +349,7 @@ pub fn process_attester_slashings<T: EthSpec>(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_attestations<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
attestations: &[Attestation<T>],
|
||||
@ -379,7 +395,7 @@ pub fn process_attestations<T: EthSpec>(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_deposits<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
deposits: &[Deposit],
|
||||
@ -416,7 +432,7 @@ pub fn process_deposits<T: EthSpec>(
|
||||
|
||||
/// Process a single deposit, optionally verifying its merkle proof.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_deposit<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
deposit: &Deposit,
|
||||
@ -482,7 +498,7 @@ pub fn process_deposit<T: EthSpec>(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_exits<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
voluntary_exits: &[SignedVoluntaryExit],
|
||||
|
@ -56,9 +56,18 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
builder.set_proposer_index(proposer_index as u64);
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
Some(sk) => {
|
||||
builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec)
|
||||
}
|
||||
None => builder.set_randao_reveal(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
),
|
||||
}
|
||||
|
||||
self.block_builder.insert_deposits(
|
||||
@ -70,7 +79,12 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
spec,
|
||||
);
|
||||
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
let block = self.block_builder.build(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
@ -96,9 +110,18 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
builder.set_proposer_index(proposer_index as u64);
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
Some(sk) => {
|
||||
builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec)
|
||||
}
|
||||
None => builder.set_randao_reveal(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
),
|
||||
}
|
||||
match test_task {
|
||||
ExitTestTask::AlreadyInitiated => {
|
||||
@ -125,7 +148,12 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
let block = self.block_builder.build(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
@ -151,9 +179,18 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
builder.set_proposer_index(proposer_index as u64);
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
Some(sk) => {
|
||||
builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec)
|
||||
}
|
||||
None => builder.set_randao_reveal(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
),
|
||||
}
|
||||
|
||||
let all_secret_keys: Vec<&SecretKey> = keypairs.iter().map(|keypair| &keypair.sk).collect();
|
||||
@ -166,7 +203,12 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
spec,
|
||||
)
|
||||
.unwrap();
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
let block = self.block_builder.build(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
@ -192,9 +234,18 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
builder.set_proposer_index(proposer_index as u64);
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
Some(sk) => {
|
||||
builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec)
|
||||
}
|
||||
None => builder.set_randao_reveal(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
),
|
||||
}
|
||||
|
||||
let mut validator_indices = vec![];
|
||||
@ -210,10 +261,16 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
&validator_indices,
|
||||
&secret_keys,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
let block = self.block_builder.build(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
@ -239,9 +296,18 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
builder.set_proposer_index(proposer_index as u64);
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
Some(sk) => {
|
||||
builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec)
|
||||
}
|
||||
None => builder.set_randao_reveal(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
),
|
||||
}
|
||||
|
||||
for i in 0..num_proposer_slashings {
|
||||
@ -252,10 +318,16 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
validator_indices,
|
||||
&secret_keys,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
let block = self.block_builder.build(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
@ -279,12 +351,26 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
builder.set_proposer_index(proposer_index as u64);
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
Some(sk) => {
|
||||
builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec)
|
||||
}
|
||||
None => builder.set_randao_reveal(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
),
|
||||
}
|
||||
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
let block = self.block_builder.build(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
|
@ -159,7 +159,14 @@ impl<T> From<ssz_types::Error> for BlockOperationError<T> {
|
||||
pub enum HeaderInvalid {
|
||||
ProposalSignatureInvalid,
|
||||
StateSlotMismatch,
|
||||
ParentBlockRootMismatch { state: Hash256, block: Hash256 },
|
||||
ProposerIndexMismatch {
|
||||
block_proposer_index: usize,
|
||||
state_proposer_index: usize,
|
||||
},
|
||||
ParentBlockRootMismatch {
|
||||
state: Hash256,
|
||||
block: Hash256,
|
||||
},
|
||||
ProposerSlashed(usize),
|
||||
}
|
||||
|
||||
@ -171,6 +178,10 @@ pub enum ProposerSlashingInvalid {
|
||||
///
|
||||
/// (proposal_1_slot, proposal_2_slot)
|
||||
ProposalSlotMismatch(Slot, Slot),
|
||||
/// The two proposals have different proposer indices.
|
||||
///
|
||||
/// (proposer_index_1, proposer_index_2)
|
||||
ProposerIndexMismatch(u64, u64),
|
||||
/// The proposals are identical and therefore not slashable.
|
||||
ProposalsIdentical,
|
||||
/// The specified proposer cannot be slashed because they are already slashed, or not active.
|
||||
|
@ -11,7 +11,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
|
||||
|
||||
/// Verify an `IndexedAttestation`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn is_valid_indexed_attestation<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
indexed_attestation: &IndexedAttestation<T>,
|
||||
|
@ -53,6 +53,7 @@ pub fn block_proposal_signature_set<'a, T: EthSpec>(
|
||||
block.slot.epoch(T::slots_per_epoch()),
|
||||
Domain::BeaconProposer,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = if let Some(root) = block_root {
|
||||
@ -84,6 +85,7 @@ pub fn randao_signature_set<'a, T: EthSpec>(
|
||||
block.slot.epoch(T::slots_per_epoch()),
|
||||
Domain::Randao,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = state.current_epoch().signing_root(domain);
|
||||
@ -101,7 +103,7 @@ pub fn proposer_slashing_signature_set<'a, T: EthSpec>(
|
||||
proposer_slashing: &'a ProposerSlashing,
|
||||
spec: &'a ChainSpec,
|
||||
) -> Result<(SignatureSet<'a>, SignatureSet<'a>)> {
|
||||
let proposer_index = proposer_slashing.proposer_index as usize;
|
||||
let proposer_index = proposer_slashing.signed_header_1.message.proposer_index as usize;
|
||||
|
||||
Ok((
|
||||
block_header_signature_set(
|
||||
@ -130,6 +132,7 @@ fn block_header_signature_set<'a, T: EthSpec>(
|
||||
signed_header.message.slot.epoch(T::slots_per_epoch()),
|
||||
Domain::BeaconProposer,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = signed_header
|
||||
@ -162,6 +165,7 @@ pub fn indexed_attestation_signature_set<'a, 'b, T: EthSpec>(
|
||||
indexed_attestation.data.target.epoch,
|
||||
Domain::BeaconAttester,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = indexed_attestation.data.signing_root(domain);
|
||||
@ -177,6 +181,7 @@ pub fn indexed_attestation_signature_set_from_pubkeys<'a, 'b, T: EthSpec>(
|
||||
signature: &'a AggregateSignature,
|
||||
indexed_attestation: &'b IndexedAttestation<T>,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &'a ChainSpec,
|
||||
) -> Result<SignatureSet<'a>> {
|
||||
let pubkeys = pubkeys
|
||||
@ -188,6 +193,7 @@ pub fn indexed_attestation_signature_set_from_pubkeys<'a, 'b, T: EthSpec>(
|
||||
indexed_attestation.data.target.epoch,
|
||||
Domain::BeaconAttester,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = indexed_attestation.data.signing_root(domain);
|
||||
@ -258,7 +264,12 @@ pub fn exit_signature_set<'a, T: EthSpec>(
|
||||
let exit = &signed_exit.message;
|
||||
let proposer_index = exit.validator_index as usize;
|
||||
|
||||
let domain = spec.get_domain(exit.epoch, Domain::VoluntaryExit, &state.fork);
|
||||
let domain = spec.get_domain(
|
||||
exit.epoch,
|
||||
Domain::VoluntaryExit,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = exit.signing_root(domain).as_bytes().to_vec();
|
||||
|
||||
|
@ -10,7 +10,7 @@ use types::test_utils::{
|
||||
use types::*;
|
||||
|
||||
pub const NUM_DEPOSITS: u64 = 1;
|
||||
pub const VALIDATOR_COUNT: usize = 10;
|
||||
pub const VALIDATOR_COUNT: usize = 64;
|
||||
pub const SLOT_OFFSET: u64 = 4;
|
||||
pub const EXIT_SLOT_OFFSET: u64 = 2048;
|
||||
pub const NUM_ATTESTATIONS: u64 = 1;
|
||||
@ -93,7 +93,12 @@ fn invalid_block_signature() {
|
||||
|
||||
// sign the block with a keypair that is not the expected proposer
|
||||
let keypair = Keypair::random();
|
||||
let block = block.message.sign(&keypair.sk, &state.fork, &spec);
|
||||
let block = block.message.sign(
|
||||
&keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
&spec,
|
||||
);
|
||||
|
||||
// process block with invalid block signature
|
||||
let result = per_block_processing(
|
||||
@ -630,7 +635,7 @@ fn invalid_attestation_wrong_justified_checkpoint() {
|
||||
#[test]
|
||||
fn invalid_attestation_bad_indexed_attestation_bad_signature() {
|
||||
let spec = MainnetEthSpec::default_spec();
|
||||
let builder = get_builder(&spec, SLOT_OFFSET, 33); // minmium number of validators required for this test
|
||||
let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT);
|
||||
let test_task = AttestationTestTask::BadIndexedAttestationBadSignature;
|
||||
let (block, mut state) =
|
||||
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
|
||||
|
@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
|
||||
///
|
||||
/// Optionally verifies the aggregate signature, depending on `verify_signatures`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
@ -49,7 +49,7 @@ pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
|
||||
/// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the
|
||||
/// prior blocks in `state`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_attestation_for_state<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
@ -58,6 +58,13 @@ pub fn verify_attestation_for_state<T: EthSpec>(
|
||||
) -> Result<()> {
|
||||
let data = &attestation.data;
|
||||
|
||||
// This emptiness check is required *in addition* to the length check in `get_attesting_indices`
|
||||
// because we can parse a bitfield and know its length, even if it has no bits set.
|
||||
verify!(
|
||||
!attestation.aggregation_bits.is_zero(),
|
||||
Invalid::AggregationBitfieldIsEmpty
|
||||
);
|
||||
|
||||
verify!(
|
||||
data.index < state.get_committee_count_at_slot(data.slot)?,
|
||||
Invalid::BadCommitteeIndex
|
||||
@ -76,7 +83,7 @@ pub fn verify_attestation_for_state<T: EthSpec>(
|
||||
|
||||
/// Check target epoch and source checkpoint.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn verify_casper_ffg_vote<T: EthSpec>(
|
||||
attestation: &Attestation<T>,
|
||||
state: &BeaconState<T>,
|
||||
|
@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
|
||||
///
|
||||
/// Returns `Ok(())` if the `AttesterSlashing` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_attester_slashing<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attester_slashing: &AttesterSlashing<T>,
|
||||
@ -47,7 +47,7 @@ pub fn verify_attester_slashing<T: EthSpec>(
|
||||
///
|
||||
/// Returns Ok(indices) if `indices.len() > 0`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_slashable_indices<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attester_slashing: &AttesterSlashing<T>,
|
||||
|
@ -14,7 +14,7 @@ fn error(reason: DepositInvalid) -> BlockOperationError<DepositInvalid> {
|
||||
|
||||
/// Verify `Deposit.pubkey` signed `Deposit.signature`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_deposit_signature(deposit_data: &DepositData, spec: &ChainSpec) -> Result<()> {
|
||||
let deposit_signature_message = deposit_pubkey_signature_message(&deposit_data, spec)
|
||||
.ok_or_else(|| error(DepositInvalid::BadBlsBytes))?;
|
||||
@ -46,7 +46,7 @@ pub fn get_existing_validator_index<T: EthSpec>(
|
||||
/// The deposit index is provided as a parameter so we can check proofs
|
||||
/// before they're due to be processed, and in parallel.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_deposit_merkle_proof<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
deposit: &Deposit,
|
||||
|
@ -13,7 +13,7 @@ fn error(reason: ExitInvalid) -> BlockOperationError<ExitInvalid> {
|
||||
///
|
||||
/// Returns `Ok(())` if the `Exit` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_exit<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
exit: &SignedVoluntaryExit,
|
||||
@ -25,7 +25,7 @@ pub fn verify_exit<T: EthSpec>(
|
||||
|
||||
/// Like `verify_exit` but doesn't run checks which may become true in future states.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_exit_time_independent_only<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
exit: &SignedVoluntaryExit,
|
||||
@ -37,7 +37,7 @@ pub fn verify_exit_time_independent_only<T: EthSpec>(
|
||||
|
||||
/// Parametric version of `verify_exit` that skips some checks if `time_independent_only` is true.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn verify_exit_parametric<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
signed_exit: &SignedVoluntaryExit,
|
||||
|
@ -14,38 +14,40 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
|
||||
///
|
||||
/// Returns `Ok(())` if the `ProposerSlashing` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn verify_proposer_slashing<T: EthSpec>(
|
||||
proposer_slashing: &ProposerSlashing,
|
||||
state: &BeaconState<T>,
|
||||
verify_signatures: VerifySignatures,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<()> {
|
||||
let proposer = state
|
||||
.validators
|
||||
.get(proposer_slashing.proposer_index as usize)
|
||||
.ok_or_else(|| error(Invalid::ProposerUnknown(proposer_slashing.proposer_index)))?;
|
||||
let header_1 = &proposer_slashing.signed_header_1.message;
|
||||
let header_2 = &proposer_slashing.signed_header_2.message;
|
||||
|
||||
// Verify slots match
|
||||
verify!(
|
||||
proposer_slashing.signed_header_1.message.slot
|
||||
== proposer_slashing.signed_header_2.message.slot,
|
||||
Invalid::ProposalSlotMismatch(
|
||||
proposer_slashing.signed_header_1.message.slot,
|
||||
proposer_slashing.signed_header_2.message.slot
|
||||
)
|
||||
header_1.slot == header_2.slot,
|
||||
Invalid::ProposalSlotMismatch(header_1.slot, header_2.slot)
|
||||
);
|
||||
|
||||
// Verify header proposer indices match
|
||||
verify!(
|
||||
header_1.proposer_index == header_2.proposer_index,
|
||||
Invalid::ProposerIndexMismatch(header_1.proposer_index, header_2.proposer_index)
|
||||
);
|
||||
|
||||
// But the headers are different
|
||||
verify!(
|
||||
proposer_slashing.signed_header_1 != proposer_slashing.signed_header_2,
|
||||
Invalid::ProposalsIdentical
|
||||
);
|
||||
verify!(header_1 != header_2, Invalid::ProposalsIdentical);
|
||||
|
||||
// Check proposer is slashable
|
||||
let proposer = state
|
||||
.validators
|
||||
.get(header_1.proposer_index as usize)
|
||||
.ok_or_else(|| error(Invalid::ProposerUnknown(header_1.proposer_index)))?;
|
||||
|
||||
verify!(
|
||||
proposer.is_slashable_at(state.current_epoch()),
|
||||
Invalid::ProposerNotSlashable(proposer_slashing.proposer_index)
|
||||
Invalid::ProposerNotSlashable(header_1.proposer_index)
|
||||
);
|
||||
|
||||
if verify_signatures.is_true() {
|
||||
|
@ -19,7 +19,7 @@ pub use validator_statuses::{TotalBalances, ValidatorStatus, ValidatorStatuses};
|
||||
/// Mutates the given `BeaconState`, returning early if an error is encountered. If an error is
|
||||
/// returned, a state might be "half-processed" and therefore in an invalid state.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn per_epoch_processing<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
@ -45,7 +45,11 @@ pub fn per_epoch_processing<T: EthSpec>(
|
||||
process_registry_updates(state, spec)?;
|
||||
|
||||
// Slashings.
|
||||
process_slashings(state, validator_statuses.total_balances.current_epoch, spec)?;
|
||||
process_slashings(
|
||||
state,
|
||||
validator_statuses.total_balances.current_epoch(),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
// Final updates.
|
||||
process_final_updates(state, spec)?;
|
||||
@ -66,7 +70,7 @@ pub fn per_epoch_processing<T: EthSpec>(
|
||||
/// - `finalized_epoch`
|
||||
/// - `finalized_root`
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[allow(clippy::if_same_then_else)] // For readability and consistency with spec.
|
||||
pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
@ -86,7 +90,7 @@ pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
state.previous_justified_checkpoint = state.current_justified_checkpoint.clone();
|
||||
state.justification_bits.shift_up(1)?;
|
||||
|
||||
if total_balances.previous_epoch_target_attesters * 3 >= total_balances.current_epoch * 2 {
|
||||
if total_balances.previous_epoch_target_attesters() * 3 >= total_balances.current_epoch() * 2 {
|
||||
state.current_justified_checkpoint = Checkpoint {
|
||||
epoch: previous_epoch,
|
||||
root: *state.get_block_root_at_epoch(previous_epoch)?,
|
||||
@ -94,7 +98,7 @@ pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
state.justification_bits.set(1, true)?;
|
||||
}
|
||||
// If the current epoch gets justified, fill the last bit.
|
||||
if total_balances.current_epoch_target_attesters * 3 >= total_balances.current_epoch * 2 {
|
||||
if total_balances.current_epoch_target_attesters() * 3 >= total_balances.current_epoch() * 2 {
|
||||
state.current_justified_checkpoint = Checkpoint {
|
||||
epoch: current_epoch,
|
||||
root: *state.get_block_root_at_epoch(current_epoch)?,
|
||||
@ -134,7 +138,7 @@ pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
|
||||
/// Finish up an epoch update.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_final_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
@ -148,11 +152,14 @@ pub fn process_final_updates<T: EthSpec>(
|
||||
}
|
||||
|
||||
// Update effective balances with hysteresis (lag).
|
||||
let hysteresis_increment = spec.effective_balance_increment / spec.hysteresis_quotient;
|
||||
let downward_threshold = hysteresis_increment * spec.hysteresis_downward_multiplier;
|
||||
let upward_threshold = hysteresis_increment * spec.hysteresis_upward_multiplier;
|
||||
for (index, validator) in state.validators.iter_mut().enumerate() {
|
||||
let balance = state.balances[index];
|
||||
let half_increment = spec.effective_balance_increment / 2;
|
||||
if balance < validator.effective_balance
|
||||
|| validator.effective_balance + 3 * half_increment < balance
|
||||
|
||||
if balance + downward_threshold < validator.effective_balance
|
||||
|| validator.effective_balance + upward_threshold < balance
|
||||
{
|
||||
validator.effective_balance = std::cmp::min(
|
||||
balance - balance % spec.effective_balance_increment,
|
||||
|
@ -33,7 +33,7 @@ impl std::ops::AddAssign for Delta {
|
||||
|
||||
/// Apply attester and proposer rewards.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_rewards_and_penalties<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
validator_statuses: &mut ValidatorStatuses,
|
||||
@ -67,7 +67,7 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
|
||||
|
||||
/// For each attesting validator, reward the proposer who was first to include their attestation.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn get_proposer_deltas<T: EthSpec>(
|
||||
deltas: &mut Vec<Delta>,
|
||||
state: &BeaconState<T>,
|
||||
@ -83,7 +83,7 @@ fn get_proposer_deltas<T: EthSpec>(
|
||||
let base_reward = get_base_reward(
|
||||
state,
|
||||
index,
|
||||
validator_statuses.total_balances.current_epoch,
|
||||
validator_statuses.total_balances.current_epoch(),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
@ -100,7 +100,7 @@ fn get_proposer_deltas<T: EthSpec>(
|
||||
|
||||
/// Apply rewards for participation in attestations during the previous epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn get_attestation_deltas<T: EthSpec>(
|
||||
deltas: &mut Vec<Delta>,
|
||||
state: &BeaconState<T>,
|
||||
@ -113,7 +113,7 @@ fn get_attestation_deltas<T: EthSpec>(
|
||||
let base_reward = get_base_reward(
|
||||
state,
|
||||
index,
|
||||
validator_statuses.total_balances.current_epoch,
|
||||
validator_statuses.total_balances.current_epoch(),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
@ -133,7 +133,7 @@ fn get_attestation_deltas<T: EthSpec>(
|
||||
|
||||
/// Determine the delta for a single validator, sans proposer rewards.
|
||||
///
|
||||
/// Spec v0.11.0
|
||||
/// Spec v0.11.1
|
||||
fn get_attestation_delta<T: EthSpec>(
|
||||
validator: &ValidatorStatus,
|
||||
total_balances: &TotalBalances,
|
||||
@ -157,13 +157,13 @@ fn get_attestation_delta<T: EthSpec>(
|
||||
// - increment = EFFECTIVE_BALANCE_INCREMENT
|
||||
// - reward_numerator = get_base_reward(state, index) * (attesting_balance // increment)
|
||||
// - rewards[index] = reward_numerator // (total_balance // increment)
|
||||
let total_balance_ebi = total_balances.current_epoch / spec.effective_balance_increment;
|
||||
let total_balance_ebi = total_balances.current_epoch() / spec.effective_balance_increment;
|
||||
let total_attesting_balance_ebi =
|
||||
total_balances.previous_epoch_attesters / spec.effective_balance_increment;
|
||||
total_balances.previous_epoch_attesters() / spec.effective_balance_increment;
|
||||
let matching_target_balance_ebi =
|
||||
total_balances.previous_epoch_target_attesters / spec.effective_balance_increment;
|
||||
total_balances.previous_epoch_target_attesters() / spec.effective_balance_increment;
|
||||
let matching_head_balance_ebi =
|
||||
total_balances.previous_epoch_head_attesters / spec.effective_balance_increment;
|
||||
total_balances.previous_epoch_head_attesters() / spec.effective_balance_increment;
|
||||
|
||||
// Expected FFG source.
|
||||
// Spec:
|
||||
|
@ -2,7 +2,7 @@ use types::{BeaconStateError as Error, *};
|
||||
|
||||
/// Process slashings.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_slashings<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
total_balance: u64,
|
||||
|
@ -5,7 +5,7 @@ use types::*;
|
||||
|
||||
/// Performs a validator registry update, if required.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_registry_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
|
@ -12,7 +12,7 @@ macro_rules! set_self_if_other_is_true {
|
||||
}
|
||||
|
||||
/// The information required to reward a block producer for including an attestation in a block.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct InclusionInfo {
|
||||
/// The distance between the attestation slot and the slot that attestation was included in a
|
||||
/// block.
|
||||
@ -43,7 +43,7 @@ impl InclusionInfo {
|
||||
}
|
||||
|
||||
/// Information required to reward some validator during the current and previous epoch.
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ValidatorStatus {
|
||||
/// True if the validator has been slashed, ever.
|
||||
pub is_slashed: bool,
|
||||
@ -107,30 +107,64 @@ impl ValidatorStatus {
|
||||
|
||||
/// The total effective balances for different sets of validators during the previous and current
|
||||
/// epochs.
|
||||
#[derive(Default, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TotalBalances {
|
||||
/// The effective balance increment from the spec.
|
||||
effective_balance_increment: u64,
|
||||
/// The total effective balance of all active validators during the _current_ epoch.
|
||||
pub current_epoch: u64,
|
||||
current_epoch: u64,
|
||||
/// The total effective balance of all active validators during the _previous_ epoch.
|
||||
pub previous_epoch: u64,
|
||||
previous_epoch: u64,
|
||||
/// The total effective balance of all validators who attested during the _current_ epoch.
|
||||
pub current_epoch_attesters: u64,
|
||||
current_epoch_attesters: u64,
|
||||
/// The total effective balance of all validators who attested during the _current_ epoch and
|
||||
/// agreed with the state about the beacon block at the first slot of the _current_ epoch.
|
||||
pub current_epoch_target_attesters: u64,
|
||||
current_epoch_target_attesters: u64,
|
||||
/// The total effective balance of all validators who attested during the _previous_ epoch.
|
||||
pub previous_epoch_attesters: u64,
|
||||
previous_epoch_attesters: u64,
|
||||
/// The total effective balance of all validators who attested during the _previous_ epoch and
|
||||
/// agreed with the state about the beacon block at the first slot of the _previous_ epoch.
|
||||
pub previous_epoch_target_attesters: u64,
|
||||
previous_epoch_target_attesters: u64,
|
||||
/// The total effective balance of all validators who attested during the _previous_ epoch and
|
||||
/// agreed with the state about the beacon block at the time of attestation.
|
||||
pub previous_epoch_head_attesters: u64,
|
||||
previous_epoch_head_attesters: u64,
|
||||
}
|
||||
|
||||
// Generate a safe accessor for a balance in `TotalBalances`, as per spec `get_total_balance`.
|
||||
macro_rules! balance_accessor {
|
||||
($field_name:ident) => {
|
||||
pub fn $field_name(&self) -> u64 {
|
||||
std::cmp::max(self.effective_balance_increment, self.$field_name)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl TotalBalances {
|
||||
pub fn new(spec: &ChainSpec) -> Self {
|
||||
Self {
|
||||
effective_balance_increment: spec.effective_balance_increment,
|
||||
current_epoch: 0,
|
||||
previous_epoch: 0,
|
||||
current_epoch_attesters: 0,
|
||||
current_epoch_target_attesters: 0,
|
||||
previous_epoch_attesters: 0,
|
||||
previous_epoch_target_attesters: 0,
|
||||
previous_epoch_head_attesters: 0,
|
||||
}
|
||||
}
|
||||
|
||||
balance_accessor!(current_epoch);
|
||||
balance_accessor!(previous_epoch);
|
||||
balance_accessor!(current_epoch_attesters);
|
||||
balance_accessor!(current_epoch_target_attesters);
|
||||
balance_accessor!(previous_epoch_attesters);
|
||||
balance_accessor!(previous_epoch_target_attesters);
|
||||
balance_accessor!(previous_epoch_head_attesters);
|
||||
}
|
||||
|
||||
/// Summarised information about validator participation in the _previous and _current_ epochs of
|
||||
/// some `BeaconState`.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValidatorStatuses {
|
||||
/// Information about each individual validator from the state's validator registry.
|
||||
pub statuses: Vec<ValidatorStatus>,
|
||||
@ -144,13 +178,13 @@ impl ValidatorStatuses {
|
||||
/// - Active validators
|
||||
/// - Total balances for the current and previous epochs.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn new<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Self, BeaconStateError> {
|
||||
let mut statuses = Vec::with_capacity(state.validators.len());
|
||||
let mut total_balances = TotalBalances::default();
|
||||
let mut total_balances = TotalBalances::new(spec);
|
||||
|
||||
for (i, validator) in state.validators.iter().enumerate() {
|
||||
let effective_balance = state.get_effective_balance(i, spec)?;
|
||||
@ -184,7 +218,7 @@ impl ValidatorStatuses {
|
||||
/// Process some attestations from the given `state` updating the `statuses` and
|
||||
/// `total_balances` fields.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn process_attestations<T: EthSpec>(
|
||||
&mut self,
|
||||
state: &BeaconState<T>,
|
||||
@ -221,10 +255,10 @@ impl ValidatorStatuses {
|
||||
|
||||
if target_matches_epoch_start_block(a, state, state.previous_epoch())? {
|
||||
status.is_previous_epoch_target_attester = true;
|
||||
}
|
||||
|
||||
if has_common_beacon_block_root(a, state)? {
|
||||
status.is_previous_epoch_head_attester = true;
|
||||
if has_common_beacon_block_root(a, state)? {
|
||||
status.is_previous_epoch_head_attester = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,7 +299,7 @@ impl ValidatorStatuses {
|
||||
/// Returns `true` if the attestation's FFG target is equal to the hash of the `state`'s first
|
||||
/// beacon block in the given `epoch`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn target_matches_epoch_start_block<T: EthSpec>(
|
||||
a: &PendingAttestation<T>,
|
||||
state: &BeaconState<T>,
|
||||
@ -280,7 +314,7 @@ fn target_matches_epoch_start_block<T: EthSpec>(
|
||||
/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for
|
||||
/// the current slot of the `PendingAttestation`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn has_common_beacon_block_root<T: EthSpec>(
|
||||
a: &PendingAttestation<T>,
|
||||
state: &BeaconState<T>,
|
||||
|
@ -13,7 +13,7 @@ pub enum Error {
|
||||
/// `state_root` is `None`, the root of `state` will be computed using a cached tree hash.
|
||||
/// Providing the `state_root` makes this function several orders of magniude faster.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn per_slot_processing<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
state_root: Option<Hash256>,
|
||||
|
@ -62,7 +62,14 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
|
||||
let proposer_keypair = &keypairs[proposer_index];
|
||||
|
||||
builder.set_randao_reveal(&proposer_keypair.sk, &state.fork, spec);
|
||||
builder.set_proposer_index(proposer_index as u64);
|
||||
|
||||
builder.set_randao_reveal(
|
||||
&proposer_keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
let parent_root = state.latest_block_header.canonical_root();
|
||||
builder.set_parent_root(parent_root);
|
||||
@ -79,6 +86,7 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
validator_index,
|
||||
&keypairs[validator_index as usize].sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
@ -106,6 +114,7 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
&attesters,
|
||||
&secret_keys,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
@ -161,9 +170,12 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
// Set the eth1 data to be different from the state.
|
||||
self.block_builder.block.body.eth1_data.block_hash = Hash256::from_slice(&[42; 32]);
|
||||
|
||||
let block = self
|
||||
.block_builder
|
||||
.build(&proposer_keypair.sk, &state.fork, spec);
|
||||
let block = self.block_builder.build(
|
||||
&proposer_keypair.sk,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use super::{
|
||||
AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey,
|
||||
Signature, SignedRoot,
|
||||
};
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{test_utils::TestRandom, Hash256};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@ -17,7 +17,7 @@ pub enum Error {
|
||||
|
||||
/// Details an attestation that can be slashable.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct Attestation<T: EthSpec> {
|
||||
@ -53,6 +53,7 @@ impl<T: EthSpec> Attestation<T> {
|
||||
secret_key: &SecretKey,
|
||||
committee_position: usize,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
if self
|
||||
@ -66,7 +67,12 @@ impl<T: EthSpec> Attestation<T> {
|
||||
.set(committee_position, true)
|
||||
.map_err(Error::SszTypesError)?;
|
||||
|
||||
let domain = spec.get_domain(self.data.target.epoch, Domain::BeaconAttester, fork);
|
||||
let domain = spec.get_domain(
|
||||
self.data.target.epoch,
|
||||
Domain::BeaconAttester,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = self.data.signing_root(domain);
|
||||
|
||||
self.signature
|
||||
|
@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// The data upon which an attestation is based.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom,
|
||||
)]
|
||||
|
@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Two conflicting attestations.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct AttesterSlashing<T: EthSpec> {
|
||||
|
@ -10,11 +10,12 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// A block of the `BeaconChain`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BeaconBlock<T: EthSpec> {
|
||||
pub slot: Slot,
|
||||
pub proposer_index: u64,
|
||||
pub parent_root: Hash256,
|
||||
pub state_root: Hash256,
|
||||
pub body: BeaconBlockBody<T>,
|
||||
@ -25,10 +26,11 @@ impl<T: EthSpec> SignedRoot for BeaconBlock<T> {}
|
||||
impl<T: EthSpec> BeaconBlock<T> {
|
||||
/// Returns an empty block to be used during genesis.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn empty(spec: &ChainSpec) -> Self {
|
||||
BeaconBlock {
|
||||
slot: spec.genesis_slot,
|
||||
proposer_index: 0,
|
||||
parent_root: Hash256::zero(),
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBody {
|
||||
@ -55,7 +57,7 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
|
||||
/// Returns the `tree_hash_root` of the block.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.tree_hash_root()[..])
|
||||
}
|
||||
@ -67,10 +69,11 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
///
|
||||
/// Note: performs a full tree-hash of `self.body`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn block_header(&self) -> BeaconBlockHeader {
|
||||
BeaconBlockHeader {
|
||||
slot: self.slot,
|
||||
proposer_index: self.proposer_index,
|
||||
parent_root: self.parent_root,
|
||||
state_root: self.state_root,
|
||||
body_root: Hash256::from_slice(&self.body.tree_hash_root()[..]),
|
||||
@ -79,7 +82,7 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
|
||||
/// Returns a "temporary" header, where the `state_root` is `Hash256::zero()`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn temporary_block_header(&self) -> BeaconBlockHeader {
|
||||
BeaconBlockHeader {
|
||||
state_root: Hash256::zero(),
|
||||
@ -92,9 +95,15 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
self,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> SignedBeaconBlock<T> {
|
||||
let domain = spec.get_domain(self.epoch(), Domain::BeaconProposer, fork);
|
||||
let domain = spec.get_domain(
|
||||
self.epoch(),
|
||||
Domain::BeaconProposer,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = self.signing_root(domain);
|
||||
let signature = Signature::new(message.as_bytes(), secret_key);
|
||||
SignedBeaconBlock {
|
||||
|
@ -10,7 +10,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// The body of a `BeaconChain` block, containing operations.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BeaconBlockBody<T: EthSpec> {
|
||||
|
@ -9,10 +9,11 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// A header of a `BeaconBlock`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct BeaconBlockHeader {
|
||||
pub slot: Slot,
|
||||
pub proposer_index: u64,
|
||||
pub parent_root: Hash256,
|
||||
pub state_root: Hash256,
|
||||
pub body_root: Hash256,
|
||||
@ -23,17 +24,18 @@ impl SignedRoot for BeaconBlockHeader {}
|
||||
impl BeaconBlockHeader {
|
||||
/// Returns the `tree_hash_root` of the header.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.tree_hash_root()[..])
|
||||
}
|
||||
|
||||
/// Given a `body`, consumes `self` and returns a complete `BeaconBlock`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn into_block<T: EthSpec>(self, body: BeaconBlockBody<T>) -> BeaconBlock<T> {
|
||||
BeaconBlock {
|
||||
slot: self.slot,
|
||||
proposer_index: self.proposer_index,
|
||||
parent_root: self.parent_root,
|
||||
state_root: self.state_root,
|
||||
body,
|
||||
@ -45,10 +47,11 @@ impl BeaconBlockHeader {
|
||||
self,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> SignedBeaconBlockHeader {
|
||||
let epoch = self.slot.epoch(E::slots_per_epoch());
|
||||
let domain = spec.get_domain(epoch, Domain::BeaconProposer, fork);
|
||||
let domain = spec.get_domain(epoch, Domain::BeaconProposer, fork, genesis_validators_root);
|
||||
let message = self.signing_root(domain);
|
||||
let signature = Signature::new(message.as_bytes(), secret_key);
|
||||
SignedBeaconBlockHeader {
|
||||
|
@ -91,7 +91,7 @@ impl AllowNextEpoch {
|
||||
|
||||
/// The state of the `BeaconChain` at some slot.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -111,6 +111,7 @@ where
|
||||
{
|
||||
// Versioning
|
||||
pub genesis_time: u64,
|
||||
pub genesis_validators_root: Hash256,
|
||||
pub slot: Slot,
|
||||
pub fork: Fork,
|
||||
|
||||
@ -182,11 +183,12 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Not a complete genesis state, see `initialize_beacon_state_from_eth1`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn new(genesis_time: u64, eth1_data: Eth1Data, spec: &ChainSpec) -> Self {
|
||||
BeaconState {
|
||||
// Versioning
|
||||
genesis_time,
|
||||
genesis_validators_root: Hash256::zero(), // Set later.
|
||||
slot: spec.genesis_slot,
|
||||
fork: Fork {
|
||||
previous_version: spec.genesis_fork_version,
|
||||
@ -239,7 +241,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Returns the `tree_hash_root` of the state.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.tree_hash_root()[..])
|
||||
}
|
||||
@ -268,7 +270,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// The epoch corresponding to `self.slot`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn current_epoch(&self) -> Epoch {
|
||||
self.slot.epoch(T::slots_per_epoch())
|
||||
}
|
||||
@ -277,7 +279,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// If the current epoch is the genesis epoch, the genesis_epoch is returned.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn previous_epoch(&self) -> Epoch {
|
||||
let current_epoch = self.current_epoch();
|
||||
if current_epoch > T::genesis_epoch() {
|
||||
@ -289,7 +291,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// The epoch following `self.current_epoch()`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn next_epoch(&self) -> Epoch {
|
||||
self.current_epoch() + 1
|
||||
}
|
||||
@ -298,7 +300,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Makes use of the committee cache and will fail if no cache exists for the slot's epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_committee_count_at_slot(&self, slot: Slot) -> Result<u64, Error> {
|
||||
let cache = self.committee_cache_at_slot(slot)?;
|
||||
Ok(cache.committees_per_slot() as u64)
|
||||
@ -306,7 +308,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Compute the number of committees in an entire epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result<u64, Error> {
|
||||
let cache = self.committee_cache(relative_epoch)?;
|
||||
Ok(cache.epoch_committee_count() as u64)
|
||||
@ -330,7 +332,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Does not utilize the cache, performs a full iteration over the validator registry.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_active_validator_indices(&self, epoch: Epoch) -> Vec<usize> {
|
||||
get_active_validator_indices(&self.validators, epoch)
|
||||
}
|
||||
@ -350,7 +352,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Utilises the committee cache.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_beacon_committee(
|
||||
&self,
|
||||
slot: Slot,
|
||||
@ -369,7 +371,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Utilises the committee cache.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_beacon_committees_at_slot(&self, slot: Slot) -> Result<Vec<BeaconCommittee>, Error> {
|
||||
let cache = self.committee_cache_at_slot(slot)?;
|
||||
cache.get_beacon_committees_at_slot(slot)
|
||||
@ -379,7 +381,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Utilises the committee cache.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_beacon_committees_at_epoch(
|
||||
&self,
|
||||
relative_epoch: RelativeEpoch,
|
||||
@ -390,7 +392,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Compute the proposer (not necessarily for the Beacon chain) from a list of indices.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
// NOTE: be sure to test this bad boy.
|
||||
pub fn compute_proposer_index(
|
||||
&self,
|
||||
@ -429,7 +431,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Returns the beacon proposer index for the `slot` in the given `relative_epoch`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_beacon_proposer_index(&self, slot: Slot, spec: &ChainSpec) -> Result<usize, Error> {
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
let seed = self.get_beacon_proposer_seed(slot, spec)?;
|
||||
@ -440,7 +442,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Compute the seed to use for the beacon proposer selection at the given `slot`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn get_beacon_proposer_seed(&self, slot: Slot, spec: &ChainSpec) -> Result<Vec<u8>, Error> {
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
let mut preimage = self
|
||||
@ -455,7 +457,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// It needs filling in on all slots where there isn't a skip.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_latest_block_root(&self, current_state_root: Hash256) -> Hash256 {
|
||||
if self.latest_block_header.state_root.is_zero() {
|
||||
let mut latest_block_header = self.latest_block_header.clone();
|
||||
@ -468,7 +470,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Safely obtains the index for latest block roots, given some `slot`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn get_latest_block_roots_index(&self, slot: Slot) -> Result<usize, Error> {
|
||||
if (slot < self.slot) && (self.slot <= slot + self.block_roots.len() as u64) {
|
||||
Ok(slot.as_usize() % self.block_roots.len())
|
||||
@ -479,7 +481,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the block root at a recent `slot`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> {
|
||||
let i = self.get_latest_block_roots_index(slot)?;
|
||||
Ok(&self.block_roots[i])
|
||||
@ -487,7 +489,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the block root at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
// NOTE: the spec calls this get_block_root
|
||||
pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> {
|
||||
self.get_block_root(epoch.start_slot(T::slots_per_epoch()))
|
||||
@ -495,7 +497,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Sets the block root for some given slot.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn set_block_root(
|
||||
&mut self,
|
||||
slot: Slot,
|
||||
@ -513,7 +515,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Safely obtains the index for `randao_mixes`
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn get_randao_mix_index(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
@ -535,7 +537,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// See `Self::get_randao_mix`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> {
|
||||
let i = epoch.as_usize() % T::EpochsPerHistoricalVector::to_usize();
|
||||
|
||||
@ -548,7 +550,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> {
|
||||
let i = self.get_randao_mix_index(epoch, AllowNextEpoch::False)?;
|
||||
Ok(&self.randao_mixes[i])
|
||||
@ -556,7 +558,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Set the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> {
|
||||
let i = self.get_randao_mix_index(epoch, AllowNextEpoch::True)?;
|
||||
self.randao_mixes[i] = mix;
|
||||
@ -565,7 +567,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Safely obtains the index for latest state roots, given some `slot`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn get_latest_state_roots_index(&self, slot: Slot) -> Result<usize, Error> {
|
||||
if (slot < self.slot) && (self.slot <= slot + Slot::from(self.state_roots.len())) {
|
||||
Ok(slot.as_usize() % self.state_roots.len())
|
||||
@ -576,7 +578,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Gets the state root for some slot.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> {
|
||||
let i = self.get_latest_state_roots_index(slot)?;
|
||||
Ok(&self.state_roots[i])
|
||||
@ -584,7 +586,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Gets the oldest (earliest slot) state root.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> {
|
||||
let i =
|
||||
self.get_latest_state_roots_index(self.slot - Slot::from(self.state_roots.len()))?;
|
||||
@ -593,7 +595,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Gets the oldest (earliest slot) block root.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_oldest_block_root(&self) -> Result<&Hash256, Error> {
|
||||
let i = self.get_latest_block_roots_index(self.slot - self.block_roots.len() as u64)?;
|
||||
Ok(&self.block_roots[i])
|
||||
@ -601,7 +603,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Sets the latest state root for slot.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> {
|
||||
let i = self.get_latest_state_roots_index(slot)?;
|
||||
self.state_roots[i] = state_root;
|
||||
@ -610,7 +612,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Safely obtain the index for `slashings`, given some `epoch`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn get_slashings_index(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
@ -630,14 +632,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Get a reference to the entire `slashings` vector.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_all_slashings(&self) -> &[u64] {
|
||||
&self.slashings
|
||||
}
|
||||
|
||||
/// Get the total slashed balances for some epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_slashings(&self, epoch: Epoch) -> Result<u64, Error> {
|
||||
let i = self.get_slashings_index(epoch, AllowNextEpoch::False)?;
|
||||
Ok(self.slashings[i])
|
||||
@ -645,7 +647,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Set the total slashed balances for some epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn set_slashings(&mut self, epoch: Epoch, value: u64) -> Result<(), Error> {
|
||||
let i = self.get_slashings_index(epoch, AllowNextEpoch::True)?;
|
||||
self.slashings[i] = value;
|
||||
@ -654,7 +656,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Get the attestations from the current or previous epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_matching_source_attestations(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
@ -670,7 +672,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Generate a seed for the given `epoch`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_seed(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
@ -701,7 +703,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_effective_balance(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
@ -715,7 +717,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn compute_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch {
|
||||
epoch + 1 + spec.max_seed_lookahead
|
||||
}
|
||||
@ -724,7 +726,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Uses the epoch cache, and will error if it isn't initialized.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
Ok(std::cmp::max(
|
||||
spec.min_per_epoch_churn_limit,
|
||||
@ -739,7 +741,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_attestation_duties(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
@ -752,7 +754,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the combined effective balance of an array of validators.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_total_balance(
|
||||
&self,
|
||||
validator_indices: &[usize],
|
||||
@ -925,7 +927,26 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
if let Some(mut cache) = cache {
|
||||
// Note: we return early if the tree hash fails, leaving `self.tree_hash_cache` as
|
||||
// None. There's no need to keep a cache that fails.
|
||||
let root = cache.recalculate_tree_hash_root(self)?;
|
||||
let root = cache.recalculate_tree_hash_root(&self)?;
|
||||
self.tree_hash_cache = Some(cache);
|
||||
Ok(root)
|
||||
} else {
|
||||
Err(Error::TreeHashCacheNotInitialized)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the tree hash root of the validators using the tree hash cache.
|
||||
///
|
||||
/// Initialize the tree hash cache if it isn't already initialized.
|
||||
pub fn update_validators_tree_hash_cache(&mut self) -> Result<Hash256, Error> {
|
||||
self.initialize_tree_hash_cache();
|
||||
|
||||
let cache = self.tree_hash_cache.take();
|
||||
|
||||
if let Some(mut cache) = cache {
|
||||
// Note: we return early if the tree hash fails, leaving `self.tree_hash_cache` as
|
||||
// None. There's no need to keep a cache that fails.
|
||||
let root = cache.recalculate_validators_tree_hash_root(&self.validators)?;
|
||||
self.tree_hash_cache = Some(cache);
|
||||
Ok(root)
|
||||
} else {
|
||||
@ -959,6 +980,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
pub fn clone_with(&self, config: CloneConfig) -> Self {
|
||||
BeaconState {
|
||||
genesis_time: self.genesis_time,
|
||||
genesis_validators_root: self.genesis_validators_root,
|
||||
slot: self.slot,
|
||||
fork: self.fork.clone(),
|
||||
latest_block_header: self.latest_block_header.clone(),
|
||||
|
@ -22,7 +22,7 @@ pub struct CommitteeCache {
|
||||
impl CommitteeCache {
|
||||
/// Return a new, fully initialized cache.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn initialized<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
epoch: Epoch,
|
||||
@ -87,7 +87,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `&[]` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn active_validator_indices(&self) -> &[usize] {
|
||||
&self.shuffling
|
||||
}
|
||||
@ -96,7 +96,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `&[]` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn shuffling(&self) -> &[usize] {
|
||||
&self.shuffling
|
||||
}
|
||||
@ -202,7 +202,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `usize::default()` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn active_validator_count(&self) -> usize {
|
||||
self.shuffling.len()
|
||||
}
|
||||
@ -211,7 +211,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `usize::default()` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn epoch_committee_count(&self) -> usize {
|
||||
self.committees_per_slot as usize * self.slots_per_epoch as usize
|
||||
}
|
||||
@ -223,7 +223,7 @@ impl CommitteeCache {
|
||||
|
||||
/// Returns a slice of `self.shuffling` that represents the `index`'th committee in the epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn compute_committee(&self, index: usize) -> Option<&[usize]> {
|
||||
Some(&self.shuffling[self.compute_committee_range(index)?])
|
||||
}
|
||||
@ -234,7 +234,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Will also return `None` if the index is out of bounds.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn compute_committee_range(&self, index: usize) -> Option<Range<usize>> {
|
||||
let count = self.epoch_committee_count();
|
||||
if count == 0 || index >= count {
|
||||
@ -261,7 +261,7 @@ impl CommitteeCache {
|
||||
/// Returns a list of all `validators` indices where the validator is active at the given
|
||||
/// `epoch`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec<usize> {
|
||||
let mut active = Vec::with_capacity(validators.len());
|
||||
|
||||
|
@ -82,6 +82,7 @@ impl BeaconTreeHashCache {
|
||||
let mut hasher = MerkleHasher::with_leaves(NUM_BEACON_STATE_HASHING_FIELDS);
|
||||
|
||||
hasher.write(state.genesis_time.tree_hash_root().as_bytes())?;
|
||||
hasher.write(state.genesis_validators_root.tree_hash_root().as_bytes())?;
|
||||
hasher.write(state.slot.tree_hash_root().as_bytes())?;
|
||||
hasher.write(state.fork.tree_hash_root().as_bytes())?;
|
||||
hasher.write(state.latest_block_header.tree_hash_root().as_bytes())?;
|
||||
@ -153,6 +154,14 @@ impl BeaconTreeHashCache {
|
||||
|
||||
hasher.finish().map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Updates the cache and provides the root of the given `validators`.
|
||||
pub fn recalculate_validators_tree_hash_root(
|
||||
&mut self,
|
||||
validators: &[Validator],
|
||||
) -> Result<Hash256, Error> {
|
||||
self.validators.recalculate_tree_hash_root(validators)
|
||||
}
|
||||
}
|
||||
|
||||
/// A specialized cache for computing the tree hash root of `state.validators`.
|
||||
|
@ -3,6 +3,7 @@ use int_to_bytes::int_to_bytes4;
|
||||
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,
|
||||
@ -10,18 +11,21 @@ use utils::{
|
||||
|
||||
/// Each of the BLS signature domains.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum Domain {
|
||||
BeaconProposer,
|
||||
BeaconAttester,
|
||||
Randao,
|
||||
Deposit,
|
||||
VoluntaryExit,
|
||||
SelectionProof,
|
||||
AggregateAndProof,
|
||||
}
|
||||
|
||||
/// Holds all the "constants" for a BeaconChain.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ChainSpec {
|
||||
@ -44,6 +48,9 @@ pub struct ChainSpec {
|
||||
pub shuffle_round_count: u8,
|
||||
pub min_genesis_active_validator_count: u64,
|
||||
pub min_genesis_time: u64,
|
||||
pub hysteresis_quotient: u64,
|
||||
pub hysteresis_downward_multiplier: u64,
|
||||
pub hysteresis_upward_multiplier: u64,
|
||||
|
||||
/*
|
||||
* Gwei values
|
||||
@ -93,6 +100,8 @@ pub struct ChainSpec {
|
||||
domain_randao: u32,
|
||||
domain_deposit: u32,
|
||||
domain_voluntary_exit: u32,
|
||||
domain_selection_proof: u32,
|
||||
domain_aggregate_and_proof: u32,
|
||||
|
||||
/*
|
||||
* Fork choice
|
||||
@ -112,7 +121,7 @@ pub struct ChainSpec {
|
||||
impl ChainSpec {
|
||||
/// Get the domain number, unmodified by the fork.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_domain_constant(&self, domain: Domain) -> u32 {
|
||||
match domain {
|
||||
Domain::BeaconProposer => self.domain_beacon_proposer,
|
||||
@ -120,15 +129,23 @@ impl ChainSpec {
|
||||
Domain::Randao => self.domain_randao,
|
||||
Domain::Deposit => self.domain_deposit,
|
||||
Domain::VoluntaryExit => self.domain_voluntary_exit,
|
||||
Domain::SelectionProof => self.domain_selection_proof,
|
||||
Domain::AggregateAndProof => self.domain_aggregate_and_proof,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the domain number that represents the fork meta and signature domain.
|
||||
/// Get the domain that represents the fork meta and signature domain.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 {
|
||||
/// Spec v0.11.1
|
||||
pub fn get_domain(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
domain: Domain,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
) -> Hash256 {
|
||||
let fork_version = fork.get_fork_version(epoch);
|
||||
self.compute_domain(domain, fork_version)
|
||||
self.compute_domain(domain, fork_version, genesis_validators_root)
|
||||
}
|
||||
|
||||
/// Get the domain for a deposit signature.
|
||||
@ -136,29 +153,64 @@ impl ChainSpec {
|
||||
/// Deposits are valid across forks, thus the deposit domain is computed
|
||||
/// with the genesis fork version.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
pub fn get_deposit_domain(&self) -> u64 {
|
||||
self.compute_domain(Domain::Deposit, self.genesis_fork_version)
|
||||
/// Spec v0.11.1
|
||||
pub fn get_deposit_domain(&self) -> Hash256 {
|
||||
self.compute_domain(Domain::Deposit, self.genesis_fork_version, Hash256::zero())
|
||||
}
|
||||
|
||||
/// Return the 32-byte fork data root for the `current_version` and `genesis_validators_root`.
|
||||
///
|
||||
/// This is used primarily in signature domains to avoid collisions across forks/chains.
|
||||
///
|
||||
/// Spec v0.11.1
|
||||
pub fn compute_fork_data_root(
|
||||
current_version: [u8; 4],
|
||||
genesis_validators_root: Hash256,
|
||||
) -> Hash256 {
|
||||
ForkData {
|
||||
current_version,
|
||||
genesis_validators_root,
|
||||
}
|
||||
.tree_hash_root()
|
||||
}
|
||||
|
||||
/// Return the 4-byte fork digest for the `current_version` and `genesis_validators_root`.
|
||||
///
|
||||
/// This is a digest primarily used for domain separation on the p2p layer.
|
||||
/// 4-bytes suffices for practical separation of forks/chains.
|
||||
pub fn compute_fork_digest(
|
||||
current_version: [u8; 4],
|
||||
genesis_validators_root: Hash256,
|
||||
) -> [u8; 4] {
|
||||
let mut result = [0; 4];
|
||||
let root = Self::compute_fork_data_root(current_version, genesis_validators_root);
|
||||
result.copy_from_slice(&root.as_bytes()[0..4]);
|
||||
result
|
||||
}
|
||||
|
||||
/// Compute a domain by applying the given `fork_version`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
pub fn compute_domain(&self, domain: Domain, fork_version: [u8; 4]) -> u64 {
|
||||
/// Spec v0.11.1
|
||||
pub fn compute_domain(
|
||||
&self,
|
||||
domain: Domain,
|
||||
fork_version: [u8; 4],
|
||||
genesis_validators_root: Hash256,
|
||||
) -> Hash256 {
|
||||
let domain_constant = self.get_domain_constant(domain);
|
||||
|
||||
let mut bytes: Vec<u8> = int_to_bytes4(domain_constant);
|
||||
bytes.append(&mut fork_version.to_vec());
|
||||
let mut domain = [0; 32];
|
||||
domain[0..4].copy_from_slice(&int_to_bytes4(domain_constant));
|
||||
domain[4..].copy_from_slice(
|
||||
&Self::compute_fork_data_root(fork_version, genesis_validators_root)[..28],
|
||||
);
|
||||
|
||||
let mut fork_and_domain = [0; 8];
|
||||
fork_and_domain.copy_from_slice(&bytes);
|
||||
|
||||
u64::from_le_bytes(fork_and_domain)
|
||||
Hash256::from(domain)
|
||||
}
|
||||
|
||||
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn mainnet() -> Self {
|
||||
Self {
|
||||
/*
|
||||
@ -179,6 +231,9 @@ impl ChainSpec {
|
||||
shuffle_round_count: 90,
|
||||
min_genesis_active_validator_count: 16_384,
|
||||
min_genesis_time: 1_578_009_600, // Jan 3, 2020
|
||||
hysteresis_quotient: 4,
|
||||
hysteresis_downward_multiplier: 1,
|
||||
hysteresis_upward_multiplier: 5,
|
||||
|
||||
/*
|
||||
* Gwei values
|
||||
@ -223,6 +278,8 @@ impl ChainSpec {
|
||||
domain_randao: 2,
|
||||
domain_deposit: 3,
|
||||
domain_voluntary_exit: 4,
|
||||
domain_selection_proof: 5,
|
||||
domain_aggregate_and_proof: 6,
|
||||
|
||||
/*
|
||||
* Fork choice
|
||||
@ -245,7 +302,7 @@ impl ChainSpec {
|
||||
|
||||
/// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn minimal() -> Self {
|
||||
// Note: bootnodes to be updated when static nodes exist.
|
||||
let boot_nodes = vec![];
|
||||
@ -257,6 +314,7 @@ impl ChainSpec {
|
||||
min_genesis_active_validator_count: 64,
|
||||
eth1_follow_distance: 16,
|
||||
genesis_fork_version: [0x00, 0x00, 0x00, 0x01],
|
||||
persistent_committee_period: 128,
|
||||
min_genesis_delay: 300,
|
||||
milliseconds_per_slot: 6_000,
|
||||
network_id: 2, // lighthouse testnet network id
|
||||
@ -291,7 +349,6 @@ impl Default for ChainSpec {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use int_to_bytes::int_to_bytes8;
|
||||
|
||||
#[test]
|
||||
fn test_mainnet_spec_can_be_constructed() {
|
||||
@ -299,19 +356,27 @@ mod tests {
|
||||
}
|
||||
|
||||
fn test_domain(domain_type: Domain, raw_domain: u32, spec: &ChainSpec) {
|
||||
let previous_version = [0, 0, 0, 1];
|
||||
let current_version = [0, 0, 0, 2];
|
||||
let genesis_validators_root = Hash256::from_low_u64_le(77);
|
||||
let fork_epoch = Epoch::new(1024);
|
||||
let fork = Fork {
|
||||
previous_version: spec.genesis_fork_version,
|
||||
current_version: spec.genesis_fork_version,
|
||||
epoch: MinimalEthSpec::genesis_epoch(),
|
||||
previous_version,
|
||||
current_version,
|
||||
epoch: fork_epoch,
|
||||
};
|
||||
let epoch = Epoch::new(0);
|
||||
|
||||
let domain = spec.get_domain(epoch, domain_type, &fork);
|
||||
for (epoch, version) in vec![
|
||||
(fork_epoch - 1, previous_version),
|
||||
(fork_epoch, current_version),
|
||||
(fork_epoch + 1, current_version),
|
||||
] {
|
||||
let domain1 = spec.get_domain(epoch, domain_type, &fork, genesis_validators_root);
|
||||
let domain2 = spec.compute_domain(domain_type, version, genesis_validators_root);
|
||||
|
||||
let mut expected = int_to_bytes4(raw_domain);
|
||||
expected.append(&mut fork.get_fork_version(epoch).to_vec());
|
||||
|
||||
assert_eq!(int_to_bytes8(domain), expected);
|
||||
assert_eq!(domain1, domain2);
|
||||
assert_eq!(&domain1.as_bytes()[0..4], &int_to_bytes4(raw_domain)[..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -323,18 +388,25 @@ mod tests {
|
||||
test_domain(Domain::Randao, spec.domain_randao, &spec);
|
||||
test_domain(Domain::Deposit, spec.domain_deposit, &spec);
|
||||
test_domain(Domain::VoluntaryExit, spec.domain_voluntary_exit, &spec);
|
||||
test_domain(Domain::SelectionProof, spec.domain_selection_proof, &spec);
|
||||
test_domain(
|
||||
Domain::AggregateAndProof,
|
||||
spec.domain_aggregate_and_proof,
|
||||
&spec,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Union of a ChainSpec struct and an EthSpec struct that holds constants used for the configs
|
||||
/// from the Ethereum 2 specs repo (https://github.com/ethereum/eth2.0-specs/tree/dev/configs)
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Doesn't include fields of the YAML that we don't need yet (e.g. Phase 1 stuff).
|
||||
///
|
||||
/// Spec v0.11.1
|
||||
// Yaml Config is declared here in order to access domain fields of ChainSpec which are private.
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
#[serde(default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct YamlConfig {
|
||||
// ChainSpec
|
||||
far_future_epoch: u64,
|
||||
@ -352,6 +424,9 @@ pub struct YamlConfig {
|
||||
max_effective_balance: u64,
|
||||
ejection_balance: u64,
|
||||
effective_balance_increment: u64,
|
||||
hysteresis_quotient: u64,
|
||||
hysteresis_downward_multiplier: u64,
|
||||
hysteresis_upward_multiplier: u64,
|
||||
genesis_slot: u64,
|
||||
#[serde(
|
||||
serialize_with = "fork_to_hex_str",
|
||||
@ -403,12 +478,22 @@ pub struct YamlConfig {
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
domain_selection_proof: u32,
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
domain_aggregate_and_proof: u32,
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
// EthSpec
|
||||
justification_bits_length: u32,
|
||||
max_validators_per_committee: u32,
|
||||
genesis_epoch: Epoch,
|
||||
slots_per_epoch: u64,
|
||||
slots_per_eth1_voting_period: usize,
|
||||
epochs_per_eth1_voting_period: u64,
|
||||
slots_per_historical_root: usize,
|
||||
epochs_per_historical_vector: usize,
|
||||
epochs_per_slashings_vector: usize,
|
||||
@ -426,34 +511,6 @@ pub struct YamlConfig {
|
||||
random_subnets_per_validator: u64,
|
||||
epochs_per_random_subnet_subscription: u64,
|
||||
seconds_per_eth1_block: u64,
|
||||
|
||||
// Deposit Contract (unused)
|
||||
#[serde(skip_serializing)]
|
||||
deposit_contract_address: String,
|
||||
|
||||
// Phase 1
|
||||
#[serde(skip_serializing)]
|
||||
epochs_per_custody_period: u32,
|
||||
#[serde(skip_serializing)]
|
||||
custody_period_to_randao_padding: u32,
|
||||
#[serde(skip_serializing)]
|
||||
shard_slots_per_beacon_slot: u32,
|
||||
#[serde(skip_serializing)]
|
||||
epochs_per_shard_period: u32,
|
||||
#[serde(skip_serializing)]
|
||||
phase_1_fork_epoch: u32,
|
||||
#[serde(skip_serializing)]
|
||||
phase_1_fork_slot: u32,
|
||||
#[serde(skip_serializing)]
|
||||
domain_custody_bit_challenge: u32,
|
||||
#[serde(skip_serializing)]
|
||||
domain_shard_proposer: u32,
|
||||
#[serde(skip_serializing)]
|
||||
domain_shard_attester: u32,
|
||||
#[serde(skip_serializing)]
|
||||
max_epochs_per_crosslink: u64,
|
||||
#[serde(skip_serializing)]
|
||||
early_derived_secret_penalty_max_future_epochs: u32,
|
||||
}
|
||||
|
||||
impl Default for YamlConfig {
|
||||
@ -463,7 +520,7 @@ impl Default for YamlConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
impl YamlConfig {
|
||||
pub fn from_spec<T: EthSpec>(spec: &ChainSpec) -> Self {
|
||||
Self {
|
||||
@ -483,6 +540,9 @@ impl YamlConfig {
|
||||
max_effective_balance: spec.max_effective_balance,
|
||||
ejection_balance: spec.ejection_balance,
|
||||
effective_balance_increment: spec.effective_balance_increment,
|
||||
hysteresis_quotient: spec.hysteresis_quotient,
|
||||
hysteresis_downward_multiplier: spec.hysteresis_downward_multiplier,
|
||||
hysteresis_upward_multiplier: spec.hysteresis_upward_multiplier,
|
||||
genesis_slot: spec.genesis_slot.into(),
|
||||
bls_withdrawal_prefix: spec.bls_withdrawal_prefix_byte,
|
||||
seconds_per_slot: spec.milliseconds_per_slot / 1000,
|
||||
@ -504,13 +564,15 @@ impl YamlConfig {
|
||||
domain_randao: spec.domain_randao,
|
||||
domain_deposit: spec.domain_deposit,
|
||||
domain_voluntary_exit: spec.domain_voluntary_exit,
|
||||
domain_selection_proof: spec.domain_selection_proof,
|
||||
domain_aggregate_and_proof: spec.domain_aggregate_and_proof,
|
||||
|
||||
// EthSpec
|
||||
justification_bits_length: T::JustificationBitsLength::to_u32(),
|
||||
max_validators_per_committee: T::MaxValidatorsPerCommittee::to_u32(),
|
||||
genesis_epoch: T::genesis_epoch(),
|
||||
slots_per_epoch: T::slots_per_epoch(),
|
||||
slots_per_eth1_voting_period: T::slots_per_eth1_voting_period(),
|
||||
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(),
|
||||
@ -528,22 +590,6 @@ impl YamlConfig {
|
||||
random_subnets_per_validator: 0,
|
||||
epochs_per_random_subnet_subscription: 0,
|
||||
seconds_per_eth1_block: spec.seconds_per_eth1_block,
|
||||
|
||||
// Deposit Contract (unused)
|
||||
deposit_contract_address: String::new(),
|
||||
|
||||
// Phase 1
|
||||
epochs_per_custody_period: 0,
|
||||
custody_period_to_randao_padding: 0,
|
||||
shard_slots_per_beacon_slot: 0,
|
||||
epochs_per_shard_period: 0,
|
||||
phase_1_fork_epoch: 0,
|
||||
phase_1_fork_slot: 0,
|
||||
domain_custody_bit_challenge: 0,
|
||||
domain_shard_proposer: 0,
|
||||
domain_shard_attester: 0,
|
||||
max_epochs_per_crosslink: 0,
|
||||
early_derived_secret_penalty_max_future_epochs: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,7 +606,7 @@ impl YamlConfig {
|
||||
|| self.max_validators_per_committee != T::MaxValidatorsPerCommittee::to_u32()
|
||||
|| self.genesis_epoch != T::genesis_epoch()
|
||||
|| self.slots_per_epoch != T::slots_per_epoch()
|
||||
|| self.slots_per_eth1_voting_period != T::slots_per_eth1_voting_period()
|
||||
|| 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()
|
||||
@ -589,6 +635,9 @@ impl YamlConfig {
|
||||
min_deposit_amount: self.min_deposit_amount,
|
||||
min_genesis_delay: self.min_genesis_delay,
|
||||
max_effective_balance: self.max_effective_balance,
|
||||
hysteresis_quotient: self.hysteresis_quotient,
|
||||
hysteresis_downward_multiplier: self.hysteresis_downward_multiplier,
|
||||
hysteresis_upward_multiplier: self.hysteresis_upward_multiplier,
|
||||
ejection_balance: self.ejection_balance,
|
||||
effective_balance_increment: self.effective_balance_increment,
|
||||
genesis_slot: Slot::from(self.genesis_slot),
|
||||
@ -608,6 +657,7 @@ impl YamlConfig {
|
||||
inactivity_penalty_quotient: self.inactivity_penalty_quotient,
|
||||
min_slashing_penalty_quotient: self.min_slashing_penalty_quotient,
|
||||
domain_beacon_proposer: self.domain_beacon_proposer,
|
||||
domain_beacon_attester: self.domain_beacon_attester,
|
||||
domain_randao: self.domain_randao,
|
||||
domain_deposit: self.domain_deposit,
|
||||
domain_voluntary_exit: self.domain_voluntary_exit,
|
||||
|
@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Casper FFG checkpoint, used in attestations.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
|
@ -11,7 +11,7 @@ pub const DEPOSIT_TREE_DEPTH: usize = 32;
|
||||
|
||||
/// A deposit to potentially become a beacon chain validator.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct Deposit {
|
||||
pub proof: FixedVector<Hash256, U33>,
|
||||
|
@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// The data supplied by the user to the deposit contract.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct DepositData {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
@ -21,7 +21,7 @@ pub struct DepositData {
|
||||
impl DepositData {
|
||||
/// Create a `DepositMessage` corresponding to this `DepositData`, for signature verification.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn as_deposit_message(&self) -> DepositMessage {
|
||||
DepositMessage {
|
||||
pubkey: self.pubkey.clone(),
|
||||
@ -32,7 +32,7 @@ impl DepositData {
|
||||
|
||||
/// Generate the signature for a given DepositData details.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn create_signature(&self, secret_key: &SecretKey, spec: &ChainSpec) -> SignatureBytes {
|
||||
let domain = spec.get_deposit_domain();
|
||||
let msg = self.as_deposit_message().signing_root(domain);
|
||||
|
@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// The data supplied by the user to the deposit contract.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct DepositMessage {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
|
@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Contains data obtained from the Eth1 chain.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::*;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_types::typenum::{
|
||||
Unsigned, U0, U1, U1024, U1099511627776, U128, U16, U16777216, U2048, U32, U4, U4096, U64,
|
||||
Unsigned, U0, U1, U1024, U1099511627776, U128, U16, U16777216, U2, U2048, U32, U4, U4096, U64,
|
||||
U65536, U8, U8192,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
@ -20,7 +20,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
* Time parameters
|
||||
*/
|
||||
type SlotsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type SlotsPerEth1VotingPeriod: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type EpochsPerEth1VotingPeriod: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type SlotsPerHistoricalRoot: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/*
|
||||
* State list lengths
|
||||
@ -43,9 +43,13 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
/// The length of the `{previous,current}_epoch_attestations` lists.
|
||||
///
|
||||
/// Must be set to `MaxAttestations * SlotsPerEpoch`
|
||||
// NOTE: we could safely instantiate this by using type-level arithmetic, but doing
|
||||
// NOTE: we could safely instantiate these by using type-level arithmetic, but doing
|
||||
// so adds ~25s to the time required to type-check this crate
|
||||
type MaxPendingAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/// The length of `eth1_data_votes`.
|
||||
///
|
||||
/// Must be set to `EpochsPerEth1VotingPeriod * SlotsPerEpoch`
|
||||
type SlotsPerEth1VotingPeriod: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
|
||||
fn default_spec() -> ChainSpec;
|
||||
|
||||
@ -58,7 +62,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
/// Note: the number of committees per slot is constant in each epoch, and depends only on
|
||||
/// the `active_validator_count` during the slot's epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn get_committee_count_per_slot(active_validator_count: usize, spec: &ChainSpec) -> usize {
|
||||
let slots_per_epoch = Self::SlotsPerEpoch::to_usize();
|
||||
|
||||
@ -82,28 +86,28 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
|
||||
/// Returns the `SLOTS_PER_EPOCH` constant for this specification.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn slots_per_epoch() -> u64 {
|
||||
Self::SlotsPerEpoch::to_u64()
|
||||
}
|
||||
|
||||
/// Returns the `SLOTS_PER_HISTORICAL_ROOT` constant for this specification.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn slots_per_historical_root() -> usize {
|
||||
Self::SlotsPerHistoricalRoot::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `EPOCHS_PER_HISTORICAL_VECTOR` constant for this specification.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn epochs_per_historical_vector() -> usize {
|
||||
Self::EpochsPerHistoricalVector::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `SLOTS_PER_ETH1_VOTING_PERIOD` constant for this specification.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
fn slots_per_eth1_voting_period() -> usize {
|
||||
Self::SlotsPerEth1VotingPeriod::to_usize()
|
||||
}
|
||||
@ -119,7 +123,7 @@ macro_rules! params_from_eth_spec {
|
||||
|
||||
/// Ethereum Foundation specifications.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct MainnetEthSpec;
|
||||
|
||||
@ -128,7 +132,7 @@ impl EthSpec for MainnetEthSpec {
|
||||
type MaxValidatorsPerCommittee = U2048;
|
||||
type GenesisEpoch = U0;
|
||||
type SlotsPerEpoch = U32;
|
||||
type SlotsPerEth1VotingPeriod = U1024;
|
||||
type EpochsPerEth1VotingPeriod = U32;
|
||||
type SlotsPerHistoricalRoot = U8192;
|
||||
type EpochsPerHistoricalVector = U65536;
|
||||
type EpochsPerSlashingsVector = U8192;
|
||||
@ -140,6 +144,7 @@ impl EthSpec for MainnetEthSpec {
|
||||
type MaxDeposits = U16;
|
||||
type MaxVoluntaryExits = U16;
|
||||
type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch
|
||||
type SlotsPerEth1VotingPeriod = U1024; // 32 epochs * 32 slots per epoch
|
||||
|
||||
fn default_spec() -> ChainSpec {
|
||||
ChainSpec::mainnet()
|
||||
@ -150,17 +155,18 @@ pub type FoundationBeaconState = BeaconState<MainnetEthSpec>;
|
||||
|
||||
/// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct MinimalEthSpec;
|
||||
|
||||
impl EthSpec for MinimalEthSpec {
|
||||
type SlotsPerEpoch = U8;
|
||||
type SlotsPerEth1VotingPeriod = U16;
|
||||
type EpochsPerEth1VotingPeriod = U2;
|
||||
type SlotsPerHistoricalRoot = U64;
|
||||
type EpochsPerHistoricalVector = U64;
|
||||
type EpochsPerSlashingsVector = U64;
|
||||
type MaxPendingAttestations = U1024; // 128 max attestations * 8 slots per epoch
|
||||
type SlotsPerEth1VotingPeriod = U16; // 2 epochs * 8 slots per epoch
|
||||
|
||||
params_from_eth_spec!(MainnetEthSpec {
|
||||
JustificationBitsLength,
|
||||
@ -188,11 +194,12 @@ pub struct InteropEthSpec;
|
||||
|
||||
impl EthSpec for InteropEthSpec {
|
||||
type SlotsPerEpoch = U8;
|
||||
type EpochsPerEth1VotingPeriod = U2;
|
||||
type SlotsPerHistoricalRoot = U64;
|
||||
type SlotsPerEth1VotingPeriod = U16;
|
||||
type EpochsPerHistoricalVector = U64;
|
||||
type EpochsPerSlashingsVector = U64;
|
||||
type MaxPendingAttestations = U1024; // 128 max attestations * 8 slots per epoch
|
||||
type SlotsPerEth1VotingPeriod = U16; // 2 epochs * 8 slots per epoch
|
||||
|
||||
params_from_eth_spec!(MainnetEthSpec {
|
||||
JustificationBitsLength,
|
||||
|
@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
||||
)]
|
||||
@ -30,7 +30,7 @@ pub struct Fork {
|
||||
impl Fork {
|
||||
/// Return the fork version of the given ``epoch``.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn get_fork_version(&self, epoch: Epoch) -> [u8; 4] {
|
||||
if epoch < self.epoch {
|
||||
return self.previous_version;
|
||||
|
32
eth2/types/src/fork_data.rs
Normal file
32
eth2/types/src/fork_data.rs
Normal file
@ -0,0 +1,32 @@
|
||||
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};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
|
||||
///
|
||||
/// Spec v0.11.1
|
||||
#[derive(
|
||||
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"
|
||||
)]
|
||||
pub current_version: [u8; 4],
|
||||
pub genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
impl SignedRoot for ForkData {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
ssz_and_tree_hash_tests!(ForkData);
|
||||
}
|
@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Historical block and state roots.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct HistoricalBatch<T: EthSpec> {
|
||||
pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
|
@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
///
|
||||
/// To be included in an `AttesterSlashing`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct IndexedAttestation<T: EthSpec> {
|
||||
@ -21,14 +21,14 @@ pub struct IndexedAttestation<T: EthSpec> {
|
||||
impl<T: EthSpec> IndexedAttestation<T> {
|
||||
/// Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn is_double_vote(&self, other: &Self) -> bool {
|
||||
self.data.target.epoch == other.data.target.epoch && self.data != other.data
|
||||
}
|
||||
|
||||
/// Check if ``attestation_data_1`` surrounds ``attestation_data_2``.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn is_surround_vote(&self, other: &Self) -> bool {
|
||||
self.data.source.epoch < other.data.source.epoch
|
||||
&& other.data.target.epoch < self.data.target.epoch
|
||||
|
@ -23,6 +23,7 @@ pub mod deposit_message;
|
||||
pub mod eth1_data;
|
||||
pub mod eth_spec;
|
||||
pub mod fork;
|
||||
pub mod fork_data;
|
||||
pub mod free_attestation;
|
||||
pub mod historical_batch;
|
||||
pub mod indexed_attestation;
|
||||
@ -59,6 +60,7 @@ pub use crate::deposit_data::DepositData;
|
||||
pub use crate::deposit_message::DepositMessage;
|
||||
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::historical_batch::HistoricalBatch;
|
||||
pub use crate::indexed_attestation::IndexedAttestation;
|
||||
|
@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// An attestation that has been included in the state but not yet fully processed.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct PendingAttestation<T: EthSpec> {
|
||||
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
|
||||
|
@ -8,10 +8,9 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Two conflicting proposals from the same proposer (validator).
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct ProposerSlashing {
|
||||
pub proposer_index: u64,
|
||||
pub signed_header_1: SignedBeaconBlockHeader,
|
||||
pub signed_header_2: SignedBeaconBlockHeader,
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ pub enum Error {
|
||||
/// Defines the epochs relative to some epoch. Most useful when referring to the committees prior
|
||||
/// to and following some epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum RelativeEpoch {
|
||||
/// The prior epoch.
|
||||
@ -23,7 +23,7 @@ pub enum RelativeEpoch {
|
||||
impl RelativeEpoch {
|
||||
/// Returns the `epoch` that `self` refers to, with respect to the `base` epoch.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn into_epoch(self, base: Epoch) -> Epoch {
|
||||
match self {
|
||||
// Due to saturating nature of epoch, check for current first.
|
||||
@ -40,7 +40,7 @@ impl RelativeEpoch {
|
||||
/// - `EpochTooLow` when `other` is more than 1 prior to `base`.
|
||||
/// - `EpochTooHigh` when `other` is more than 1 after `base`.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn from_epoch(base: Epoch, other: Epoch) -> Result<Self, Error> {
|
||||
// Due to saturating nature of epoch, check for current first.
|
||||
if other == base {
|
||||
|
@ -8,7 +8,7 @@ use tree_hash::TreeHash;
|
||||
|
||||
/// A `BeaconBlock` and a signature from its proposer.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TestRandom)]
|
||||
#[serde(bound = "E: EthSpec")]
|
||||
pub struct SignedBeaconBlock<E: EthSpec> {
|
||||
@ -34,7 +34,7 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
|
||||
|
||||
/// Returns the `tree_hash_root` of the block.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.message.tree_hash_root()[..])
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// An exit voluntarily submitted a validator who wishes to withdraw.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct SignedBeaconBlockHeader {
|
||||
pub message: BeaconBlockHeader,
|
||||
|
@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// An exit voluntarily submitted a validator who wishes to withdraw.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct SignedVoluntaryExit {
|
||||
pub message: VoluntaryExit,
|
||||
|
@ -9,11 +9,11 @@ use tree_hash_derive::TreeHash;
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct SigningRoot {
|
||||
pub object_root: Hash256,
|
||||
pub domain: u64,
|
||||
pub domain: Hash256,
|
||||
}
|
||||
|
||||
pub trait SignedRoot: TreeHash {
|
||||
fn signing_root(&self, domain: u64) -> Hash256 {
|
||||
fn signing_root(&self, domain: Hash256) -> Hash256 {
|
||||
SigningRoot {
|
||||
object_root: self.tree_hash_root(),
|
||||
domain,
|
||||
|
@ -55,6 +55,7 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
|
||||
signing_validators: &[usize],
|
||||
secret_keys: &[&SecretKey],
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> &mut Self {
|
||||
assert_eq!(
|
||||
@ -77,15 +78,28 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
|
||||
};
|
||||
|
||||
self.attestation
|
||||
.sign(secret_keys[index], committee_index, fork, spec)
|
||||
.sign(
|
||||
secret_keys[index],
|
||||
committee_index,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
.expect("can sign attestation");
|
||||
|
||||
if let AttestationTestTask::BadIndexedAttestationBadSignature = test_task {
|
||||
self.attestation
|
||||
.aggregation_bits
|
||||
.set(committee_index, false)
|
||||
.unwrap();
|
||||
}
|
||||
self.attestation
|
||||
.aggregation_bits
|
||||
.set(committee_index, true)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if test_task == AttestationTestTask::BadIndexedAttestationBadSignature {
|
||||
// Flip an aggregation bit, to make the aggregate invalid
|
||||
// (We also want to avoid making it completely empty)
|
||||
self.attestation
|
||||
.aggregation_bits
|
||||
.set(0, !self.attestation.aggregation_bits.get(0).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
self
|
||||
|
@ -22,6 +22,7 @@ impl TestingAttesterSlashingBuilder {
|
||||
validator_indices: &[u64],
|
||||
signer: F,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> AttesterSlashing<T>
|
||||
where
|
||||
@ -83,8 +84,12 @@ impl TestingAttesterSlashingBuilder {
|
||||
};
|
||||
|
||||
let add_signatures = |attestation: &mut IndexedAttestation<T>| {
|
||||
let domain =
|
||||
spec.get_domain(attestation.data.target.epoch, Domain::BeaconAttester, fork);
|
||||
let domain = spec.get_domain(
|
||||
attestation.data.target.epoch,
|
||||
Domain::BeaconAttester,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = attestation.data.signing_root(domain);
|
||||
|
||||
for validator_index in validator_indices {
|
||||
|
@ -97,12 +97,23 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
self.block.slot = slot;
|
||||
}
|
||||
|
||||
/// Set the proposer index of the block.
|
||||
pub fn set_proposer_index(&mut self, proposer_index: u64) {
|
||||
self.block.proposer_index = proposer_index;
|
||||
}
|
||||
|
||||
/// Sets the randao to be a signature across the blocks epoch.
|
||||
///
|
||||
/// Modifying the block's slot after signing may invalidate the signature.
|
||||
pub fn set_randao_reveal(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) {
|
||||
pub fn set_randao_reveal(
|
||||
&mut self,
|
||||
sk: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let epoch = self.block.slot.epoch(T::slots_per_epoch());
|
||||
let domain = spec.get_domain(epoch, Domain::Randao, fork);
|
||||
let domain = spec.get_domain(epoch, Domain::Randao, fork, genesis_validators_root);
|
||||
let message = epoch.signing_root(domain);
|
||||
self.block.body.randao_reveal = Signature::new(message.as_bytes(), sk);
|
||||
}
|
||||
@ -119,10 +130,17 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
validator_index: u64,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let proposer_slashing =
|
||||
build_proposer_slashing::<T>(test_task, validator_index, secret_key, fork, spec);
|
||||
let proposer_slashing = build_proposer_slashing::<T>(
|
||||
test_task,
|
||||
validator_index,
|
||||
secret_key,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
self.block
|
||||
.body
|
||||
.proposer_slashings
|
||||
@ -137,6 +155,7 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
validator_indices: &[u64],
|
||||
secret_keys: &[&SecretKey],
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let attester_slashing = build_double_vote_attester_slashing(
|
||||
@ -144,6 +163,7 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
validator_indices,
|
||||
secret_keys,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
let _ = self.block.body.attester_slashings.push(attester_slashing);
|
||||
@ -246,6 +266,7 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
signing_validators,
|
||||
&signing_secret_keys,
|
||||
&state.fork,
|
||||
state.genesis_validators_root,
|
||||
spec,
|
||||
);
|
||||
|
||||
@ -355,14 +376,20 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
}
|
||||
|
||||
let builder = TestingVoluntaryExitBuilder::new(exit_epoch, validator_index);
|
||||
let exit = builder.build(sk, &state.fork, spec);
|
||||
let exit = builder.build(sk, &state.fork, state.genesis_validators_root, spec);
|
||||
|
||||
self.block.body.voluntary_exits.push(exit).unwrap();
|
||||
}
|
||||
|
||||
/// Signs and returns the block, consuming the builder.
|
||||
pub fn build(self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> SignedBeaconBlock<T> {
|
||||
self.block.sign(sk, fork, spec)
|
||||
pub fn build(
|
||||
self,
|
||||
sk: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> SignedBeaconBlock<T> {
|
||||
self.block.sign(sk, fork, genesis_validators_root, spec)
|
||||
}
|
||||
|
||||
/// Returns the block, consuming the builder.
|
||||
@ -382,6 +409,7 @@ pub fn build_proposer_slashing<T: EthSpec>(
|
||||
validator_index: u64,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> ProposerSlashing {
|
||||
TestingProposerSlashingBuilder::double_vote::<T>(
|
||||
@ -389,6 +417,7 @@ pub fn build_proposer_slashing<T: EthSpec>(
|
||||
validator_index,
|
||||
secret_key,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
}
|
||||
@ -401,6 +430,7 @@ pub fn build_double_vote_attester_slashing<T: EthSpec>(
|
||||
validator_indices: &[u64],
|
||||
secret_keys: &[&SecretKey],
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> AttesterSlashing<T> {
|
||||
let signer = |validator_index: u64, message: &[u8]| {
|
||||
@ -411,5 +441,12 @@ pub fn build_double_vote_attester_slashing<T: EthSpec>(
|
||||
Signature::new(message, secret_keys[key_index])
|
||||
};
|
||||
|
||||
TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer, fork, spec)
|
||||
TestingAttesterSlashingBuilder::double_vote(
|
||||
test_task,
|
||||
validator_indices,
|
||||
signer,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
}
|
||||
|
@ -12,9 +12,10 @@ impl TestingProposerSlashingBuilder {
|
||||
/// Where domain is a domain "constant" (e.g., `spec.domain_attestation`).
|
||||
pub fn double_vote<T>(
|
||||
test_task: ProposerSlashingTestTask,
|
||||
mut proposer_index: u64,
|
||||
proposer_index: u64,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> ProposerSlashing
|
||||
where
|
||||
@ -31,6 +32,7 @@ impl TestingProposerSlashingBuilder {
|
||||
let mut signed_header_1 = SignedBeaconBlockHeader {
|
||||
message: BeaconBlockHeader {
|
||||
slot,
|
||||
proposer_index,
|
||||
parent_root: hash_1,
|
||||
state_root: hash_1,
|
||||
body_root: hash_1,
|
||||
@ -54,19 +56,25 @@ impl TestingProposerSlashingBuilder {
|
||||
};
|
||||
|
||||
if test_task != ProposerSlashingTestTask::BadProposal1Signature {
|
||||
signed_header_1 = signed_header_1.message.sign::<T>(secret_key, fork, spec);
|
||||
signed_header_1 =
|
||||
signed_header_1
|
||||
.message
|
||||
.sign::<T>(secret_key, fork, genesis_validators_root, spec);
|
||||
}
|
||||
|
||||
if test_task != ProposerSlashingTestTask::BadProposal2Signature {
|
||||
signed_header_2 = signed_header_2.message.sign::<T>(secret_key, fork, spec);
|
||||
signed_header_2 =
|
||||
signed_header_2
|
||||
.message
|
||||
.sign::<T>(secret_key, fork, genesis_validators_root, spec);
|
||||
}
|
||||
|
||||
if test_task == ProposerSlashingTestTask::ProposerUnknown {
|
||||
proposer_index = 3_141_592;
|
||||
signed_header_1.message.proposer_index = 3_141_592;
|
||||
signed_header_2.message.proposer_index = 3_141_592;
|
||||
}
|
||||
|
||||
ProposerSlashing {
|
||||
proposer_index,
|
||||
signed_header_1,
|
||||
signed_header_2,
|
||||
}
|
||||
|
@ -25,8 +25,10 @@ impl TestingVoluntaryExitBuilder {
|
||||
self,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> SignedVoluntaryExit {
|
||||
self.exit.sign(secret_key, fork, spec)
|
||||
self.exit
|
||||
.sign(secret_key, fork, genesis_validators_root, spec)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Information about a `BeaconChain` validator.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
|
||||
pub struct Validator {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
@ -44,7 +44,7 @@ impl Validator {
|
||||
|
||||
/// Returns `true` if the validator is eligible to join the activation queue.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn is_eligible_for_activation_queue(&self, spec: &ChainSpec) -> bool {
|
||||
self.activation_eligibility_epoch == spec.far_future_epoch
|
||||
&& self.effective_balance == spec.max_effective_balance
|
||||
@ -52,7 +52,7 @@ impl Validator {
|
||||
|
||||
/// Returns `true` if the validator is eligible to be activated.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
pub fn is_eligible_for_activation<E: EthSpec>(
|
||||
&self,
|
||||
state: &BeaconState<E>,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
test_utils::TestRandom, ChainSpec, Domain, Epoch, Fork, SecretKey, Signature, SignedRoot,
|
||||
SignedVoluntaryExit,
|
||||
test_utils::TestRandom, ChainSpec, Domain, Epoch, Fork, Hash256, SecretKey, Signature,
|
||||
SignedRoot, SignedVoluntaryExit,
|
||||
};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@ -10,7 +10,7 @@ use tree_hash_derive::TreeHash;
|
||||
|
||||
/// An exit voluntarily submitted a validator who wishes to withdraw.
|
||||
///
|
||||
/// Spec v0.10.1
|
||||
/// Spec v0.11.1
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct VoluntaryExit {
|
||||
/// Earliest epoch when voluntary exit can be processed.
|
||||
@ -25,9 +25,15 @@ impl VoluntaryExit {
|
||||
self,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> SignedVoluntaryExit {
|
||||
let domain = spec.get_domain(self.epoch, Domain::VoluntaryExit, fork);
|
||||
let domain = spec.get_domain(
|
||||
self.epoch,
|
||||
Domain::VoluntaryExit,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = self.signing_root(domain);
|
||||
let signature = Signature::new(message.as_bytes(), &secret_key);
|
||||
SignedVoluntaryExit {
|
||||
|
@ -9,9 +9,9 @@ use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const TAG: &str = "v0.10.1";
|
||||
const TAG: &str = "v0.11.1";
|
||||
// NOTE: the version of the unsafe contract lags the main tag, but the v0.9.2.1 code is compatible
|
||||
// with the unmodified v0.10.1 contract
|
||||
// with the unmodified v0.11.1 contract
|
||||
const UNSAFE_TAG: &str = "v0.9.2.1";
|
||||
|
||||
fn spec_url() -> String {
|
||||
|
@ -23,15 +23,15 @@ impl From<ethabi::Error> for DecodeError {
|
||||
|
||||
pub const CONTRACT_DEPLOY_GAS: usize = 4_000_000;
|
||||
pub const DEPOSIT_GAS: usize = 4_000_000;
|
||||
pub const ABI: &[u8] = include_bytes!("../contracts/v0.10.1_validator_registration.json");
|
||||
pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.10.1_validator_registration.bytecode");
|
||||
pub const ABI: &[u8] = include_bytes!("../contracts/v0.11.1_validator_registration.json");
|
||||
pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.11.1_validator_registration.bytecode");
|
||||
pub const DEPOSIT_DATA_LEN: usize = 420; // lol
|
||||
|
||||
pub mod testnet {
|
||||
pub const ABI: &[u8] =
|
||||
include_bytes!("../contracts/v0.10.1_testnet_validator_registration.json");
|
||||
include_bytes!("../contracts/v0.11.1_testnet_validator_registration.json");
|
||||
pub const BYTECODE: &[u8] =
|
||||
include_bytes!("../contracts/v0.10.1_testnet_validator_registration.bytecode");
|
||||
include_bytes!("../contracts/v0.11.1_testnet_validator_registration.bytecode");
|
||||
}
|
||||
|
||||
pub fn encode_eth1_tx_data(deposit_data: &DepositData) -> Result<Vec<u8>, Error> {
|
||||
|
@ -202,6 +202,7 @@ mod tests {
|
||||
|
||||
type E = MainnetEthSpec;
|
||||
|
||||
/* TODO: disabled until testnet config is updated for v0.11
|
||||
#[test]
|
||||
fn hard_coded_works() {
|
||||
let dir: Eth2TestnetConfig<E> =
|
||||
@ -211,6 +212,7 @@ mod tests {
|
||||
assert!(dir.genesis_state.is_some());
|
||||
assert!(dir.yaml_config.is_some());
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
|
@ -32,10 +32,8 @@ pub fn int_to_bytes3(int: u32) -> Option<Vec<u8>> {
|
||||
}
|
||||
|
||||
/// Returns `int` as little-endian bytes with a length of 4.
|
||||
pub fn int_to_bytes4(int: u32) -> Vec<u8> {
|
||||
let mut bytes = BytesMut::with_capacity(4);
|
||||
bytes.put_u32_le(int);
|
||||
bytes.to_vec()
|
||||
pub fn int_to_bytes4(int: u32) -> [u8; 4] {
|
||||
int.to_le_bytes()
|
||||
}
|
||||
|
||||
/// Returns `int` as little-endian bytes with a length of 8.
|
||||
@ -128,7 +126,7 @@ mod tests {
|
||||
1 => assert_eq!(int_to_bytes1(int as u8), bytes),
|
||||
2 => assert_eq!(int_to_bytes2(int as u16), bytes),
|
||||
3 => assert_eq!(int_to_bytes3(int as u32), Some(bytes)),
|
||||
4 => assert_eq!(int_to_bytes4(int as u32), bytes),
|
||||
4 => assert_eq!(&int_to_bytes4(int as u32)[..], &bytes[..]),
|
||||
8 => assert_eq!(int_to_bytes8(int), bytes),
|
||||
32 => assert_eq!(int_to_bytes32(int), bytes),
|
||||
48 => assert_eq!(int_to_bytes48(int), bytes),
|
||||
|
@ -324,6 +324,14 @@ impl<E: EthSpec> Beacon<E> {
|
||||
.and_then(move |url| client.json_get(url, vec![]))
|
||||
}
|
||||
|
||||
/// Returns the genesis validators root.
|
||||
pub fn get_genesis_validators_root(&self) -> impl Future<Item = Hash256, Error = Error> {
|
||||
let client = self.0.clone();
|
||||
self.url("genesis_validators_root")
|
||||
.into_future()
|
||||
.and_then(move |url| client.json_get(url, vec![]))
|
||||
}
|
||||
|
||||
/// Returns the fork at the head of the beacon chain.
|
||||
pub fn get_fork(&self) -> impl Future<Item = Fork, Error = Error> {
|
||||
let client = self.0.clone();
|
||||
|
@ -2,8 +2,8 @@
|
||||
//! format designed for use in Ethereum 2.0.
|
||||
//!
|
||||
//! Adheres to the Ethereum 2.0 [SSZ
|
||||
//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/ssz/simple-serialize.md)
|
||||
//! at v0.10.1.
|
||||
//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/ssz/simple-serialize.md)
|
||||
//! at v0.11.1.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
|
@ -9,8 +9,8 @@
|
||||
//! for padding and verification.
|
||||
//!
|
||||
//! Adheres to the Ethereum 2.0 [SSZ
|
||||
//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/ssz/simple-serialize.md)
|
||||
//! at v0.10.1.
|
||||
//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/ssz/simple-serialize.md)
|
||||
//! at v0.11.1.
|
||||
//!
|
||||
//! ## Example
|
||||
//! ```
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Provides list-shuffling functions matching the Ethereum 2.0 specification.
|
||||
//!
|
||||
//! See
|
||||
//! [compute_shuffled_index](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_shuffled_index)
|
||||
//! [compute_shuffled_index](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_shuffled_index)
|
||||
//! for specifications.
|
||||
//!
|
||||
//! There are two functions exported by this crate:
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Bump the test tag here and in .gitlab-ci.yml and CI will take care of updating the cached tarballs
|
||||
TESTS_TAG := v0.10.1
|
||||
TESTS_TAG := v0.11.1
|
||||
TESTS = general minimal mainnet
|
||||
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
||||
|
||||
|
@ -81,7 +81,11 @@ impl<E: EthSpec> EpochTransition<E> for Slashings {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
||||
validator_statuses.process_attestations(&state, spec)?;
|
||||
process_slashings(state, validator_statuses.total_balances.current_epoch, spec)?;
|
||||
process_slashings(
|
||||
state,
|
||||
validator_statuses.total_balances.current_epoch(),
|
||||
spec,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +176,6 @@ mod ssz_static {
|
||||
ssz_static_test!(voluntary_exit, VoluntaryExit);
|
||||
}
|
||||
|
||||
/* NOTE: SSZ generic tests disabled, missing from v0.10.0
|
||||
#[test]
|
||||
fn ssz_generic() {
|
||||
SszGenericHandler::<BasicVector>::run();
|
||||
@ -186,7 +185,6 @@ fn ssz_generic() {
|
||||
SszGenericHandler::<Uints>::run();
|
||||
SszGenericHandler::<Containers>::run();
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_justification_and_finalization() {
|
||||
|
@ -143,101 +143,125 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
box_future
|
||||
})
|
||||
})
|
||||
.and_then(move |(beacon_node, remote_eth2_config, genesis_time)| {
|
||||
let log = log_4.clone();
|
||||
.and_then(|(beacon_node, eth2_config, genesis_time)| {
|
||||
beacon_node
|
||||
.http
|
||||
.beacon()
|
||||
.get_genesis_validators_root()
|
||||
.map(move |genesis_validators_root| {
|
||||
(
|
||||
beacon_node,
|
||||
eth2_config,
|
||||
genesis_time,
|
||||
genesis_validators_root,
|
||||
)
|
||||
})
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Unable to read genesis validators root from beacon node: {:?}",
|
||||
e
|
||||
)
|
||||
})
|
||||
})
|
||||
.and_then(
|
||||
move |(beacon_node, remote_eth2_config, genesis_time, genesis_validators_root)| {
|
||||
let log = log_4.clone();
|
||||
|
||||
// Do not permit a connection to a beacon node using different spec constants.
|
||||
if context.eth2_config.spec_constants != remote_eth2_config.spec_constants {
|
||||
return Err(format!(
|
||||
"Beacon node is using an incompatible spec. Got {}, expected {}",
|
||||
remote_eth2_config.spec_constants, context.eth2_config.spec_constants
|
||||
));
|
||||
}
|
||||
// Do not permit a connection to a beacon node using different spec constants.
|
||||
if context.eth2_config.spec_constants != remote_eth2_config.spec_constants {
|
||||
return Err(format!(
|
||||
"Beacon node is using an incompatible spec. Got {}, expected {}",
|
||||
remote_eth2_config.spec_constants, context.eth2_config.spec_constants
|
||||
));
|
||||
}
|
||||
|
||||
// Note: here we just assume the spec variables of the remote node. This is very useful
|
||||
// for testnets, but perhaps a security issue when it comes to mainnet.
|
||||
//
|
||||
// A damaging attack would be for a beacon node to convince the validator client of a
|
||||
// different `SLOTS_PER_EPOCH` variable. This could result in slashable messages being
|
||||
// produced. We are safe from this because `SLOTS_PER_EPOCH` is a type-level constant
|
||||
// for Lighthouse.
|
||||
context.eth2_config = remote_eth2_config;
|
||||
// Note: here we just assume the spec variables of the remote node. This is very useful
|
||||
// for testnets, but perhaps a security issue when it comes to mainnet.
|
||||
//
|
||||
// A damaging attack would be for a beacon node to convince the validator client of a
|
||||
// different `SLOTS_PER_EPOCH` variable. This could result in slashable messages being
|
||||
// produced. We are safe from this because `SLOTS_PER_EPOCH` is a type-level constant
|
||||
// for Lighthouse.
|
||||
context.eth2_config = remote_eth2_config;
|
||||
|
||||
let slot_clock = SystemTimeSlotClock::new(
|
||||
context.eth2_config.spec.genesis_slot,
|
||||
Duration::from_secs(genesis_time),
|
||||
Duration::from_millis(context.eth2_config.spec.milliseconds_per_slot),
|
||||
);
|
||||
let slot_clock = SystemTimeSlotClock::new(
|
||||
context.eth2_config.spec.genesis_slot,
|
||||
Duration::from_secs(genesis_time),
|
||||
Duration::from_millis(context.eth2_config.spec.milliseconds_per_slot),
|
||||
);
|
||||
|
||||
let fork_service = ForkServiceBuilder::new()
|
||||
.slot_clock(slot_clock.clone())
|
||||
.beacon_node(beacon_node.clone())
|
||||
.runtime_context(context.service_context("fork".into()))
|
||||
.build()?;
|
||||
let fork_service = ForkServiceBuilder::new()
|
||||
.slot_clock(slot_clock.clone())
|
||||
.beacon_node(beacon_node.clone())
|
||||
.runtime_context(context.service_context("fork".into()))
|
||||
.build()?;
|
||||
|
||||
let validator_store: ValidatorStore<SystemTimeSlotClock, T> =
|
||||
match &config.key_source {
|
||||
// Load pre-existing validators from the data dir.
|
||||
//
|
||||
// Use the `account_manager` to generate these files.
|
||||
KeySource::Disk => ValidatorStore::load_from_disk(
|
||||
config.data_dir.clone(),
|
||||
context.eth2_config.spec.clone(),
|
||||
fork_service.clone(),
|
||||
log.clone(),
|
||||
)?,
|
||||
// Generate ephemeral insecure keypairs for testing purposes.
|
||||
//
|
||||
// Do not use in production.
|
||||
KeySource::InsecureKeypairs(indices) => {
|
||||
ValidatorStore::insecure_ephemeral_validators(
|
||||
&indices,
|
||||
let validator_store: ValidatorStore<SystemTimeSlotClock, T> =
|
||||
match &config.key_source {
|
||||
// Load pre-existing validators from the data dir.
|
||||
//
|
||||
// Use the `account_manager` to generate these files.
|
||||
KeySource::Disk => ValidatorStore::load_from_disk(
|
||||
config.data_dir.clone(),
|
||||
genesis_validators_root,
|
||||
context.eth2_config.spec.clone(),
|
||||
fork_service.clone(),
|
||||
log.clone(),
|
||||
)?
|
||||
}
|
||||
};
|
||||
)?,
|
||||
// Generate ephemeral insecure keypairs for testing purposes.
|
||||
//
|
||||
// Do not use in production.
|
||||
KeySource::InsecureKeypairs(indices) => {
|
||||
ValidatorStore::insecure_ephemeral_validators(
|
||||
&indices,
|
||||
genesis_validators_root,
|
||||
context.eth2_config.spec.clone(),
|
||||
fork_service.clone(),
|
||||
log.clone(),
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
info!(
|
||||
log,
|
||||
"Loaded validator keypair store";
|
||||
"voting_validators" => validator_store.num_voting_validators()
|
||||
);
|
||||
info!(
|
||||
log,
|
||||
"Loaded validator keypair store";
|
||||
"voting_validators" => validator_store.num_voting_validators()
|
||||
);
|
||||
|
||||
let duties_service = DutiesServiceBuilder::new()
|
||||
.slot_clock(slot_clock.clone())
|
||||
.validator_store(validator_store.clone())
|
||||
.beacon_node(beacon_node.clone())
|
||||
.runtime_context(context.service_context("duties".into()))
|
||||
.allow_unsynced_beacon_node(config.allow_unsynced_beacon_node)
|
||||
.build()?;
|
||||
let duties_service = DutiesServiceBuilder::new()
|
||||
.slot_clock(slot_clock.clone())
|
||||
.validator_store(validator_store.clone())
|
||||
.beacon_node(beacon_node.clone())
|
||||
.runtime_context(context.service_context("duties".into()))
|
||||
.allow_unsynced_beacon_node(config.allow_unsynced_beacon_node)
|
||||
.build()?;
|
||||
|
||||
let block_service = BlockServiceBuilder::new()
|
||||
.duties_service(duties_service.clone())
|
||||
.slot_clock(slot_clock.clone())
|
||||
.validator_store(validator_store.clone())
|
||||
.beacon_node(beacon_node.clone())
|
||||
.runtime_context(context.service_context("block".into()))
|
||||
.build()?;
|
||||
let block_service = BlockServiceBuilder::new()
|
||||
.duties_service(duties_service.clone())
|
||||
.slot_clock(slot_clock.clone())
|
||||
.validator_store(validator_store.clone())
|
||||
.beacon_node(beacon_node.clone())
|
||||
.runtime_context(context.service_context("block".into()))
|
||||
.build()?;
|
||||
|
||||
let attestation_service = AttestationServiceBuilder::new()
|
||||
.duties_service(duties_service.clone())
|
||||
.slot_clock(slot_clock)
|
||||
.validator_store(validator_store)
|
||||
.beacon_node(beacon_node)
|
||||
.runtime_context(context.service_context("attestation".into()))
|
||||
.build()?;
|
||||
let attestation_service = AttestationServiceBuilder::new()
|
||||
.duties_service(duties_service.clone())
|
||||
.slot_clock(slot_clock)
|
||||
.validator_store(validator_store)
|
||||
.beacon_node(beacon_node)
|
||||
.runtime_context(context.service_context("attestation".into()))
|
||||
.build()?;
|
||||
|
||||
Ok(Self {
|
||||
context,
|
||||
duties_service,
|
||||
fork_service,
|
||||
block_service,
|
||||
attestation_service,
|
||||
exit_signals: vec![],
|
||||
})
|
||||
})
|
||||
Ok(Self {
|
||||
context,
|
||||
duties_service,
|
||||
fork_service,
|
||||
block_service,
|
||||
attestation_service,
|
||||
exit_signals: vec![],
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn start_service(&mut self) -> Result<(), String> {
|
||||
|
@ -12,13 +12,14 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tempdir::TempDir;
|
||||
use types::{
|
||||
Attestation, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, PublicKey, Signature,
|
||||
SignedBeaconBlock, SignedRoot,
|
||||
Attestation, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, Hash256, PublicKey,
|
||||
Signature, SignedBeaconBlock, SignedRoot,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ValidatorStore<T, E: EthSpec> {
|
||||
validators: Arc<RwLock<HashMap<PublicKey, ValidatorDirectory>>>,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: Arc<ChainSpec>,
|
||||
log: Logger,
|
||||
temp_dir: Option<Arc<TempDir>>,
|
||||
@ -29,6 +30,7 @@ pub struct ValidatorStore<T, E: EthSpec> {
|
||||
impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
pub fn load_from_disk(
|
||||
base_dir: PathBuf,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: ChainSpec,
|
||||
fork_service: ForkService<T, E>,
|
||||
log: Logger,
|
||||
@ -66,6 +68,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
|
||||
Ok(Self {
|
||||
validators: Arc::new(RwLock::new(HashMap::from_par_iter(validator_key_values))),
|
||||
genesis_validators_root,
|
||||
spec: Arc::new(spec),
|
||||
log,
|
||||
temp_dir: None,
|
||||
@ -76,6 +79,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
|
||||
pub fn insecure_ephemeral_validators(
|
||||
validator_indices: &[usize],
|
||||
genesis_validators_root: Hash256,
|
||||
spec: ChainSpec,
|
||||
fork_service: ForkService<T, E>,
|
||||
log: Logger,
|
||||
@ -107,6 +111,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
|
||||
Ok(Self {
|
||||
validators: Arc::new(RwLock::new(HashMap::from_iter(validators))),
|
||||
genesis_validators_root,
|
||||
spec: Arc::new(spec),
|
||||
log,
|
||||
temp_dir: Some(Arc::new(temp_dir)),
|
||||
@ -144,7 +149,12 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
.get(validator_pubkey)
|
||||
.and_then(|validator_dir| {
|
||||
let voting_keypair = validator_dir.voting_keypair.as_ref()?;
|
||||
let domain = self.spec.get_domain(epoch, Domain::Randao, &self.fork()?);
|
||||
let domain = self.spec.get_domain(
|
||||
epoch,
|
||||
Domain::Randao,
|
||||
&self.fork()?,
|
||||
self.genesis_validators_root,
|
||||
);
|
||||
let message = epoch.signing_root(domain);
|
||||
|
||||
Some(Signature::new(message.as_bytes(), &voting_keypair.sk))
|
||||
@ -162,7 +172,12 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
.get(validator_pubkey)
|
||||
.and_then(|validator_dir| {
|
||||
let voting_keypair = validator_dir.voting_keypair.as_ref()?;
|
||||
Some(block.sign(&voting_keypair.sk, &self.fork()?, &self.spec))
|
||||
Some(block.sign(
|
||||
&voting_keypair.sk,
|
||||
&self.fork()?,
|
||||
self.genesis_validators_root,
|
||||
&self.spec,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@ -184,6 +199,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
&voting_keypair.sk,
|
||||
validator_committee_position,
|
||||
&self.fork()?,
|
||||
self.genesis_validators_root,
|
||||
&self.spec,
|
||||
)
|
||||
.map_err(|e| {
|
||||
|
Loading…
Reference in New Issue
Block a user