Add interop-spec genesis procedure

This commit is contained in:
Paul Hauner 2019-08-30 15:33:34 +10:00
parent e154b30232
commit 6234adc0d6
No known key found for this signature in database
GPG Key ID: 5E2CFF9B75FA63DF
7 changed files with 181 additions and 17 deletions

View File

@ -6,6 +6,7 @@ edition = "2018"
[dependencies]
eth2_config = { path = "../../eth2/utils/eth2_config" }
merkle_proof = { path = "../../eth2/utils/merkle_proof" }
store = { path = "../store" }
parking_lot = "0.7"
lazy_static = "1.3.0"
@ -21,6 +22,7 @@ eth2-libp2p = { path = "../eth2-libp2p" }
slog = { version = "^2.2.3" , features = ["max_level_trace"] }
sloggers = { version = "^0.3" }
slot_clock = { path = "../../eth2/utils/slot_clock" }
eth2_hashing = { path = "../../eth2/utils/eth2_hashing" }
eth2_ssz = "0.1"
eth2_ssz_derive = "0.1"
state_processing = { path = "../../eth2/state_processing" }

View File

@ -1,11 +1,20 @@
use super::bootstrapper::Bootstrapper;
use crate::{BeaconChain, BeaconChainTypes};
use eth2_hashing::hash;
use merkle_proof::MerkleTree;
use rayon::prelude::*;
use slog::Logger;
use ssz::Encode;
use state_processing::initialize_beacon_state_from_eth1;
use std::fs::File;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::SystemTime;
use types::{test_utils::TestingBeaconStateBuilder, BeaconBlock, BeaconState, ChainSpec, EthSpec};
use tree_hash::{SignedRoot, TreeHash};
use types::{
test_utils::generate_deterministic_keypairs, BeaconBlock, BeaconState, ChainSpec, Deposit,
DepositData, Domain, EthSpec, Fork, Hash256, PublicKey, Signature,
};
enum BuildStrategy<T: BeaconChainTypes> {
FromGenesis {
@ -27,7 +36,7 @@ impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
minutes: u64,
spec: ChainSpec,
log: Logger,
) -> Self {
) -> Result<Self, String> {
Self::quick_start(recent_genesis_time(minutes), validator_count, spec, log)
}
@ -36,14 +45,10 @@ impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
validator_count: usize,
spec: ChainSpec,
log: Logger,
) -> Self {
let (mut genesis_state, _keypairs) =
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &spec)
.build();
) -> Result<Self, String> {
let genesis_state = interop_genesis_state(validator_count, genesis_time, &spec)?;
genesis_state.genesis_time = genesis_time;
Self::from_genesis_state(genesis_state, spec, log)
Ok(Self::from_genesis_state(genesis_state, spec, log))
}
pub fn yaml_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
@ -125,6 +130,95 @@ fn genesis_block<T: EthSpec>(genesis_state: &BeaconState<T>, spec: &ChainSpec) -
genesis_block
}
fn interop_genesis_state<T: EthSpec>(
validator_count: usize,
genesis_time: u64,
spec: &ChainSpec,
) -> Result<BeaconState<T>, String> {
let keypairs = generate_deterministic_keypairs(validator_count);
let eth1_block_hash = Hash256::from_slice(&[42; 32]);
let eth1_timestamp = 2_u64.pow(40);
let amount = spec.max_effective_balance;
dbg!(amount);
let withdrawal_credentials = |pubkey: &PublicKey| {
let mut credentials = hash(&pubkey.as_ssz_bytes());
credentials[0] = spec.bls_withdrawal_prefix_byte;
Hash256::from_slice(&credentials)
};
let datas = keypairs
.into_par_iter()
.map(|keypair| {
let mut data = DepositData {
withdrawal_credentials: withdrawal_credentials(&keypair.pk),
pubkey: keypair.pk.into(),
amount,
signature: Signature::empty_signature().into(),
};
let domain = spec.get_domain(
spec.genesis_slot.epoch(T::slots_per_epoch()),
Domain::Deposit,
&Fork::default(),
);
data.signature = Signature::new(&data.signed_root()[..], domain, &keypair.sk).into();
data
})
.collect::<Vec<_>>();
let deposit_root_leaves = datas
.par_iter()
.map(|data| Hash256::from_slice(&data.tree_hash_root()))
.collect::<Vec<_>>();
let mut proofs = vec![];
for i in 1..=deposit_root_leaves.len() {
// Note: this implementation is not so efficient.
//
// If `MerkleTree` had a push method, we could just build one tree and sample it instead of
// rebuilding the tree for each deposit.
let tree = MerkleTree::create(
&deposit_root_leaves[0..i],
spec.deposit_contract_tree_depth as usize,
);
let (_, mut proof) = tree.generate_proof(i - 1, spec.deposit_contract_tree_depth as usize);
proof.push(Hash256::from_slice(&int_to_bytes32(i)));
assert_eq!(
proof.len(),
spec.deposit_contract_tree_depth as usize + 1,
"Deposit proof should be correct len"
);
proofs.push(proof);
}
let deposits = datas
.into_par_iter()
.zip(proofs.into_par_iter())
.map(|(data, proof)| (data, proof.into()))
.map(|(data, proof)| Deposit { proof, data })
.collect::<Vec<_>>();
let mut state =
initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits, spec)
.map_err(|e| format!("Unable to initialize genesis state: {:?}", e))?;
state.genesis_time = genesis_time;
Ok(state)
}
/// Returns `int` as little-endian bytes with a length of 32.
fn int_to_bytes32(int: usize) -> Vec<u8> {
let mut vec = int.to_le_bytes().to_vec();
vec.resize(32, 0);
vec
}
/// Returns the system time, mod 30 minutes.
///
/// Used for easily creating testnets.
@ -134,6 +228,66 @@ fn recent_genesis_time(minutes: u64) -> u64 {
.unwrap()
.as_secs();
let secs_after_last_period = now.checked_rem(minutes * 60).unwrap_or(0);
// genesis is now the last 15 minute block.
now - secs_after_last_period
}
#[cfg(test)]
mod test {
use super::*;
use types::{EthSpec, MinimalEthSpec};
type TestEthSpec = MinimalEthSpec;
#[test]
fn interop_state() {
let validator_count = 16;
let genesis_time = 42;
let spec = &TestEthSpec::default_spec();
let state = interop_genesis_state::<TestEthSpec>(validator_count, genesis_time, spec)
.expect("should build state");
assert_eq!(
state.eth1_data.block_hash,
Hash256::from_slice(&[42; 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.as_bytes();
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"
)
}
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"
);
}
}

