mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 04:37:13 +00:00
Merge latest master
This commit is contained in:
@@ -22,6 +22,10 @@ pub enum Error {
|
||||
/// There was an error attempting to read from a `BeaconState`. Block
|
||||
/// validity was not determined.
|
||||
BeaconStateError(BeaconStateError),
|
||||
/// The `BeaconBlock` has a `proposer_index` that does not match the index we computed locally.
|
||||
///
|
||||
/// The block is invalid.
|
||||
IncorrectBlockProposer { block: u64, local_shuffling: u64 },
|
||||
/// Failed to load a signature set. The block may be invalid or we failed to process it.
|
||||
SignatureSetError(SignatureSetError),
|
||||
}
|
||||
@@ -34,7 +38,18 @@ impl From<BeaconStateError> for Error {
|
||||
|
||||
impl From<SignatureSetError> for Error {
|
||||
fn from(e: SignatureSetError) -> Error {
|
||||
Error::SignatureSetError(e)
|
||||
match e {
|
||||
// Make a special distinction for `IncorrectBlockProposer` since it indicates an
|
||||
// invalid block, not an internal error.
|
||||
SignatureSetError::IncorrectBlockProposer {
|
||||
block,
|
||||
local_shuffling,
|
||||
} => Error::IncorrectBlockProposer {
|
||||
block,
|
||||
local_shuffling,
|
||||
},
|
||||
e => Error::SignatureSetError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ use tree_hash::TreeHash;
|
||||
use types::{
|
||||
AggregateSignature, AttesterSlashing, BeaconBlock, BeaconState, BeaconStateError, ChainSpec,
|
||||
DepositData, Domain, EthSpec, Fork, Hash256, IndexedAttestation, ProposerSlashing, PublicKey,
|
||||
Signature, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot, SignedVoluntaryExit,
|
||||
SigningRoot,
|
||||
Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot,
|
||||
SignedVoluntaryExit, SigningRoot,
|
||||
};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -26,6 +26,10 @@ pub enum Error {
|
||||
/// Attempted to find the public key of a validator that does not exist. You cannot distinguish
|
||||
/// between an error and an invalid block in this case.
|
||||
ValidatorUnknown(u64),
|
||||
/// The `BeaconBlock` has a `proposer_index` that does not match the index we computed locally.
|
||||
///
|
||||
/// The block is invalid.
|
||||
IncorrectBlockProposer { block: u64, local_shuffling: u64 },
|
||||
/// The public keys supplied do not match the number of objects requiring keys. Block validity
|
||||
/// was not determined.
|
||||
MismatchedPublicKeyLen { pubkey_len: usize, other_len: usize },
|
||||
@@ -73,6 +77,13 @@ where
|
||||
let block = &signed_block.message;
|
||||
let proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
|
||||
|
||||
if proposer_index as u64 != block.proposer_index {
|
||||
return Err(Error::IncorrectBlockProposer {
|
||||
block: block.proposer_index,
|
||||
local_shuffling: proposer_index as u64,
|
||||
});
|
||||
}
|
||||
|
||||
let domain = spec.get_domain(
|
||||
block.slot.epoch(T::slots_per_epoch()),
|
||||
Domain::BeaconProposer,
|
||||
@@ -343,3 +354,74 @@ where
|
||||
message,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn signed_aggregate_selection_proof_signature_set<'a, T, F>(
|
||||
get_pubkey: F,
|
||||
signed_aggregate_and_proof: &'a SignedAggregateAndProof<T>,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &'a ChainSpec,
|
||||
) -> Result<SignatureSet>
|
||||
where
|
||||
T: EthSpec,
|
||||
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
|
||||
{
|
||||
let slot = signed_aggregate_and_proof.message.aggregate.data.slot;
|
||||
|
||||
let domain = spec.get_domain(
|
||||
slot.epoch(T::slots_per_epoch()),
|
||||
Domain::SelectionProof,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = slot.signing_root(domain).as_bytes().to_vec();
|
||||
let signature = &signed_aggregate_and_proof.message.selection_proof;
|
||||
let validator_index = signed_aggregate_and_proof.message.aggregator_index;
|
||||
|
||||
Ok(SignatureSet::single(
|
||||
signature,
|
||||
get_pubkey(validator_index as usize)
|
||||
.ok_or_else(|| Error::ValidatorUnknown(validator_index))?,
|
||||
message,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn signed_aggregate_signature_set<'a, T, F>(
|
||||
get_pubkey: F,
|
||||
signed_aggregate_and_proof: &'a SignedAggregateAndProof<T>,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &'a ChainSpec,
|
||||
) -> Result<SignatureSet>
|
||||
where
|
||||
T: EthSpec,
|
||||
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
|
||||
{
|
||||
let target_epoch = signed_aggregate_and_proof
|
||||
.message
|
||||
.aggregate
|
||||
.data
|
||||
.target
|
||||
.epoch;
|
||||
|
||||
let domain = spec.get_domain(
|
||||
target_epoch,
|
||||
Domain::AggregateAndProof,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = signed_aggregate_and_proof
|
||||
.message
|
||||
.signing_root(domain)
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
let signature = &signed_aggregate_and_proof.signature;
|
||||
let validator_index = signed_aggregate_and_proof.message.aggregator_index;
|
||||
|
||||
Ok(SignatureSet::single(
|
||||
signature,
|
||||
get_pubkey(validator_index as usize)
|
||||
.ok_or_else(|| Error::ValidatorUnknown(validator_index))?,
|
||||
message,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1039,7 +1039,12 @@ fn invalid_proposer_slashing_duplicate_slashing() {
|
||||
|
||||
let slashing = block.message.body.proposer_slashings[0].clone();
|
||||
let slashed_proposer = slashing.signed_header_1.message.proposer_index;
|
||||
block.message.body.proposer_slashings.push(slashing);
|
||||
block
|
||||
.message
|
||||
.body
|
||||
.proposer_slashings
|
||||
.push(slashing)
|
||||
.expect("should push slashing");
|
||||
|
||||
let result = per_block_processing(
|
||||
&mut state,
|
||||
|
||||
@@ -27,22 +27,28 @@ pub struct AggregateAndProof<T: EthSpec> {
|
||||
impl<T: EthSpec> AggregateAndProof<T> {
|
||||
/// Produces a new `AggregateAndProof` with a `selection_proof` generated by signing
|
||||
/// `aggregate.data.slot` with `secret_key`.
|
||||
///
|
||||
/// If `selection_proof.is_none()` it will be computed locally.
|
||||
pub fn from_aggregate(
|
||||
aggregator_index: u64,
|
||||
aggregate: Attestation<T>,
|
||||
selection_proof: Option<SelectionProof>,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let selection_proof = SelectionProof::new::<T>(
|
||||
aggregate.data.slot,
|
||||
secret_key,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
.into();
|
||||
let selection_proof = selection_proof
|
||||
.unwrap_or_else(|| {
|
||||
SelectionProof::new::<T>(
|
||||
aggregate.data.slot,
|
||||
secret_key,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
})
|
||||
.into();
|
||||
|
||||
Self {
|
||||
aggregator_index,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use crate::{ChainSpec, Domain, EthSpec, Fork, Hash256, SecretKey, Signature, SignedRoot, Slot};
|
||||
use crate::{
|
||||
ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, SecretKey, Signature, SignedRoot, Slot,
|
||||
};
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use std::cmp;
|
||||
use std::convert::TryInto;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
@@ -26,7 +29,23 @@ impl SelectionProof {
|
||||
Self(Signature::new(message.as_bytes(), secret_key))
|
||||
}
|
||||
|
||||
pub fn is_aggregator(&self, modulo: u64) -> Result<bool, ArithError> {
|
||||
/// Returns the "modulo" used for determining if a `SelectionProof` elects an aggregator.
|
||||
pub fn modulo(committee_len: usize, spec: &ChainSpec) -> Result<u64, ArithError> {
|
||||
Ok(cmp::max(
|
||||
1,
|
||||
(committee_len as u64).safe_div(spec.target_aggregators_per_committee)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn is_aggregator(
|
||||
&self,
|
||||
committee_len: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<bool, ArithError> {
|
||||
self.is_aggregator_from_modulo(Self::modulo(committee_len, spec)?)
|
||||
}
|
||||
|
||||
pub fn is_aggregator_from_modulo(&self, modulo: u64) -> Result<bool, ArithError> {
|
||||
let signature_hash = self.0.tree_hash_root();
|
||||
let signature_hash_int = u64::from_le_bytes(
|
||||
signature_hash[0..8]
|
||||
@@ -37,6 +56,25 @@ impl SelectionProof {
|
||||
|
||||
signature_hash_int.safe_rem(modulo).map(|rem| rem == 0)
|
||||
}
|
||||
|
||||
pub fn verify<T: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
pubkey: &PublicKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> bool {
|
||||
let domain = spec.get_domain(
|
||||
slot.epoch(T::slots_per_epoch()),
|
||||
Domain::SelectionProof,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = slot.signing_root(domain);
|
||||
|
||||
self.0.verify(message.as_bytes(), pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Signature> for SelectionProof {
|
||||
@@ -44,3 +82,9 @@ impl Into<Signature> for SelectionProof {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for SelectionProof {
|
||||
fn from(sig: Signature) -> Self {
|
||||
Self(sig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
AggregateAndProof, Attestation, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey,
|
||||
SecretKey, Signature, SignedRoot,
|
||||
SecretKey, SelectionProof, Signature, SignedRoot,
|
||||
};
|
||||
use crate::test_utils::TestRandom;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@@ -25,9 +25,12 @@ pub struct SignedAggregateAndProof<T: EthSpec> {
|
||||
impl<T: EthSpec> SignedAggregateAndProof<T> {
|
||||
/// Produces a new `SignedAggregateAndProof` with a `selection_proof` generated by signing
|
||||
/// `aggregate.data.slot` with `secret_key`.
|
||||
///
|
||||
/// If `selection_proof.is_none()` it will be computed locally.
|
||||
pub fn from_aggregate(
|
||||
aggregator_index: u64,
|
||||
aggregate: Attestation<T>,
|
||||
selection_proof: Option<SelectionProof>,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
@@ -36,6 +39,7 @@ impl<T: EthSpec> SignedAggregateAndProof<T> {
|
||||
let message = AggregateAndProof::from_aggregate(
|
||||
aggregator_index,
|
||||
aggregate,
|
||||
selection_proof,
|
||||
secret_key,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use crate::{test_utils::TestRandom, BeaconBlock, EthSpec, Hash256, Slot};
|
||||
use std::fmt;
|
||||
|
||||
use crate::{
|
||||
test_utils::TestRandom, BeaconBlock, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey,
|
||||
SignedRoot, SigningRoot, Slot,
|
||||
};
|
||||
use bls::Signature;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::fmt;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
@@ -47,6 +49,38 @@ pub struct SignedBeaconBlock<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> SignedBeaconBlock<E> {
|
||||
/// Verify `self.signature`.
|
||||
///
|
||||
/// If the root of `block.message` is already known it can be passed in via `object_root_opt`.
|
||||
/// Otherwise, it will be computed locally.
|
||||
pub fn verify_signature(
|
||||
&self,
|
||||
object_root_opt: Option<Hash256>,
|
||||
pubkey: &PublicKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> bool {
|
||||
let domain = spec.get_domain(
|
||||
self.message.slot.epoch(E::slots_per_epoch()),
|
||||
Domain::BeaconProposer,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = if let Some(object_root) = object_root_opt {
|
||||
SigningRoot {
|
||||
object_root,
|
||||
domain,
|
||||
}
|
||||
.tree_hash_root()
|
||||
} else {
|
||||
self.message.signing_root(domain)
|
||||
};
|
||||
|
||||
self.signature.verify(message.as_bytes(), pubkey)
|
||||
}
|
||||
|
||||
/// Convenience accessor for the block's slot.
|
||||
pub fn slot(&self) -> Slot {
|
||||
self.message.slot
|
||||
|
||||
1
eth2/utils/eth2_testnet_config/.gitignore
vendored
1
eth2/utils/eth2_testnet_config/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
testnet*
|
||||
schlesi-*
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/// Pulls down the latest Lighthouse testnet from https://github.com/eth2-clients/eth2-testnets
|
||||
//! Downloads a testnet configuration from Github.
|
||||
|
||||
use reqwest;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const TESTNET_ID: &str = "testnet5";
|
||||
const TESTNET_ID: &str = "schlesi-v0-11";
|
||||
|
||||
fn main() {
|
||||
if !base_dir().exists() {
|
||||
@@ -37,16 +38,25 @@ pub fn get_all_files() -> Result<(), String> {
|
||||
|
||||
pub fn get_file(filename: &str) -> Result<(), String> {
|
||||
let url = format!(
|
||||
"https://raw.githubusercontent.com/eth2-clients/eth2-testnets/master/lighthouse/{}/{}",
|
||||
TESTNET_ID, filename
|
||||
"https://raw.githubusercontent.com/goerli/schlesi/839866fe29a1b4df3a87bfe2ff1257c8a58671c9/light/{}",
|
||||
filename
|
||||
);
|
||||
|
||||
let path = base_dir().join(filename);
|
||||
let mut file =
|
||||
File::create(path).map_err(|e| format!("Failed to create {}: {:?}", filename, e))?;
|
||||
|
||||
let contents = reqwest::blocking::get(&url)
|
||||
let request = reqwest::blocking::Client::builder()
|
||||
.build()
|
||||
.map_err(|_| "Could not build request client".to_string())?
|
||||
.get(&url)
|
||||
.timeout(std::time::Duration::from_secs(120));
|
||||
|
||||
let contents = request
|
||||
.send()
|
||||
.map_err(|e| format!("Failed to download {}: {}", filename, e))?
|
||||
.error_for_status()
|
||||
.map_err(|e| format!("Error downloading {}: {}", filename, e))?
|
||||
.bytes()
|
||||
.map_err(|e| format!("Failed to read {} response bytes: {}", filename, e))?;
|
||||
|
||||
|
||||
@@ -20,11 +20,14 @@ pub const BOOT_ENR_FILE: &str = "boot_enr.yaml";
|
||||
pub const GENESIS_STATE_FILE: &str = "genesis.ssz";
|
||||
pub const YAML_CONFIG_FILE: &str = "config.yaml";
|
||||
|
||||
pub const HARDCODED_YAML_CONFIG: &[u8] = include_bytes!("../testnet5/config.yaml");
|
||||
pub const HARDCODED_DEPLOY_BLOCK: &[u8] = include_bytes!("../testnet5/deploy_block.txt");
|
||||
pub const HARDCODED_DEPOSIT_CONTRACT: &[u8] = include_bytes!("../testnet5/deposit_contract.txt");
|
||||
pub const HARDCODED_GENESIS_STATE: &[u8] = include_bytes!("../testnet5/genesis.ssz");
|
||||
pub const HARDCODED_BOOT_ENR: &[u8] = include_bytes!("../testnet5/boot_enr.yaml");
|
||||
pub const HARDCODED_TESTNET: &str = "schlesi-v0-11";
|
||||
|
||||
pub const HARDCODED_YAML_CONFIG: &[u8] = include_bytes!("../schlesi-v0-11/config.yaml");
|
||||
pub const HARDCODED_DEPLOY_BLOCK: &[u8] = include_bytes!("../schlesi-v0-11/deploy_block.txt");
|
||||
pub const HARDCODED_DEPOSIT_CONTRACT: &[u8] =
|
||||
include_bytes!("../schlesi-v0-11/deposit_contract.txt");
|
||||
pub const HARDCODED_GENESIS_STATE: &[u8] = include_bytes!("../schlesi-v0-11/genesis.ssz");
|
||||
pub const HARDCODED_BOOT_ENR: &[u8] = include_bytes!("../schlesi-v0-11/boot_enr.yaml");
|
||||
|
||||
/// Specifies an Eth2 testnet.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user