mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-04 21:34:36 +00:00
Merge remote-tracking branch 'origin/master' into v0.9
This commit is contained in:
@@ -5,7 +5,7 @@ extern crate lazy_static;
|
||||
|
||||
use beacon_chain::test_utils::{
|
||||
generate_deterministic_keypairs, AttestationStrategy,
|
||||
BeaconChainHarness as BaseBeaconChainHarness, BlockStrategy,
|
||||
BeaconChainHarness as BaseBeaconChainHarness, BlockStrategy, HarnessType,
|
||||
};
|
||||
use lmd_ghost::{LmdGhost, ThreadSafeReducedTree as BaseThreadSafeReducedTree};
|
||||
use rand::{prelude::*, rngs::StdRng};
|
||||
@@ -21,7 +21,7 @@ pub const VALIDATOR_COUNT: usize = 3 * 8;
|
||||
|
||||
type TestEthSpec = MinimalEthSpec;
|
||||
type ThreadSafeReducedTree = BaseThreadSafeReducedTree<MemoryStore, TestEthSpec>;
|
||||
type BeaconChainHarness = BaseBeaconChainHarness<ThreadSafeReducedTree, TestEthSpec>;
|
||||
type BeaconChainHarness = BaseBeaconChainHarness<HarnessType<TestEthSpec>>;
|
||||
type RootAndSlot = (Hash256, Slot);
|
||||
|
||||
lazy_static! {
|
||||
@@ -54,7 +54,10 @@ struct ForkedHarness {
|
||||
impl ForkedHarness {
|
||||
/// A new standard instance of with constant parameters.
|
||||
pub fn new() -> Self {
|
||||
let harness = BeaconChainHarness::new(generate_deterministic_keypairs(VALIDATOR_COUNT));
|
||||
let harness = BeaconChainHarness::new(
|
||||
MinimalEthSpec,
|
||||
generate_deterministic_keypairs(VALIDATOR_COUNT),
|
||||
);
|
||||
|
||||
// Move past the zero slot.
|
||||
harness.advance_slot();
|
||||
|
||||
@@ -554,7 +554,7 @@ mod tests {
|
||||
|
||||
let mut state = BeaconState::random_for_test(rng);
|
||||
|
||||
state.fork = Fork::genesis(MainnetEthSpec::genesis_epoch());
|
||||
state.fork = Fork::default();
|
||||
|
||||
(spec, state)
|
||||
}
|
||||
@@ -575,13 +575,27 @@ mod tests {
|
||||
spec: &ChainSpec,
|
||||
extra_signer: Option<usize>,
|
||||
) -> Attestation<E> {
|
||||
let mut builder = TestingAttestationBuilder::new(state, committee, slot, index);
|
||||
let mut builder = TestingAttestationBuilder::new(
|
||||
AttestationTestTask::Valid,
|
||||
state,
|
||||
committee,
|
||||
slot,
|
||||
index,
|
||||
spec,
|
||||
);
|
||||
let signers = &committee[signing_range];
|
||||
let committee_keys = signers.iter().map(|&i| &keypairs[i].sk).collect::<Vec<_>>();
|
||||
builder.sign(signers, &committee_keys, &state.fork, spec);
|
||||
builder.sign(
|
||||
AttestationTestTask::Valid,
|
||||
signers,
|
||||
&committee_keys,
|
||||
&state.fork,
|
||||
spec,
|
||||
);
|
||||
extra_signer.map(|c_idx| {
|
||||
let validator_index = committee[c_idx];
|
||||
builder.sign(
|
||||
AttestationTestTask::Valid,
|
||||
&[validator_index],
|
||||
&[&keypairs[validator_index].sk],
|
||||
&state.fork,
|
||||
|
||||
@@ -37,18 +37,7 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
|
||||
process_deposit(&mut state, &deposit, spec, true)?;
|
||||
}
|
||||
|
||||
// Process activations
|
||||
for (index, validator) in state.validators.iter_mut().enumerate() {
|
||||
let balance = state.balances[index];
|
||||
validator.effective_balance = std::cmp::min(
|
||||
balance - balance % spec.effective_balance_increment,
|
||||
spec.max_effective_balance,
|
||||
);
|
||||
if validator.effective_balance == spec.max_effective_balance {
|
||||
validator.activation_eligibility_epoch = T::genesis_epoch();
|
||||
validator.activation_epoch = T::genesis_epoch();
|
||||
}
|
||||
}
|
||||
process_activations(&mut state, spec);
|
||||
|
||||
// Now that we have our validators, initialize the caches (including the committees)
|
||||
state.build_all_caches(spec)?;
|
||||
@@ -64,3 +53,20 @@ pub fn is_valid_genesis_state<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSp
|
||||
&& state.get_active_validator_indices(T::genesis_epoch()).len() as u64
|
||||
>= spec.min_genesis_active_validator_count
|
||||
}
|
||||
|
||||
/// Activate genesis validators, if their balance is acceptable.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
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];
|
||||
validator.effective_balance = std::cmp::min(
|
||||
balance - balance % spec.effective_balance_increment,
|
||||
spec.max_effective_balance,
|
||||
);
|
||||
if validator.effective_balance == spec.max_effective_balance {
|
||||
validator.activation_eligibility_epoch = T::genesis_epoch();
|
||||
validator.activation_epoch = T::genesis_epoch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub mod per_epoch_processing;
|
||||
pub mod per_slot_processing;
|
||||
pub mod test_utils;
|
||||
|
||||
pub use genesis::{initialize_beacon_state_from_eth1, is_valid_genesis_state};
|
||||
pub use genesis::{initialize_beacon_state_from_eth1, is_valid_genesis_state, process_activations};
|
||||
pub use per_block_processing::{
|
||||
errors::BlockProcessingError, per_block_processing, BlockSignatureStrategy, VerifySignatures,
|
||||
};
|
||||
|
||||
@@ -435,7 +435,7 @@ pub fn process_deposit<T: EthSpec>(
|
||||
} else {
|
||||
// The signature should be checked for new validators. Return early for a bad
|
||||
// signature.
|
||||
if verify_deposit_signature(state, deposit, spec).is_err() {
|
||||
if verify_deposit_signature(&deposit.data, spec).is_err() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use std::convert::TryInto;
|
||||
use tree_hash::SignedRoot;
|
||||
use types::test_utils::{TestingBeaconBlockBuilder, TestingBeaconStateBuilder};
|
||||
use types::test_utils::{
|
||||
AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask,
|
||||
ProposerSlashingTestTask, TestingBeaconBlockBuilder, TestingBeaconStateBuilder,
|
||||
};
|
||||
use types::*;
|
||||
|
||||
pub struct BlockProcessingBuilder<T: EthSpec> {
|
||||
@@ -30,6 +34,242 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
self.state_builder.build_caches(&spec).unwrap();
|
||||
}
|
||||
|
||||
pub fn build_with_n_deposits(
|
||||
mut self,
|
||||
num_deposits: u64,
|
||||
test_task: DepositTestTask,
|
||||
randao_sk: Option<SecretKey>,
|
||||
previous_block_root: Option<Hash256>,
|
||||
spec: &ChainSpec,
|
||||
) -> (BeaconBlock<T>, BeaconState<T>) {
|
||||
let (mut state, keypairs) = self.state_builder.build();
|
||||
|
||||
let builder = &mut self.block_builder;
|
||||
|
||||
builder.set_slot(state.slot);
|
||||
|
||||
match previous_block_root {
|
||||
Some(root) => builder.set_parent_root(root),
|
||||
None => builder.set_parent_root(Hash256::from_slice(
|
||||
&state.latest_block_header.signed_root(),
|
||||
)),
|
||||
}
|
||||
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
}
|
||||
|
||||
self.block_builder.insert_deposits(
|
||||
spec.max_effective_balance,
|
||||
test_task,
|
||||
1,
|
||||
num_deposits,
|
||||
&mut state,
|
||||
spec,
|
||||
);
|
||||
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
|
||||
pub fn build_with_n_exits(
|
||||
mut self,
|
||||
num_exits: usize,
|
||||
test_task: ExitTestTask,
|
||||
randao_sk: Option<SecretKey>,
|
||||
previous_block_root: Option<Hash256>,
|
||||
spec: &ChainSpec,
|
||||
) -> (BeaconBlock<T>, BeaconState<T>) {
|
||||
let (mut state, keypairs) = self.state_builder.build();
|
||||
let builder = &mut self.block_builder;
|
||||
|
||||
builder.set_slot(state.slot);
|
||||
|
||||
match previous_block_root {
|
||||
Some(root) => builder.set_parent_root(root),
|
||||
None => builder.set_parent_root(Hash256::from_slice(
|
||||
&state.latest_block_header.signed_root(),
|
||||
)),
|
||||
}
|
||||
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
}
|
||||
match test_task {
|
||||
ExitTestTask::AlreadyInitiated => {
|
||||
for _ in 0..2 {
|
||||
self.block_builder.insert_exit(
|
||||
test_task,
|
||||
&mut state,
|
||||
(0 as usize).try_into().unwrap(),
|
||||
&keypairs[0].sk,
|
||||
spec,
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
for (i, keypair) in keypairs.iter().take(num_exits).enumerate() {
|
||||
self.block_builder.insert_exit(
|
||||
test_task,
|
||||
&mut state,
|
||||
(i as usize).try_into().unwrap(),
|
||||
&keypair.sk,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
|
||||
pub fn build_with_n_attestations(
|
||||
mut self,
|
||||
test_task: AttestationTestTask,
|
||||
num_attestations: u64,
|
||||
randao_sk: Option<SecretKey>,
|
||||
previous_block_root: Option<Hash256>,
|
||||
spec: &ChainSpec,
|
||||
) -> (BeaconBlock<T>, BeaconState<T>) {
|
||||
let (state, keypairs) = self.state_builder.build();
|
||||
let builder = &mut self.block_builder;
|
||||
|
||||
builder.set_slot(state.slot);
|
||||
|
||||
match previous_block_root {
|
||||
Some(root) => builder.set_parent_root(root),
|
||||
None => builder.set_parent_root(Hash256::from_slice(
|
||||
&state.latest_block_header.signed_root(),
|
||||
)),
|
||||
}
|
||||
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
}
|
||||
|
||||
let all_secret_keys: Vec<&SecretKey> = keypairs.iter().map(|keypair| &keypair.sk).collect();
|
||||
self.block_builder
|
||||
.insert_attestations(
|
||||
test_task,
|
||||
&state,
|
||||
&all_secret_keys,
|
||||
num_attestations as usize,
|
||||
spec,
|
||||
)
|
||||
.unwrap();
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
|
||||
pub fn build_with_attester_slashing(
|
||||
mut self,
|
||||
test_task: AttesterSlashingTestTask,
|
||||
num_attester_slashings: u64,
|
||||
randao_sk: Option<SecretKey>,
|
||||
previous_block_root: Option<Hash256>,
|
||||
spec: &ChainSpec,
|
||||
) -> (BeaconBlock<T>, BeaconState<T>) {
|
||||
let (state, keypairs) = self.state_builder.build();
|
||||
let builder = &mut self.block_builder;
|
||||
|
||||
builder.set_slot(state.slot);
|
||||
|
||||
match previous_block_root {
|
||||
Some(root) => builder.set_parent_root(root),
|
||||
None => builder.set_parent_root(Hash256::from_slice(
|
||||
&state.latest_block_header.signed_root(),
|
||||
)),
|
||||
}
|
||||
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
}
|
||||
|
||||
let mut validator_indices = vec![];
|
||||
let mut secret_keys = vec![];
|
||||
for i in 0..num_attester_slashings {
|
||||
validator_indices.push(i);
|
||||
secret_keys.push(&keypairs[i as usize].sk);
|
||||
}
|
||||
|
||||
for _ in 0..num_attester_slashings {
|
||||
self.block_builder.insert_attester_slashing(
|
||||
test_task,
|
||||
&validator_indices,
|
||||
&secret_keys,
|
||||
&state.fork,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
|
||||
pub fn build_with_proposer_slashing(
|
||||
mut self,
|
||||
test_task: ProposerSlashingTestTask,
|
||||
num_proposer_slashings: u64,
|
||||
randao_sk: Option<SecretKey>,
|
||||
previous_block_root: Option<Hash256>,
|
||||
spec: &ChainSpec,
|
||||
) -> (BeaconBlock<T>, BeaconState<T>) {
|
||||
let (state, keypairs) = self.state_builder.build();
|
||||
let builder = &mut self.block_builder;
|
||||
|
||||
builder.set_slot(state.slot);
|
||||
|
||||
match previous_block_root {
|
||||
Some(root) => builder.set_parent_root(root),
|
||||
None => builder.set_parent_root(Hash256::from_slice(
|
||||
&state.latest_block_header.signed_root(),
|
||||
)),
|
||||
}
|
||||
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
}
|
||||
|
||||
for i in 0..num_proposer_slashings {
|
||||
let validator_indices = i;
|
||||
let secret_keys = &keypairs[i as usize].sk;
|
||||
self.block_builder.insert_proposer_slashing(
|
||||
test_task,
|
||||
validator_indices,
|
||||
&secret_keys,
|
||||
&state.fork,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
|
||||
pub fn build(
|
||||
mut self,
|
||||
randao_sk: Option<SecretKey>,
|
||||
|
||||
@@ -181,8 +181,6 @@ pub enum ProposerSlashingInvalid {
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttesterSlashingInvalid {
|
||||
/// The attestation data is identical, an attestation cannot conflict with itself.
|
||||
AttestationDataIdentical,
|
||||
/// The attestations were not in conflict.
|
||||
NotSlashable,
|
||||
/// The first `IndexedAttestation` was invalid.
|
||||
@@ -222,23 +220,14 @@ pub enum AttestationInvalid {
|
||||
attestation: Checkpoint,
|
||||
is_current: bool,
|
||||
},
|
||||
/// The custody bitfield has some bits set `true`. This is not allowed in phase 0.
|
||||
CustodyBitfieldHasSetBits,
|
||||
/// There are no set bits on the attestation -- an attestation must be signed by at least one
|
||||
/// validator.
|
||||
AggregationBitfieldIsEmpty,
|
||||
/// The custody bitfield length is not the smallest possible size to represent the committee.
|
||||
BadCustodyBitfieldLength {
|
||||
committee_len: usize,
|
||||
bitfield_len: usize,
|
||||
},
|
||||
/// The aggregation bitfield length is not the smallest possible size to represent the committee.
|
||||
BadAggregationBitfieldLength {
|
||||
committee_len: usize,
|
||||
bitfield_len: usize,
|
||||
},
|
||||
/// The bits set in the custody bitfield are not a subset of those set in the aggregation bits.
|
||||
CustodyBitfieldNotSubset,
|
||||
/// The validator index was unknown.
|
||||
UnknownValidator(u64),
|
||||
/// The attestation signature verification failed.
|
||||
@@ -264,14 +253,6 @@ impl From<BlockOperationError<IndexedAttestationInvalid>>
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum IndexedAttestationInvalid {
|
||||
/// The custody bit 0 validators intersect with the bit 1 validators.
|
||||
CustodyBitValidatorsIntersect,
|
||||
/// The custody bitfield has some bits set `true`. This is not allowed in phase 0.
|
||||
CustodyBitfieldHasSetBits,
|
||||
/// The custody bitfield violated a type-level bound.
|
||||
CustodyBitfieldBoundsError(ssz_types::Error),
|
||||
/// No validator indices were specified.
|
||||
NoValidatorIndices,
|
||||
/// The number of indices exceeds the global maximum.
|
||||
///
|
||||
/// (max_indices, indices_given)
|
||||
@@ -291,8 +272,6 @@ pub enum IndexedAttestationInvalid {
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DepositInvalid {
|
||||
/// The deposit index does not match the state index.
|
||||
BadIndex { state: u64, deposit: u64 },
|
||||
/// The signature (proof-of-possession) does not match the given pubkey.
|
||||
BadSignature,
|
||||
/// The signature or pubkey does not represent a valid BLS point.
|
||||
@@ -311,7 +290,7 @@ pub enum ExitInvalid {
|
||||
/// The specified validator has a non-maximum exit epoch.
|
||||
AlreadyExited(u64),
|
||||
/// The specified validator has already initiated exit.
|
||||
AlreadyInitiatedExited(u64),
|
||||
AlreadyInitiatedExit(u64),
|
||||
/// The exit is for a future epoch.
|
||||
FutureEpoch { state: Epoch, exit: Epoch },
|
||||
/// The validator has not been active for long enough.
|
||||
|
||||
@@ -29,7 +29,8 @@ pub fn is_valid_indexed_attestation<T: EthSpec>(
|
||||
// Check that indices are sorted
|
||||
let check_sorted = |list: &[u64]| -> Result<()> {
|
||||
list.windows(2).enumerate().try_for_each(|(i, pair)| {
|
||||
if pair[0] >= pair[1] {
|
||||
// The spec allows duplicates, so use strict comparison (>).
|
||||
if pair[0] > pair[1] {
|
||||
Err(error(Invalid::BadValidatorIndicesOrdering(i)))
|
||||
} else {
|
||||
Ok(())
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::convert::TryInto;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::{
|
||||
AggregateSignature, AttesterSlashing, BeaconBlock, BeaconBlockHeader, BeaconState,
|
||||
BeaconStateError, ChainSpec, Deposit, Domain, EthSpec, Fork, Hash256, IndexedAttestation,
|
||||
BeaconStateError, ChainSpec, DepositData, Domain, EthSpec, Hash256, IndexedAttestation,
|
||||
ProposerSlashing, PublicKey, Signature, VoluntaryExit,
|
||||
};
|
||||
|
||||
@@ -180,18 +180,17 @@ pub fn attester_slashing_signature_sets<'a, T: EthSpec>(
|
||||
///
|
||||
/// This method is separate to `deposit_signature_set` to satisfy lifetime requirements.
|
||||
pub fn deposit_pubkey_signature_message(
|
||||
deposit: &Deposit,
|
||||
deposit_data: &DepositData,
|
||||
) -> Option<(PublicKey, Signature, Vec<u8>)> {
|
||||
let pubkey = (&deposit.data.pubkey).try_into().ok()?;
|
||||
let signature = (&deposit.data.signature).try_into().ok()?;
|
||||
let message = deposit.data.signed_root();
|
||||
let pubkey = (&deposit_data.pubkey).try_into().ok()?;
|
||||
let signature = (&deposit_data.signature).try_into().ok()?;
|
||||
let message = deposit_data.signed_root();
|
||||
Some((pubkey, signature, message))
|
||||
}
|
||||
|
||||
/// Returns the signature set for some set of deposit signatures, made with
|
||||
/// `deposit_pubkey_signature_message`.
|
||||
pub fn deposit_signature_set<'a, T: EthSpec>(
|
||||
state: &'a BeaconState<T>,
|
||||
pub fn deposit_signature_set<'a>(
|
||||
pubkey_signature_message: &'a (PublicKey, Signature, Vec<u8>),
|
||||
spec: &'a ChainSpec,
|
||||
) -> SignatureSet<'a> {
|
||||
@@ -199,9 +198,12 @@ pub fn deposit_signature_set<'a, T: EthSpec>(
|
||||
|
||||
// Note: Deposits are valid across forks, thus the deposit domain is computed
|
||||
// with the fork zeroed.
|
||||
let domain = spec.get_domain(state.current_epoch(), Domain::Deposit, &Fork::default());
|
||||
|
||||
SignatureSet::single(signature, pubkey, message.clone(), domain)
|
||||
SignatureSet::single(
|
||||
signature,
|
||||
pubkey,
|
||||
message.clone(),
|
||||
spec.get_deposit_domain(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a signature set that is valid if the `VoluntaryExit` was signed by the indicated
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,7 @@ pub fn verify_attestation_for_state<T: EthSpec>(
|
||||
spec: &ChainSpec,
|
||||
) -> Result<()> {
|
||||
let data = &attestation.data;
|
||||
|
||||
verify!(
|
||||
data.index < state.get_committee_count_at_slot(data.slot)?,
|
||||
Invalid::BadCommitteeIndex
|
||||
|
||||
@@ -15,16 +15,12 @@ fn error(reason: DepositInvalid) -> BlockOperationError<DepositInvalid> {
|
||||
/// Verify `Deposit.pubkey` signed `Deposit.signature`.
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
pub fn verify_deposit_signature<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
deposit: &Deposit,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<()> {
|
||||
let deposit_signature_message = deposit_pubkey_signature_message(deposit)
|
||||
pub fn verify_deposit_signature(deposit_data: &DepositData, spec: &ChainSpec) -> Result<()> {
|
||||
let deposit_signature_message = deposit_pubkey_signature_message(&deposit_data)
|
||||
.ok_or_else(|| error(DepositInvalid::BadBlsBytes))?;
|
||||
|
||||
verify!(
|
||||
deposit_signature_set(state, &deposit_signature_message, spec).is_valid(),
|
||||
deposit_signature_set(&deposit_signature_message, spec).is_valid(),
|
||||
DepositInvalid::BadSignature
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use log::info;
|
||||
use types::test_utils::{TestingBeaconBlockBuilder, TestingBeaconStateBuilder};
|
||||
use types::test_utils::{
|
||||
AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask,
|
||||
ProposerSlashingTestTask, TestingBeaconBlockBuilder, TestingBeaconStateBuilder,
|
||||
};
|
||||
use types::{EthSpec, *};
|
||||
|
||||
pub struct BlockBuilder<T: EthSpec> {
|
||||
@@ -72,6 +75,7 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
let validator_index = validators_iter.next().expect("Insufficient validators.");
|
||||
|
||||
builder.insert_proposer_slashing(
|
||||
ProposerSlashingTestTask::Valid,
|
||||
validator_index,
|
||||
&keypairs[validator_index as usize].sk,
|
||||
&state.fork,
|
||||
@@ -97,7 +101,13 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
secret_keys.push(&keypairs[validator_index as usize].sk);
|
||||
}
|
||||
|
||||
builder.insert_attester_slashing(&attesters, &secret_keys, &state.fork, spec);
|
||||
builder.insert_attester_slashing(
|
||||
AttesterSlashingTestTask::Valid,
|
||||
&attesters,
|
||||
&secret_keys,
|
||||
&state.fork,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
info!(
|
||||
"Inserted {} attester slashings.",
|
||||
@@ -108,6 +118,7 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
let all_secret_keys: Vec<&SecretKey> = keypairs.iter().map(|keypair| &keypair.sk).collect();
|
||||
builder
|
||||
.insert_attestations(
|
||||
AttestationTestTask::Valid,
|
||||
&state,
|
||||
&all_secret_keys,
|
||||
self.num_attestations as usize,
|
||||
@@ -120,15 +131,14 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
);
|
||||
|
||||
// Insert `Deposit` objects.
|
||||
for i in 0..self.num_deposits {
|
||||
builder.insert_deposit(
|
||||
32_000_000_000,
|
||||
state.eth1_data.deposit_count + (i as u64),
|
||||
&state,
|
||||
spec,
|
||||
);
|
||||
}
|
||||
state.eth1_data.deposit_count += self.num_deposits as u64;
|
||||
builder.insert_deposits(
|
||||
32_000_000_000,
|
||||
DepositTestTask::NoReset,
|
||||
state.eth1_data.deposit_count,
|
||||
self.num_deposits as u64,
|
||||
&mut state,
|
||||
spec,
|
||||
);
|
||||
info!("Inserted {} deposits.", builder.block.body.deposits.len());
|
||||
|
||||
// Insert the maximum possible number of `Exit` objects.
|
||||
@@ -136,7 +146,8 @@ impl<T: EthSpec> BlockBuilder<T> {
|
||||
let validator_index = validators_iter.next().expect("Insufficient validators.");
|
||||
|
||||
builder.insert_exit(
|
||||
&state,
|
||||
ExitTestTask::Valid,
|
||||
&mut state,
|
||||
validator_index,
|
||||
&keypairs[validator_index as usize].sk,
|
||||
spec,
|
||||
|
||||
@@ -16,6 +16,7 @@ eth2_hashing = "0.1.0"
|
||||
hex = "0.3"
|
||||
int_to_bytes = { path = "../utils/int_to_bytes" }
|
||||
log = "0.4.8"
|
||||
merkle_proof = { path = "../utils/merkle_proof" }
|
||||
rayon = "1.2.0"
|
||||
rand = "0.7.2"
|
||||
serde = "1.0.102"
|
||||
|
||||
@@ -205,7 +205,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
// Versioning
|
||||
genesis_time,
|
||||
slot: spec.genesis_slot,
|
||||
fork: Fork::genesis(T::genesis_epoch()),
|
||||
fork: spec.genesis_fork.clone(),
|
||||
|
||||
// History
|
||||
latest_block_header: BeaconBlock::<T>::empty(spec).temporary_block_header(),
|
||||
|
||||
@@ -89,8 +89,15 @@ pub struct ChainSpec {
|
||||
*/
|
||||
pub safe_slots_to_update_justified: u64,
|
||||
|
||||
/*
|
||||
* Eth1
|
||||
*/
|
||||
pub eth1_follow_distance: u64,
|
||||
|
||||
pub boot_nodes: Vec<String>,
|
||||
pub network_id: u8,
|
||||
|
||||
pub genesis_fork: Fork,
|
||||
}
|
||||
|
||||
impl ChainSpec {
|
||||
@@ -122,6 +129,22 @@ impl ChainSpec {
|
||||
u64::from_le_bytes(fork_and_domain)
|
||||
}
|
||||
|
||||
/// Get the domain for a deposit signature.
|
||||
///
|
||||
/// Deposits are valid across forks, thus the deposit domain is computed
|
||||
/// with the fork zeroed.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
pub fn get_deposit_domain(&self) -> u64 {
|
||||
let mut bytes: Vec<u8> = int_to_bytes4(self.domain_deposit);
|
||||
bytes.append(&mut vec![0; 4]);
|
||||
|
||||
let mut fork_and_domain = [0; 8];
|
||||
fork_and_domain.copy_from_slice(&bytes);
|
||||
|
||||
u64::from_le_bytes(fork_and_domain)
|
||||
}
|
||||
|
||||
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
@@ -194,6 +217,20 @@ impl ChainSpec {
|
||||
*/
|
||||
safe_slots_to_update_justified: 8,
|
||||
|
||||
/*
|
||||
* Eth1
|
||||
*/
|
||||
eth1_follow_distance: 1_024,
|
||||
|
||||
/*
|
||||
* Fork
|
||||
*/
|
||||
genesis_fork: Fork {
|
||||
previous_version: [0; 4],
|
||||
current_version: [0; 4],
|
||||
epoch: Epoch::new(0),
|
||||
},
|
||||
|
||||
/*
|
||||
* Network specific
|
||||
*/
|
||||
@@ -217,6 +254,7 @@ impl ChainSpec {
|
||||
min_genesis_active_validator_count: 64,
|
||||
network_id: 2, // lighthouse testnet network id
|
||||
boot_nodes,
|
||||
eth1_follow_distance: 16,
|
||||
..ChainSpec::mainnet()
|
||||
}
|
||||
}
|
||||
@@ -255,7 +293,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn test_domain(domain_type: Domain, raw_domain: u32, spec: &ChainSpec) {
|
||||
let fork = Fork::genesis(Epoch::new(0));
|
||||
let fork = &spec.genesis_fork;
|
||||
let epoch = Epoch::new(0);
|
||||
|
||||
let domain = spec.get_domain(epoch, domain_type, &fork);
|
||||
|
||||
@@ -7,6 +7,8 @@ use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
pub const DEPOSIT_TREE_DEPTH: usize = 32;
|
||||
|
||||
/// A deposit to potentially become a beacon chain validator.
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
|
||||
@@ -36,15 +36,9 @@ impl DepositData {
|
||||
/// Generate the signature for a given DepositData details.
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
pub fn create_signature(
|
||||
&self,
|
||||
secret_key: &SecretKey,
|
||||
epoch: Epoch,
|
||||
fork: &Fork,
|
||||
spec: &ChainSpec,
|
||||
) -> SignatureBytes {
|
||||
pub fn create_signature(&self, secret_key: &SecretKey, spec: &ChainSpec) -> SignatureBytes {
|
||||
let msg = self.signed_root();
|
||||
let domain = spec.get_domain(epoch, Domain::Deposit, fork);
|
||||
let domain = spec.get_deposit_domain();
|
||||
|
||||
SignatureBytes::from(Signature::new(msg.as_slice(), domain, secret_key))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,18 @@ use tree_hash_derive::TreeHash;
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
#[derive(
|
||||
Debug, PartialEq, Clone, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Clone,
|
||||
Default,
|
||||
Eq,
|
||||
Hash,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Encode,
|
||||
Decode,
|
||||
TreeHash,
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct Eth1Data {
|
||||
pub deposit_root: Hash256,
|
||||
|
||||
@@ -28,17 +28,6 @@ pub struct Fork {
|
||||
}
|
||||
|
||||
impl Fork {
|
||||
/// Initialize the `Fork` from the genesis parameters in the `spec`.
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
pub fn genesis(genesis_epoch: Epoch) -> Self {
|
||||
Self {
|
||||
previous_version: [0; 4],
|
||||
current_version: [0; 4],
|
||||
epoch: genesis_epoch,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the fork version of the given ``epoch``.
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
@@ -56,24 +45,6 @@ mod tests {
|
||||
|
||||
ssz_tests!(Fork);
|
||||
|
||||
fn test_genesis(epoch: Epoch) {
|
||||
let fork = Fork::genesis(epoch);
|
||||
|
||||
assert_eq!(fork.epoch, epoch, "epoch incorrect");
|
||||
assert_eq!(
|
||||
fork.previous_version, fork.current_version,
|
||||
"previous and current are not identical"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis() {
|
||||
test_genesis(Epoch::new(0));
|
||||
test_genesis(Epoch::new(11));
|
||||
test_genesis(Epoch::new(2_u64.pow(63)));
|
||||
test_genesis(Epoch::max_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_fork_version() {
|
||||
let previous_version = [1; 4];
|
||||
|
||||
@@ -50,7 +50,7 @@ pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
|
||||
pub use crate::beacon_state::{Error as BeaconStateError, *};
|
||||
pub use crate::chain_spec::{ChainSpec, Domain};
|
||||
pub use crate::checkpoint::Checkpoint;
|
||||
pub use crate::deposit::Deposit;
|
||||
pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH};
|
||||
pub use crate::deposit_data::DepositData;
|
||||
pub use crate::eth1_data::Eth1Data;
|
||||
pub use crate::fork::Fork;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::test_utils::TestingAttestationDataBuilder;
|
||||
use crate::test_utils::{AttestationTestTask, TestingAttestationDataBuilder};
|
||||
use crate::*;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
@@ -12,12 +12,26 @@ pub struct TestingAttestationBuilder<T: EthSpec> {
|
||||
|
||||
impl<T: EthSpec> TestingAttestationBuilder<T> {
|
||||
/// Create a new attestation builder.
|
||||
pub fn new(state: &BeaconState<T>, committee: &[usize], slot: Slot, index: u64) -> Self {
|
||||
let data_builder = TestingAttestationDataBuilder::new(state, index, slot);
|
||||
pub fn new(
|
||||
test_task: AttestationTestTask,
|
||||
state: &BeaconState<T>,
|
||||
committee: &[usize],
|
||||
slot: Slot,
|
||||
index: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let data_builder = TestingAttestationDataBuilder::new(test_task, state, index, slot, spec);
|
||||
|
||||
let mut aggregation_bits = BitList::with_capacity(committee.len()).unwrap();
|
||||
let mut aggregation_bits_len = committee.len();
|
||||
|
||||
for (i, _) in committee.iter().enumerate() {
|
||||
match test_task {
|
||||
AttestationTestTask::BadAggregationBitfieldLen => aggregation_bits_len += 1,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let mut aggregation_bits = BitList::with_capacity(aggregation_bits_len).unwrap();
|
||||
|
||||
for i in 0..committee.len() {
|
||||
aggregation_bits.set(i, false).unwrap();
|
||||
}
|
||||
|
||||
@@ -39,6 +53,7 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
|
||||
/// keypair must be that of the first signing validator.
|
||||
pub fn sign(
|
||||
&mut self,
|
||||
test_task: AttestationTestTask,
|
||||
signing_validators: &[usize],
|
||||
secret_keys: &[&SecretKey],
|
||||
fork: &Fork,
|
||||
@@ -57,10 +72,15 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
|
||||
.position(|v| *v == *validator_index)
|
||||
.expect("Signing validator not in attestation committee");
|
||||
|
||||
self.attestation
|
||||
.aggregation_bits
|
||||
.set(committee_index, true)
|
||||
.unwrap();
|
||||
match test_task {
|
||||
AttestationTestTask::BadIndexedAttestationBadSignature => (),
|
||||
_ => {
|
||||
self.attestation
|
||||
.aggregation_bits
|
||||
.set(committee_index, true)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let message = self.attestation.data.tree_hash_root();
|
||||
|
||||
@@ -70,7 +90,12 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
|
||||
fork,
|
||||
);
|
||||
|
||||
let signature = Signature::new(&message, domain, secret_keys[key_index]);
|
||||
let index = if test_task == AttestationTestTask::BadSignature {
|
||||
0
|
||||
} else {
|
||||
key_index
|
||||
};
|
||||
let signature = Signature::new(&message, domain, secret_keys[index]);
|
||||
self.attestation.signature.add(&signature)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::test_utils::AttestationTestTask;
|
||||
use crate::*;
|
||||
|
||||
/// Builds an `AttestationData` to be used for testing purposes.
|
||||
@@ -10,19 +11,25 @@ pub struct TestingAttestationDataBuilder {
|
||||
impl TestingAttestationDataBuilder {
|
||||
/// Configures a new `AttestationData` which attests to all of the same parameters as the
|
||||
/// state.
|
||||
pub fn new<T: EthSpec>(state: &BeaconState<T>, index: u64, slot: Slot) -> Self {
|
||||
pub fn new<T: EthSpec>(
|
||||
test_task: AttestationTestTask,
|
||||
state: &BeaconState<T>,
|
||||
mut index: u64,
|
||||
mut slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let current_epoch = state.current_epoch();
|
||||
let previous_epoch = state.previous_epoch();
|
||||
|
||||
let is_previous_epoch = slot.epoch(T::slots_per_epoch()) != current_epoch;
|
||||
|
||||
let source = if is_previous_epoch {
|
||||
let mut source = if is_previous_epoch {
|
||||
state.previous_justified_checkpoint.clone()
|
||||
} else {
|
||||
state.current_justified_checkpoint.clone()
|
||||
};
|
||||
|
||||
let target = if is_previous_epoch {
|
||||
let mut target = if is_previous_epoch {
|
||||
Checkpoint {
|
||||
epoch: previous_epoch,
|
||||
root: *state
|
||||
@@ -38,12 +45,49 @@ impl TestingAttestationDataBuilder {
|
||||
}
|
||||
};
|
||||
|
||||
let beacon_block_root = *state.get_block_root(slot).unwrap();
|
||||
|
||||
match test_task {
|
||||
// FIXME(sproul)
|
||||
AttestationTestTask::NoCommiteeForShard => index += 2,
|
||||
// AttestationTestTask::BadShard => index = T::ShardCount::to_u64(),
|
||||
AttestationTestTask::IncludedTooEarly => {
|
||||
slot = state.slot - spec.min_attestation_inclusion_delay + 1
|
||||
}
|
||||
AttestationTestTask::IncludedTooLate => slot -= T::SlotsPerEpoch::to_u64(),
|
||||
AttestationTestTask::BadTargetEpoch => {
|
||||
target = Checkpoint {
|
||||
epoch: Epoch::from(5 as u64),
|
||||
root: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
AttestationTestTask::WrongJustifiedCheckpoint => {
|
||||
source = Checkpoint {
|
||||
epoch: Epoch::from(0 as u64),
|
||||
root: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
AttestationTestTask::BadTargetTooLow => {
|
||||
target = Checkpoint {
|
||||
epoch: Epoch::from(0 as u64),
|
||||
root: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
AttestationTestTask::BadTargetTooHigh => {
|
||||
target = Checkpoint {
|
||||
epoch: Epoch::from(10 as u64),
|
||||
root: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let data = AttestationData {
|
||||
slot,
|
||||
index,
|
||||
|
||||
// LMD GHOST vote
|
||||
beacon_block_root: *state.get_block_root(slot).unwrap(),
|
||||
beacon_block_root,
|
||||
|
||||
// FFG Vote
|
||||
source,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::test_utils::AttesterSlashingTestTask;
|
||||
use crate::*;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
@@ -17,7 +18,11 @@ impl TestingAttesterSlashingBuilder {
|
||||
/// - `domain: Domain`
|
||||
///
|
||||
/// Where domain is a domain "constant" (e.g., `spec.domain_attestation`).
|
||||
pub fn double_vote<F, T: EthSpec>(validator_indices: &[u64], signer: F) -> AttesterSlashing<T>
|
||||
pub fn double_vote<F, T: EthSpec>(
|
||||
test_task: AttesterSlashingTestTask,
|
||||
validator_indices: &[u64],
|
||||
signer: F,
|
||||
) -> AttesterSlashing<T>
|
||||
where
|
||||
F: Fn(u64, &[u8], Epoch, Domain) -> Signature,
|
||||
{
|
||||
@@ -44,19 +49,35 @@ impl TestingAttesterSlashingBuilder {
|
||||
target: checkpoint_1,
|
||||
};
|
||||
|
||||
let data_2 = AttestationData {
|
||||
target: checkpoint_2,
|
||||
..data_1.clone()
|
||||
let data_2 = if test_task == AttesterSlashingTestTask::NotSlashable {
|
||||
AttestationData { ..data_1.clone() }
|
||||
} else {
|
||||
AttestationData {
|
||||
target: checkpoint_2,
|
||||
..data_1.clone()
|
||||
}
|
||||
};
|
||||
|
||||
let mut attestation_1 = IndexedAttestation {
|
||||
attesting_indices: validator_indices.to_vec().into(),
|
||||
attesting_indices: if test_task == AttesterSlashingTestTask::IndexedAttestation1Invalid
|
||||
{
|
||||
// Trigger bad validator indices ordering error.
|
||||
vec![1, 0].into()
|
||||
} else {
|
||||
validator_indices.to_vec().into()
|
||||
},
|
||||
data: data_1,
|
||||
signature: AggregateSignature::new(),
|
||||
};
|
||||
|
||||
let mut attestation_2 = IndexedAttestation {
|
||||
attesting_indices: validator_indices.to_vec().into(),
|
||||
attesting_indices: if test_task == AttesterSlashingTestTask::IndexedAttestation2Invalid
|
||||
{
|
||||
// Trigger bad validator indices ordering error.
|
||||
vec![1, 0].into()
|
||||
} else {
|
||||
validator_indices.to_vec().into()
|
||||
},
|
||||
data: data_2,
|
||||
signature: AggregateSignature::new(),
|
||||
};
|
||||
|
||||
@@ -3,8 +3,11 @@ use crate::{
|
||||
TestingAttestationBuilder, TestingAttesterSlashingBuilder, TestingDepositBuilder,
|
||||
TestingProposerSlashingBuilder, TestingVoluntaryExitBuilder,
|
||||
},
|
||||
typenum::U4294967296,
|
||||
*,
|
||||
};
|
||||
use int_to_bytes::int_to_bytes32;
|
||||
use merkle_proof::MerkleTree;
|
||||
use rayon::prelude::*;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
|
||||
@@ -15,6 +18,67 @@ pub struct TestingBeaconBlockBuilder<T: EthSpec> {
|
||||
pub block: BeaconBlock<T>,
|
||||
}
|
||||
|
||||
/// Enum used for passing test options to builder
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum DepositTestTask {
|
||||
Valid,
|
||||
BadPubKey,
|
||||
BadSig,
|
||||
InvalidPubKey,
|
||||
NoReset,
|
||||
}
|
||||
|
||||
/// Enum used for passing test options to builder
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum ExitTestTask {
|
||||
AlreadyInitiated,
|
||||
AlreadyExited,
|
||||
BadSignature,
|
||||
FutureEpoch,
|
||||
NotActive,
|
||||
Valid,
|
||||
ValidatorUnknown,
|
||||
}
|
||||
|
||||
/// Enum used for passing test options to builder
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum AttestationTestTask {
|
||||
Valid,
|
||||
NoCommiteeForShard,
|
||||
WrongJustifiedCheckpoint,
|
||||
BadTargetTooLow,
|
||||
BadTargetTooHigh,
|
||||
BadShard,
|
||||
BadIndexedAttestationBadSignature,
|
||||
BadAggregationBitfieldLen,
|
||||
BadSignature,
|
||||
ValidatorUnknown,
|
||||
IncludedTooEarly,
|
||||
IncludedTooLate,
|
||||
BadTargetEpoch,
|
||||
}
|
||||
|
||||
/// Enum used for passing test options to builder
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum AttesterSlashingTestTask {
|
||||
Valid,
|
||||
NotSlashable,
|
||||
IndexedAttestation1Invalid,
|
||||
IndexedAttestation2Invalid,
|
||||
}
|
||||
|
||||
/// Enum used for passing test options to builder
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum ProposerSlashingTestTask {
|
||||
Valid,
|
||||
ProposerUnknown,
|
||||
ProposalEpochMismatch,
|
||||
ProposalsIdentical,
|
||||
ProposerNotSlashable,
|
||||
BadProposal1Signature,
|
||||
BadProposal2Signature,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
/// Create a new builder from genesis.
|
||||
pub fn new(spec: &ChainSpec) -> Self {
|
||||
@@ -61,13 +125,14 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
/// Inserts a signed, valid `ProposerSlashing` for the validator.
|
||||
pub fn insert_proposer_slashing(
|
||||
&mut self,
|
||||
test_task: ProposerSlashingTestTask,
|
||||
validator_index: u64,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let proposer_slashing =
|
||||
build_proposer_slashing::<T>(validator_index, secret_key, fork, spec);
|
||||
build_proposer_slashing::<T>(test_task, validator_index, secret_key, fork, spec);
|
||||
self.block
|
||||
.body
|
||||
.proposer_slashings
|
||||
@@ -78,18 +143,20 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
/// Inserts a signed, valid `AttesterSlashing` for each validator index in `validator_indices`.
|
||||
pub fn insert_attester_slashing(
|
||||
&mut self,
|
||||
test_task: AttesterSlashingTestTask,
|
||||
validator_indices: &[u64],
|
||||
secret_keys: &[&SecretKey],
|
||||
fork: &Fork,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let attester_slashing =
|
||||
build_double_vote_attester_slashing(validator_indices, secret_keys, fork, spec);
|
||||
self.block
|
||||
.body
|
||||
.attester_slashings
|
||||
.push(attester_slashing)
|
||||
.unwrap();
|
||||
let attester_slashing = build_double_vote_attester_slashing(
|
||||
test_task,
|
||||
validator_indices,
|
||||
secret_keys,
|
||||
fork,
|
||||
spec,
|
||||
);
|
||||
let _ = self.block.body.attester_slashings.push(attester_slashing);
|
||||
}
|
||||
|
||||
/// Fills the block with `num_attestations` attestations.
|
||||
@@ -103,6 +170,7 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
/// to aggregate these split attestations.
|
||||
pub fn insert_attestations(
|
||||
&mut self,
|
||||
test_task: AttestationTestTask,
|
||||
state: &BeaconState<T>,
|
||||
secret_keys: &[&SecretKey],
|
||||
num_attestations: usize,
|
||||
@@ -175,13 +243,21 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
let attestations: Vec<_> = committees
|
||||
.par_iter()
|
||||
.map(|(slot, committee, signing_validators, index)| {
|
||||
let mut builder = TestingAttestationBuilder::new(state, committee, *slot, *index);
|
||||
let mut builder = TestingAttestationBuilder::new(
|
||||
test_task, state, committee, *slot, *index, spec,
|
||||
);
|
||||
|
||||
let signing_secret_keys: Vec<&SecretKey> = signing_validators
|
||||
.iter()
|
||||
.map(|validator_index| secret_keys[*validator_index])
|
||||
.collect();
|
||||
builder.sign(signing_validators, &signing_secret_keys, &state.fork, spec);
|
||||
builder.sign(
|
||||
test_task,
|
||||
signing_validators,
|
||||
&signing_secret_keys,
|
||||
&state.fork,
|
||||
spec,
|
||||
);
|
||||
|
||||
builder.build()
|
||||
})
|
||||
@@ -195,47 +271,108 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
}
|
||||
|
||||
/// Insert a `Valid` deposit into the state.
|
||||
pub fn insert_deposit(
|
||||
pub fn insert_deposits(
|
||||
&mut self,
|
||||
amount: u64,
|
||||
test_task: DepositTestTask,
|
||||
// TODO: deal with the fact deposits no longer have explicit indices
|
||||
_index: u64,
|
||||
state: &BeaconState<T>,
|
||||
num_deposits: u64,
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let keypair = Keypair::random();
|
||||
// Vector containing deposits' data
|
||||
let mut datas = vec![];
|
||||
for _ in 0..num_deposits {
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let mut builder = TestingDepositBuilder::new(keypair.pk.clone(), amount);
|
||||
builder.sign(
|
||||
&keypair,
|
||||
state.slot.epoch(T::slots_per_epoch()),
|
||||
&state.fork,
|
||||
spec,
|
||||
);
|
||||
let mut builder = TestingDepositBuilder::new(keypair.pk.clone(), amount);
|
||||
builder.sign(test_task, &keypair, spec);
|
||||
datas.push(builder.build().data);
|
||||
}
|
||||
|
||||
self.block.body.deposits.push(builder.build()).unwrap()
|
||||
// Vector containing all leaves
|
||||
let leaves = datas
|
||||
.iter()
|
||||
.map(|data| Hash256::from_slice(&data.tree_hash_root()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Building a VarList from leaves
|
||||
let deposit_data_list = VariableList::<_, U4294967296>::from(leaves.clone());
|
||||
|
||||
// Setting the deposit_root to be the tree_hash_root of the VarList
|
||||
state.eth1_data.deposit_root = Hash256::from_slice(&deposit_data_list.tree_hash_root());
|
||||
|
||||
// Building the merkle tree used for generating proofs
|
||||
let tree = MerkleTree::create(&leaves[..], spec.deposit_contract_tree_depth as usize);
|
||||
|
||||
// Building proofs
|
||||
let mut proofs = vec![];
|
||||
for i in 0..leaves.len() {
|
||||
let (_, mut proof) = tree.generate_proof(i, spec.deposit_contract_tree_depth as usize);
|
||||
proof.push(Hash256::from_slice(&int_to_bytes32(leaves.len() as u64)));
|
||||
proofs.push(proof);
|
||||
}
|
||||
|
||||
// Building deposits
|
||||
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<_>>();
|
||||
|
||||
// Pushing deposits to block body
|
||||
for deposit in deposits {
|
||||
let _ = self.block.body.deposits.push(deposit);
|
||||
}
|
||||
|
||||
// Manually setting the deposit_count to process deposits
|
||||
// This is for test purposes only
|
||||
if test_task == DepositTestTask::NoReset {
|
||||
state.eth1_data.deposit_count += num_deposits;
|
||||
} else {
|
||||
state.eth1_deposit_index = 0;
|
||||
state.eth1_data.deposit_count = num_deposits;
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a `Valid` exit into the state.
|
||||
pub fn insert_exit(
|
||||
&mut self,
|
||||
state: &BeaconState<T>,
|
||||
validator_index: u64,
|
||||
test_task: ExitTestTask,
|
||||
state: &mut BeaconState<T>,
|
||||
mut validator_index: u64,
|
||||
secret_key: &SecretKey,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let mut builder = TestingVoluntaryExitBuilder::new(
|
||||
state.slot.epoch(T::slots_per_epoch()),
|
||||
validator_index,
|
||||
);
|
||||
let sk = &mut secret_key.clone();
|
||||
let mut exit_epoch = state.slot.epoch(T::slots_per_epoch());
|
||||
|
||||
builder.sign(secret_key, &state.fork, spec);
|
||||
match test_task {
|
||||
ExitTestTask::BadSignature => *sk = SecretKey::random(),
|
||||
ExitTestTask::ValidatorUnknown => validator_index = 4242,
|
||||
ExitTestTask::AlreadyExited => {
|
||||
state.validators[validator_index as usize].exit_epoch = Epoch::from(314_159 as u64)
|
||||
}
|
||||
// FIXME: disabled in v0.9
|
||||
ExitTestTask::NotActive => {
|
||||
state.validators[validator_index as usize].activation_epoch =
|
||||
Epoch::from(314_159 as u64)
|
||||
}
|
||||
ExitTestTask::FutureEpoch => exit_epoch = spec.far_future_epoch,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let mut builder = TestingVoluntaryExitBuilder::new(exit_epoch, validator_index);
|
||||
|
||||
builder.sign(sk, &state.fork, spec);
|
||||
|
||||
self.block
|
||||
.body
|
||||
.voluntary_exits
|
||||
.push(builder.build())
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Signs and returns the block, consuming the builder.
|
||||
@@ -254,6 +391,7 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
|
||||
///
|
||||
/// Signs the message using a `BeaconChainHarness`.
|
||||
fn build_proposer_slashing<T: EthSpec>(
|
||||
test_task: ProposerSlashingTestTask,
|
||||
validator_index: u64,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
@@ -264,13 +402,14 @@ fn build_proposer_slashing<T: EthSpec>(
|
||||
Signature::new(message, domain, secret_key)
|
||||
};
|
||||
|
||||
TestingProposerSlashingBuilder::double_vote::<T, _>(validator_index, signer)
|
||||
TestingProposerSlashingBuilder::double_vote::<T, _>(test_task, validator_index, signer)
|
||||
}
|
||||
|
||||
/// Builds an `AttesterSlashing` for some `validator_indices`.
|
||||
///
|
||||
/// Signs the message using a `BeaconChainHarness`.
|
||||
fn build_double_vote_attester_slashing<T: EthSpec>(
|
||||
test_task: AttesterSlashingTestTask,
|
||||
validator_indices: &[u64],
|
||||
secret_keys: &[&SecretKey],
|
||||
fork: &Fork,
|
||||
@@ -285,5 +424,5 @@ fn build_double_vote_attester_slashing<T: EthSpec>(
|
||||
Signature::new(message, domain, secret_keys[key_index])
|
||||
};
|
||||
|
||||
TestingAttesterSlashingBuilder::double_vote(validator_indices, signer)
|
||||
TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::super::{generate_deterministic_keypairs, KeypairsFile};
|
||||
use crate::test_utils::TestingPendingAttestationBuilder;
|
||||
use crate::test_utils::{AttestationTestTask, TestingPendingAttestationBuilder};
|
||||
use crate::*;
|
||||
use bls::get_withdrawal_credentials;
|
||||
use dirs;
|
||||
@@ -222,6 +222,7 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
|
||||
for beacon_committee in committees {
|
||||
let mut builder = TestingPendingAttestationBuilder::new(
|
||||
AttestationTestTask::Valid,
|
||||
state,
|
||||
beacon_committee.index,
|
||||
slot,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::test_utils::DepositTestTask;
|
||||
use crate::*;
|
||||
use bls::{get_withdrawal_credentials, PublicKeyBytes, SignatureBytes};
|
||||
|
||||
@@ -29,18 +30,31 @@ impl TestingDepositBuilder {
|
||||
/// - `pubkey` to the signing pubkey.
|
||||
/// - `withdrawal_credentials` to the signing pubkey.
|
||||
/// - `proof_of_possession`
|
||||
pub fn sign(&mut self, keypair: &Keypair, epoch: Epoch, fork: &Fork, spec: &ChainSpec) {
|
||||
pub fn sign(&mut self, test_task: DepositTestTask, keypair: &Keypair, spec: &ChainSpec) {
|
||||
let new_key = Keypair::random();
|
||||
let mut pubkeybytes = PublicKeyBytes::from(keypair.pk.clone());
|
||||
let mut secret_key = keypair.sk.clone();
|
||||
|
||||
match test_task {
|
||||
DepositTestTask::BadPubKey => pubkeybytes = PublicKeyBytes::from(new_key.pk.clone()),
|
||||
DepositTestTask::InvalidPubKey => {
|
||||
// Creating invalid public key bytes
|
||||
let mut public_key_bytes: Vec<u8> = vec![0; 48];
|
||||
public_key_bytes[0] = 255;
|
||||
pubkeybytes = PublicKeyBytes::from_bytes(&public_key_bytes).unwrap();
|
||||
}
|
||||
DepositTestTask::BadSig => secret_key = new_key.sk,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let withdrawal_credentials = Hash256::from_slice(
|
||||
&get_withdrawal_credentials(&keypair.pk, spec.bls_withdrawal_prefix_byte)[..],
|
||||
);
|
||||
|
||||
self.deposit.data.pubkey = PublicKeyBytes::from(keypair.pk.clone());
|
||||
// Building the data and signing it
|
||||
self.deposit.data.pubkey = pubkeybytes;
|
||||
self.deposit.data.withdrawal_credentials = withdrawal_credentials;
|
||||
|
||||
self.deposit.data.signature =
|
||||
self.deposit
|
||||
.data
|
||||
.create_signature(&keypair.sk, epoch, fork, spec);
|
||||
self.deposit.data.signature = self.deposit.data.create_signature(&secret_key, spec);
|
||||
}
|
||||
|
||||
/// Builds the deposit, consuming the builder.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::test_utils::TestingAttestationDataBuilder;
|
||||
use crate::test_utils::{AttestationTestTask, TestingAttestationDataBuilder};
|
||||
use crate::*;
|
||||
|
||||
/// Builds an `AttesterSlashing` to be used for testing purposes.
|
||||
@@ -15,8 +15,14 @@ impl<T: EthSpec> TestingPendingAttestationBuilder<T> {
|
||||
///
|
||||
/// * The aggregation and custody bitfields will all be empty, they need to be set with
|
||||
/// `Self::add_committee_participation`.
|
||||
pub fn new(state: &BeaconState<T>, index: u64, slot: Slot, spec: &ChainSpec) -> Self {
|
||||
let data_builder = TestingAttestationDataBuilder::new(state, index, slot);
|
||||
pub fn new(
|
||||
test_task: AttestationTestTask,
|
||||
state: &BeaconState<T>,
|
||||
index: u64,
|
||||
slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let data_builder = TestingAttestationDataBuilder::new(test_task, state, index, slot, spec);
|
||||
|
||||
let proposer_index = state.get_beacon_proposer_index(slot, spec).unwrap() as u64;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::test_utils::ProposerSlashingTestTask;
|
||||
use crate::*;
|
||||
use tree_hash::SignedRoot;
|
||||
|
||||
@@ -17,14 +18,22 @@ impl TestingProposerSlashingBuilder {
|
||||
/// - `domain: Domain`
|
||||
///
|
||||
/// Where domain is a domain "constant" (e.g., `spec.domain_attestation`).
|
||||
pub fn double_vote<T, F>(proposer_index: u64, signer: F) -> ProposerSlashing
|
||||
pub fn double_vote<T, F>(
|
||||
test_task: ProposerSlashingTestTask,
|
||||
mut proposer_index: u64,
|
||||
signer: F,
|
||||
) -> ProposerSlashing
|
||||
where
|
||||
T: EthSpec,
|
||||
F: Fn(u64, &[u8], Epoch, Domain) -> Signature,
|
||||
{
|
||||
let slot = Slot::new(0);
|
||||
let hash_1 = Hash256::from([1; 32]);
|
||||
let hash_2 = Hash256::from([2; 32]);
|
||||
let hash_2 = if test_task == ProposerSlashingTestTask::ProposalsIdentical {
|
||||
hash_1.clone()
|
||||
} else {
|
||||
Hash256::from([2; 32])
|
||||
};
|
||||
|
||||
let mut header_1 = BeaconBlockHeader {
|
||||
slot,
|
||||
@@ -34,22 +43,37 @@ impl TestingProposerSlashingBuilder {
|
||||
signature: Signature::empty_signature(),
|
||||
};
|
||||
|
||||
let slot_2 = if test_task == ProposerSlashingTestTask::ProposalEpochMismatch {
|
||||
Slot::new(128)
|
||||
} else {
|
||||
Slot::new(0)
|
||||
};
|
||||
|
||||
let mut header_2 = BeaconBlockHeader {
|
||||
parent_root: hash_2,
|
||||
slot: slot_2,
|
||||
..header_1.clone()
|
||||
};
|
||||
|
||||
let epoch = slot.epoch(T::slots_per_epoch());
|
||||
|
||||
header_1.signature = {
|
||||
let message = header_1.signed_root();
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
|
||||
};
|
||||
if test_task != ProposerSlashingTestTask::BadProposal1Signature {
|
||||
header_1.signature = {
|
||||
let message = header_1.signed_root();
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
|
||||
};
|
||||
}
|
||||
|
||||
header_2.signature = {
|
||||
let message = header_2.signed_root();
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
|
||||
};
|
||||
if test_task != ProposerSlashingTestTask::BadProposal2Signature {
|
||||
header_2.signature = {
|
||||
let message = header_2.signed_root();
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
|
||||
};
|
||||
}
|
||||
|
||||
if test_task == ProposerSlashingTestTask::ProposerUnknown {
|
||||
proposer_index = 3_141_592;
|
||||
}
|
||||
|
||||
ProposerSlashing {
|
||||
proposer_index,
|
||||
|
||||
@@ -5,7 +5,6 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
serde = "1.0.102"
|
||||
serde_derive = "1.0.102"
|
||||
toml = "0.5.4"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use clap::ArgMatches;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
use types::ChainSpec;
|
||||
|
||||
/// The core configuration of a Lighthouse beacon node.
|
||||
@@ -46,33 +44,6 @@ impl Eth2Config {
|
||||
}
|
||||
}
|
||||
|
||||
impl Eth2Config {
|
||||
/// Apply the following arguments to `self`, replacing values if they are specified in `args`.
|
||||
///
|
||||
/// Returns an error if arguments are obviously invalid. May succeed even if some values are
|
||||
/// invalid.
|
||||
pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> {
|
||||
if args.is_present("recent-genesis") {
|
||||
self.spec.min_genesis_time = recent_genesis_time()
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the system time, mod 30 minutes.
|
||||
///
|
||||
/// Used for easily creating testnets.
|
||||
fn recent_genesis_time() -> u64 {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
let secs_after_last_period = now.checked_rem(30 * 60).unwrap_or(0);
|
||||
// genesis is now the last 30 minute block.
|
||||
now - secs_after_last_period
|
||||
}
|
||||
|
||||
/// Write a configuration to file.
|
||||
pub fn write_to_file<T>(path: PathBuf, config: &T) -> Result<(), String>
|
||||
where
|
||||
@@ -111,3 +82,15 @@ where
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use toml;
|
||||
|
||||
#[test]
|
||||
fn serde_serialize() {
|
||||
let _ =
|
||||
toml::to_string(&Eth2Config::default()).expect("Should serde encode default config");
|
||||
}
|
||||
}
|
||||
|
||||
14
eth2/utils/remote_beacon_node/Cargo.toml
Normal file
14
eth2/utils/remote_beacon_node/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "remote_beacon_node"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
reqwest = "0.9"
|
||||
url = "1.2"
|
||||
serde = "1.0"
|
||||
futures = "0.1.25"
|
||||
types = { path = "../../../eth2/types" }
|
||||
141
eth2/utils/remote_beacon_node/src/lib.rs
Normal file
141
eth2/utils/remote_beacon_node/src/lib.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
//! Provides a `RemoteBeaconNode` which interacts with a HTTP API on another Lighthouse (or
|
||||
//! compatible) instance.
|
||||
//!
|
||||
//! Presently, this is only used for testing but it _could_ become a user-facing library.
|
||||
|
||||
use futures::{Future, IntoFuture};
|
||||
use reqwest::r#async::{Client, RequestBuilder};
|
||||
use serde::Deserialize;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::SocketAddr;
|
||||
use types::{BeaconBlock, BeaconState, EthSpec};
|
||||
use types::{Hash256, Slot};
|
||||
use url::Url;
|
||||
|
||||
/// Connects to a remote Lighthouse (or compatible) node via HTTP.
|
||||
pub struct RemoteBeaconNode<E: EthSpec> {
|
||||
pub http: HttpClient<E>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> RemoteBeaconNode<E> {
|
||||
pub fn new(http_endpoint: SocketAddr) -> Result<Self, String> {
|
||||
Ok(Self {
|
||||
http: HttpClient::new(format!("http://{}", http_endpoint.to_string()))
|
||||
.map_err(|e| format!("Unable to create http client: {:?}", e))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
UrlParseError(url::ParseError),
|
||||
ReqwestError(reqwest::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HttpClient<E> {
|
||||
client: Client,
|
||||
url: Url,
|
||||
_phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> HttpClient<E> {
|
||||
/// Creates a new instance (without connecting to the node).
|
||||
pub fn new(server_url: String) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
client: Client::new(),
|
||||
url: Url::parse(&server_url)?,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn beacon(&self) -> Beacon<E> {
|
||||
Beacon(self.clone())
|
||||
}
|
||||
|
||||
fn url(&self, path: &str) -> Result<Url, Error> {
|
||||
self.url.join(path).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn get(&self, path: &str) -> Result<RequestBuilder, Error> {
|
||||
self.url(path)
|
||||
.map(|url| Client::new().get(&url.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides the functions on the `/beacon` endpoint of the node.
|
||||
#[derive(Clone)]
|
||||
pub struct Beacon<E>(HttpClient<E>);
|
||||
|
||||
impl<E: EthSpec> Beacon<E> {
|
||||
fn url(&self, path: &str) -> Result<Url, Error> {
|
||||
self.0
|
||||
.url("beacon/")
|
||||
.and_then(move |url| url.join(path).map_err(Error::from))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Returns the block and block root at the given slot.
|
||||
pub fn block_at_slot(
|
||||
&self,
|
||||
slot: Slot,
|
||||
) -> impl Future<Item = (BeaconBlock<E>, Hash256), Error = Error> {
|
||||
let client = self.0.clone();
|
||||
self.url("block")
|
||||
.into_future()
|
||||
.and_then(move |mut url| {
|
||||
url.query_pairs_mut()
|
||||
.append_pair("slot", &format!("{}", slot.as_u64()));
|
||||
client.get(&url.to_string())
|
||||
})
|
||||
.and_then(|builder| builder.send().map_err(Error::from))
|
||||
.and_then(|response| response.error_for_status().map_err(Error::from))
|
||||
.and_then(|mut success| success.json::<BlockResponse<E>>().map_err(Error::from))
|
||||
.map(|response| (response.beacon_block, response.root))
|
||||
}
|
||||
|
||||
/// Returns the state and state root at the given slot.
|
||||
pub fn state_at_slot(
|
||||
&self,
|
||||
slot: Slot,
|
||||
) -> impl Future<Item = (BeaconState<E>, Hash256), Error = Error> {
|
||||
let client = self.0.clone();
|
||||
self.url("state")
|
||||
.into_future()
|
||||
.and_then(move |mut url| {
|
||||
url.query_pairs_mut()
|
||||
.append_pair("slot", &format!("{}", slot.as_u64()));
|
||||
client.get(&url.to_string())
|
||||
})
|
||||
.and_then(|builder| builder.send().map_err(Error::from))
|
||||
.and_then(|response| response.error_for_status().map_err(Error::from))
|
||||
.and_then(|mut success| success.json::<StateResponse<E>>().map_err(Error::from))
|
||||
.map(|response| (response.beacon_state, response.root))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BlockResponse<T: EthSpec> {
|
||||
pub beacon_block: BeaconBlock<T>,
|
||||
pub root: Hash256,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct StateResponse<T: EthSpec> {
|
||||
pub beacon_state: BeaconState<T>,
|
||||
pub root: Hash256,
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for Error {
|
||||
fn from(e: reqwest::Error) -> Error {
|
||||
Error::ReqwestError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<url::ParseError> for Error {
|
||||
fn from(e: url::ParseError) -> Error {
|
||||
Error::UrlParseError(e)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user