View File

@ -96,7 +96,7 @@ where
*minutes,
spec.clone(),
log.clone(),
),
)?,
BeaconChainStartMethod::Generated {
validator_count,
genesis_time,
@ -105,7 +105,7 @@ where
*validator_count,
spec.clone(),
log.clone(),
),
)?,
BeaconChainStartMethod::Yaml { file } => {
BeaconChainBuilder::yaml_state(file, spec.clone(), log.clone())?
}

View File

@ -16,9 +16,9 @@ use state_processing::per_block_processing::errors::{
};
use state_processing::per_block_processing::{
get_slashable_indices_modular, verify_attestation_for_block_inclusion,
verify_attestation_for_state, verify_attester_slashing, verify_exit,
verify_exit_time_independent_only, verify_proposer_slashing, verify_transfer,
verify_transfer_time_independent_only, VerifySignatures,
verify_attester_slashing, verify_exit, verify_exit_time_independent_only,
verify_proposer_slashing, verify_transfer, verify_transfer_time_independent_only,
VerifySignatures,
};
use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet};
use std::marker::PhantomData;

View File

@ -182,7 +182,7 @@ macro_rules! impl_display {
&self,
record: &slog::Record,
key: slog::Key,
serializer: &mut slog::Serializer,
serializer: &mut dyn slog::Serializer,
) -> slog::Result {
slog::Value::serialize(&self.0, record, key, serializer)
}

View File

@ -94,7 +94,7 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
/// Creates the builder from an existing set of keypairs.
pub fn from_keypairs(keypairs: Vec<Keypair>, spec: &ChainSpec) -> Self {
let validator_count = keypairs.len();
let starting_balance = 32_000_000_000;
let starting_balance = spec.max_effective_balance;
debug!(
"Building {} Validator objects from keypairs...",

View File

@ -1,5 +1,6 @@
use super::{SecretKey, BLS_PUBLIC_KEY_BYTE_SIZE};
use milagro_bls::G1Point;
use milagro_bls::PublicKey as RawPublicKey;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, HexVisitor};
@ -24,6 +25,13 @@ impl FakePublicKey {
Self::zero()
}
pub fn from_raw(raw: RawPublicKey) -> Self {
Self {
bytes: raw.clone().as_bytes(),
point: G1Point::new(),
}
}
/// Creates a new all-zero's public key
pub fn zero() -> Self {
Self {