1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#[macro_use]
extern crate clap;

mod cli;
mod config;

pub use beacon_chain;
pub use cli::cli_app;
pub use client::{Client, ClientBuilder, ClientConfig, ClientGenesis};
pub use eth2_config::Eth2Config;

use beacon_chain::{
    builder::Witness, eth1_chain::CachingEth1Backend, events::WebSocketSender,
    lmd_ghost::ThreadSafeReducedTree, slot_clock::SystemTimeSlotClock,
};
use clap::ArgMatches;
use config::get_configs;
use environment::RuntimeContext;
use futures::{Future, IntoFuture};
use slog::{info, warn};
use std::ops::{Deref, DerefMut};
use store::DiskStore;
use types::EthSpec;

/// A type-alias to the tighten the definition of a production-intended `Client`.
pub type ProductionClient<E> = Client<
    Witness<
        DiskStore,
        SystemTimeSlotClock,
        ThreadSafeReducedTree<DiskStore, E>,
        CachingEth1Backend<E, DiskStore>,
        E,
        WebSocketSender<E>,
    >,
>;

/// The beacon node `Client` that will be used in production.
///
/// Generic over some `EthSpec`.
///
/// ## Notes:
///
/// Despite being titled `Production...`, this code is not ready for production. The name
/// demonstrates an intention, not a promise.
pub struct ProductionBeaconNode<E: EthSpec>(ProductionClient<E>);

impl<E: EthSpec> ProductionBeaconNode<E> {
    /// Starts a new beacon node `Client` in the given `environment`.
    ///
    /// Identical to `start_from_client_config`, however the `client_config` is generated from the
    /// given `matches` and potentially configuration files on the local filesystem or other
    /// configurations hosted remotely.
    pub fn new_from_cli<'a, 'b>(
        mut context: RuntimeContext<E>,
        matches: &ArgMatches<'b>,
    ) -> impl Future<Item = Self, Error = String> + 'a {
        let log = context.log.clone();

        // TODO: the eth2 config in the env is being completely ignored.
        //
        // See https://github.com/sigp/lighthouse/issues/602
        get_configs(&matches, log).into_future().and_then(
            move |(client_config, eth2_config, _log)| {
                context.eth2_config = eth2_config;
                Self::new(context, client_config)
            },
        )
    }

    /// Starts a new beacon node `Client` in the given `environment`.
    ///
    /// Client behaviour is defined by the given `client_config`.
    pub fn new(
        context: RuntimeContext<E>,
        client_config: ClientConfig,
    ) -> impl Future<Item = Self, Error = String> {
        let http_eth2_config = context.eth2_config().clone();
        let spec = context.eth2_config().spec.clone();
        let genesis_eth1_config = client_config.eth1.clone();
        let client_genesis = client_config.genesis.clone();
        let log = context.log.clone();

        client_config
            .db_path()
            .ok_or_else(|| "Unable to access database path".to_string())
            .into_future()
            .and_then(move |db_path| {
                Ok(ClientBuilder::new(context.eth_spec_instance.clone())
                    .runtime_context(context)
                    .disk_store(&db_path)?
                    .chain_spec(spec))
            })
            .and_then(move |builder| {
                builder.beacon_chain_builder(client_genesis, genesis_eth1_config)
            })
            .and_then(move |builder| {
                let builder = if client_config.sync_eth1_chain && !client_config.dummy_eth1_backend
                {
                    info!(
                        log,
                        "Block production enabled";
                        "endpoint" => &client_config.eth1.endpoint,
                        "method" => "json rpc via http"
                    );
                    builder.caching_eth1_backend(client_config.eth1.clone())?
                } else if client_config.dummy_eth1_backend {
                    warn!(
                        log,
                        "Block production impaired";
                        "reason" => "dummy eth1 backend is enabled"
                    );
                    builder.dummy_eth1_backend()?
                } else {
                    info!(
                        log,
                        "Block production disabled";
                        "reason" => "no eth1 backend configured"
                    );
                    builder.no_eth1_backend()?
                };

                let builder = builder
                    .system_time_slot_clock()?
                    .websocket_event_handler(client_config.websocket_server.clone())?
                    .build_beacon_chain()?
                    .libp2p_network(&client_config.network)?
                    .http_server(&client_config, &http_eth2_config)?
                    .grpc_server(&client_config.rpc)?
                    .peer_count_notifier()?
                    .slot_notifier()?;

                Ok(Self(builder.build()))
            })
    }

    pub fn into_inner(self) -> ProductionClient<E> {
        self.0
    }
}

impl<E: EthSpec> Deref for ProductionBeaconNode<E> {
    type Target = ProductionClient<E>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<E: EthSpec> DerefMut for ProductionBeaconNode<E> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}