mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-09 19:51:47 +00:00
There are certain crates which we re-export within `types` which creates a fragmented DevEx, where there are various ways to import the same crates.
```rust
// consensus/types/src/lib.rs
pub use bls::{
AggregatePublicKey, AggregateSignature, Error as BlsError, Keypair, PUBLIC_KEY_BYTES_LEN,
PublicKey, PublicKeyBytes, SIGNATURE_BYTES_LEN, SecretKey, Signature, SignatureBytes,
get_withdrawal_credentials,
};
pub use context_deserialize::{ContextDeserialize, context_deserialize};
pub use fixed_bytes::FixedBytesExtended;
pub use milhouse::{self, List, Vector};
pub use ssz_types::{BitList, BitVector, FixedVector, VariableList, typenum, typenum::Unsigned};
pub use superstruct::superstruct;
```
This PR removes these re-exports and makes it explicit that these types are imported from a non-`consensus/types` crate.
Co-Authored-By: Mac L <mjladson@pm.me>
342 lines
11 KiB
Rust
342 lines
11 KiB
Rust
use crate::common::genesis_deposits;
|
|
use bls::{Keypair, PublicKey, Signature};
|
|
use ethereum_hashing::hash;
|
|
use rayon::prelude::*;
|
|
use ssz::Encode;
|
|
use state_processing::initialize_beacon_state_from_eth1;
|
|
use types::{BeaconState, ChainSpec, DepositData, EthSpec, ExecutionPayloadHeader, Hash256};
|
|
|
|
pub const DEFAULT_ETH1_BLOCK_HASH: &[u8] = &[0x42; 32];
|
|
|
|
pub fn bls_withdrawal_credentials(pubkey: &PublicKey, spec: &ChainSpec) -> Hash256 {
|
|
let mut credentials = hash(&pubkey.as_ssz_bytes());
|
|
credentials[0] = spec.bls_withdrawal_prefix_byte;
|
|
Hash256::from_slice(&credentials)
|
|
}
|
|
|
|
fn eth1_withdrawal_credentials(pubkey: &PublicKey, spec: &ChainSpec) -> Hash256 {
|
|
let fake_execution_address = &hash(&pubkey.as_ssz_bytes())[0..20];
|
|
let mut credentials = [0u8; 32];
|
|
credentials[0] = spec.eth1_address_withdrawal_prefix_byte;
|
|
credentials[12..].copy_from_slice(fake_execution_address);
|
|
Hash256::from_slice(&credentials)
|
|
}
|
|
|
|
pub type WithdrawalCredentialsFn =
|
|
Box<dyn for<'a> Fn(usize, &'a PublicKey, &'a ChainSpec) -> Hash256>;
|
|
|
|
/// Builds a genesis state as defined by the Eth2 interop procedure (see below).
|
|
///
|
|
/// Reference:
|
|
/// https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start
|
|
#[derive(Default)]
|
|
pub struct InteropGenesisBuilder<E: EthSpec> {
|
|
/// Mapping from validator index to initial balance for each validator.
|
|
///
|
|
/// If `None`, then the default balance of 32 ETH will be used.
|
|
initial_balance_fn: Option<Box<dyn Fn(usize) -> u64>>,
|
|
|
|
/// Mapping from validator index and pubkey to withdrawal credentials for each validator.
|
|
///
|
|
/// If `None`, then default BLS withdrawal credentials will be used.
|
|
withdrawal_credentials_fn: Option<WithdrawalCredentialsFn>,
|
|
|
|
/// The execution payload header to embed in the genesis state.
|
|
execution_payload_header: Option<ExecutionPayloadHeader<E>>,
|
|
}
|
|
|
|
impl<E: EthSpec> InteropGenesisBuilder<E> {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
pub fn set_initial_balance_fn(mut self, initial_balance_fn: Box<dyn Fn(usize) -> u64>) -> Self {
|
|
self.initial_balance_fn = Some(initial_balance_fn);
|
|
self
|
|
}
|
|
|
|
pub fn set_withdrawal_credentials_fn(
|
|
mut self,
|
|
withdrawal_credentials_fn: WithdrawalCredentialsFn,
|
|
) -> Self {
|
|
self.withdrawal_credentials_fn = Some(withdrawal_credentials_fn);
|
|
self
|
|
}
|
|
|
|
pub fn set_alternating_eth1_withdrawal_credentials(self) -> Self {
|
|
self.set_withdrawal_credentials_fn(Box::new(alternating_eth1_withdrawal_credentials_fn))
|
|
}
|
|
|
|
pub fn set_execution_payload_header(
|
|
self,
|
|
execution_payload_header: ExecutionPayloadHeader<E>,
|
|
) -> Self {
|
|
self.set_opt_execution_payload_header(Some(execution_payload_header))
|
|
}
|
|
|
|
pub fn set_opt_execution_payload_header(
|
|
mut self,
|
|
execution_payload_header: Option<ExecutionPayloadHeader<E>>,
|
|
) -> Self {
|
|
self.execution_payload_header = execution_payload_header;
|
|
self
|
|
}
|
|
|
|
pub fn build_genesis_state(
|
|
self,
|
|
keypairs: &[Keypair],
|
|
genesis_time: u64,
|
|
eth1_block_hash: Hash256,
|
|
spec: &ChainSpec,
|
|
) -> Result<BeaconState<E>, String> {
|
|
// Generate withdrawal credentials using provided function, or default BLS.
|
|
let withdrawal_credentials_fn = self.withdrawal_credentials_fn.unwrap_or_else(|| {
|
|
Box::new(|_, pubkey, spec| bls_withdrawal_credentials(pubkey, spec))
|
|
});
|
|
|
|
let withdrawal_credentials = keypairs
|
|
.iter()
|
|
.map(|key| &key.pk)
|
|
.enumerate()
|
|
.map(|(i, pubkey)| withdrawal_credentials_fn(i, pubkey, spec))
|
|
.collect::<Vec<_>>();
|
|
|
|
// Generate initial balances.
|
|
let initial_balance_fn = self
|
|
.initial_balance_fn
|
|
.unwrap_or_else(|| Box::new(|_| spec.max_effective_balance));
|
|
|
|
let eth1_timestamp = 2_u64.pow(40);
|
|
|
|
let initial_balances = (0..keypairs.len())
|
|
.map(initial_balance_fn)
|
|
.collect::<Vec<_>>();
|
|
|
|
let datas = keypairs
|
|
.into_par_iter()
|
|
.zip(withdrawal_credentials.into_par_iter())
|
|
.zip(initial_balances.into_par_iter())
|
|
.map(|((keypair, withdrawal_credentials), amount)| {
|
|
let mut data = DepositData {
|
|
withdrawal_credentials,
|
|
pubkey: keypair.pk.clone().into(),
|
|
amount,
|
|
signature: Signature::empty().into(),
|
|
};
|
|
|
|
data.signature = data.create_signature(&keypair.sk, spec);
|
|
|
|
data
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let mut state = initialize_beacon_state_from_eth1(
|
|
eth1_block_hash,
|
|
eth1_timestamp,
|
|
genesis_deposits(datas, spec)?,
|
|
self.execution_payload_header,
|
|
spec,
|
|
)
|
|
.map_err(|e| format!("Unable to initialize genesis state: {:?}", e))?;
|
|
|
|
*state.genesis_time_mut() = genesis_time;
|
|
|
|
// Invalidate all the caches after all the manual state surgery.
|
|
state
|
|
.drop_all_caches()
|
|
.map_err(|e| format!("Unable to drop caches: {:?}", e))?;
|
|
|
|
Ok(state)
|
|
}
|
|
}
|
|
|
|
pub fn interop_genesis_state<E: EthSpec>(
|
|
keypairs: &[Keypair],
|
|
genesis_time: u64,
|
|
eth1_block_hash: Hash256,
|
|
execution_payload_header: Option<ExecutionPayloadHeader<E>>,
|
|
spec: &ChainSpec,
|
|
) -> Result<BeaconState<E>, String> {
|
|
InteropGenesisBuilder::new()
|
|
.set_opt_execution_payload_header(execution_payload_header)
|
|
.build_genesis_state(keypairs, genesis_time, eth1_block_hash, spec)
|
|
}
|
|
|
|
fn alternating_eth1_withdrawal_credentials_fn<'a>(
|
|
index: usize,
|
|
pubkey: &'a PublicKey,
|
|
spec: &'a ChainSpec,
|
|
) -> Hash256 {
|
|
if index.is_multiple_of(2) {
|
|
bls_withdrawal_credentials(pubkey, spec)
|
|
} else {
|
|
eth1_withdrawal_credentials(pubkey, spec)
|
|
}
|
|
}
|
|
|
|
// returns an interop genesis state except every other
|
|
// validator has eth1 withdrawal credentials
|
|
pub fn interop_genesis_state_with_eth1<E: EthSpec>(
|
|
keypairs: &[Keypair],
|
|
genesis_time: u64,
|
|
eth1_block_hash: Hash256,
|
|
execution_payload_header: Option<ExecutionPayloadHeader<E>>,
|
|
spec: &ChainSpec,
|
|
) -> Result<BeaconState<E>, String> {
|
|
InteropGenesisBuilder::new()
|
|
.set_alternating_eth1_withdrawal_credentials()
|
|
.set_opt_execution_payload_header(execution_payload_header)
|
|
.build_genesis_state(keypairs, genesis_time, eth1_block_hash, spec)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use types::{MinimalEthSpec, test_utils::generate_deterministic_keypairs};
|
|
|
|
type TestEthSpec = MinimalEthSpec;
|
|
|
|
#[test]
|
|
fn interop_state() {
|
|
let validator_count = 16;
|
|
let genesis_time = 42;
|
|
let spec = &TestEthSpec::default_spec();
|
|
|
|
let keypairs = generate_deterministic_keypairs(validator_count);
|
|
|
|
let state = interop_genesis_state::<TestEthSpec>(
|
|
&keypairs,
|
|
genesis_time,
|
|
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
|
|
None,
|
|
spec,
|
|
)
|
|
.expect("should build state");
|
|
|
|
assert_eq!(
|
|
state.eth1_data().block_hash,
|
|
Hash256::from_slice(&[0x42; 32]),
|
|
"eth1 block hash should be co-ordinated junk"
|
|
);
|
|
|
|
assert_eq!(
|
|
state.genesis_time(),
|
|
genesis_time,
|
|
"genesis time should be as specified"
|
|
);
|
|
|
|
for b in state.balances() {
|
|
assert_eq!(
|
|
*b, spec.max_effective_balance,
|
|
"validator balances should be max effective balance"
|
|
);
|
|
}
|
|
|
|
for v in state.validators() {
|
|
let creds = v.withdrawal_credentials;
|
|
assert_eq!(
|
|
creds.as_slice()[0],
|
|
spec.bls_withdrawal_prefix_byte,
|
|
"first byte of withdrawal creds should be bls prefix"
|
|
);
|
|
assert_eq!(
|
|
&creds.as_slice()[1..],
|
|
&hash(&v.pubkey.as_ssz_bytes())[1..],
|
|
"rest of withdrawal creds should be pubkey hash"
|
|
)
|
|
}
|
|
|
|
assert_eq!(
|
|
state.balances().len(),
|
|
validator_count,
|
|
"validator balances len should be correct"
|
|
);
|
|
|
|
assert_eq!(
|
|
state.validators().len(),
|
|
validator_count,
|
|
"validator count should be correct"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn interop_state_with_eth1() {
|
|
let validator_count = 16;
|
|
let genesis_time = 42;
|
|
let spec = &TestEthSpec::default_spec();
|
|
|
|
let keypairs = generate_deterministic_keypairs(validator_count);
|
|
|
|
let state = interop_genesis_state_with_eth1::<TestEthSpec>(
|
|
&keypairs,
|
|
genesis_time,
|
|
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
|
|
None,
|
|
spec,
|
|
)
|
|
.expect("should build state");
|
|
|
|
assert_eq!(
|
|
state.eth1_data().block_hash,
|
|
Hash256::from_slice(&[0x42; 32]),
|
|
"eth1 block hash should be co-ordinated junk"
|
|
);
|
|
|
|
assert_eq!(
|
|
state.genesis_time(),
|
|
genesis_time,
|
|
"genesis time should be as specified"
|
|
);
|
|
|
|
for b in state.balances() {
|
|
assert_eq!(
|
|
*b, spec.max_effective_balance,
|
|
"validator balances should be max effective balance"
|
|
);
|
|
}
|
|
|
|
for (index, v) in state.validators().iter().enumerate() {
|
|
let withdrawal_credientials = v.withdrawal_credentials;
|
|
let creds = withdrawal_credientials.as_slice();
|
|
if index % 2 == 0 {
|
|
assert_eq!(
|
|
creds[0], spec.bls_withdrawal_prefix_byte,
|
|
"first byte of withdrawal creds should be bls prefix"
|
|
);
|
|
assert_eq!(
|
|
&creds[1..],
|
|
&hash(&v.pubkey.as_ssz_bytes())[1..],
|
|
"rest of withdrawal creds should be pubkey hash"
|
|
);
|
|
} else {
|
|
assert_eq!(
|
|
creds[0], spec.eth1_address_withdrawal_prefix_byte,
|
|
"first byte of withdrawal creds should be eth1 prefix"
|
|
);
|
|
assert_eq!(
|
|
creds[1..12],
|
|
[0u8; 11],
|
|
"bytes [1:12] of withdrawal creds must be zero"
|
|
);
|
|
assert_eq!(
|
|
&creds[12..],
|
|
&hash(&v.pubkey.as_ssz_bytes())[0..20],
|
|
"rest of withdrawal creds should be first 20 bytes of pubkey hash"
|
|
)
|
|
}
|
|
}
|
|
|
|
assert_eq!(
|
|
state.balances().len(),
|
|
validator_count,
|
|
"validator balances len should be correct"
|
|
);
|
|
|
|
assert_eq!(
|
|
state.validators().len(),
|
|
validator_count,
|
|
"validator count should be correct"
|
|
);
|
|
}
|
|
}
|