<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Protocol used to ensure peer liveness
//!
//! The HealthChecker is responsible for ensuring liveness of all peers of a node.
//! It does so by periodically selecting a random connected peer and sending a Ping probe. A
//! healthy peer is expected to respond with a corresponding Pong message.
//!
//! If a certain number of successive liveness probes for a peer fail, the HealthChecker initiates a
//! disconnect from the peer. It relies on ConnectivityManager or the remote peer to re-establish
//! the connection.
//!
//! Future Work
//! -----------
//! We can make a few other improvements to the health checker. These are:
//! - Make the policy for interpreting ping failures pluggable
//! - Use successful inbound pings as a sign of remote note being healthy
//! - Ping a peer only in periods of no application-level communication with the peer
use crate::{
    constants::NETWORK_CHANNEL_SIZE,
    counters,
    error::NetworkError,
    logging::NetworkSchema,
    peer_manager::{ConnectionRequestSender, PeerManagerRequestSender},
    protocols::{
        network::{Event, NetworkEvents, NetworkSender, NewNetworkSender},
        rpc::error::RpcError,
    },
    ProtocolId,
};
use bytes::Bytes;
use channel::message_queues::QueueStyle;
use futures::{
    channel::oneshot,
    stream::{FusedStream, FuturesUnordered, Stream, StreamExt},
};
use libra_config::network_id::NetworkContext;
use libra_logger::prelude::*;
use libra_metrics::IntCounterVec;
use libra_types::PeerId;
use rand::{rngs::SmallRng, seq::SliceRandom, Rng, SeedableRng};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, sync::Arc, time::Duration};

pub mod builder;
#[cfg(test)]
mod test;

/// The interface from Network to HealthChecker layer.
///
/// `HealthCheckerNetworkEvents` is a `Stream` of `PeerManagerNotification` where the
/// raw `Bytes` rpc messages are deserialized into
/// `HealthCheckerMsg` types. `HealthCheckerNetworkEvents` is a thin wrapper
/// around an `channel::Receiver<PeerManagerNotification>`.
pub type HealthCheckerNetworkEvents = NetworkEvents<HealthCheckerMsg>;

/// The interface from HealthChecker to Networking layer.
///
/// This is a thin wrapper around a `NetworkSender<HealthCheckerMsg>`, so it is
/// easy to clone and send off to a separate task. For example, the rpc requests
/// return Futures that encapsulate the whole flow, from sending the request to
/// remote, to finally receiving the response and deserializing. It therefore
/// makes the most sense to make the rpc call on a separate async task, which
/// requires the `HealthCheckerNetworkSender` to be `Clone` and `Send`.
#[derive(Clone)]
pub struct HealthCheckerNetworkSender {
    inner: NetworkSender<HealthCheckerMsg>,
}

/// Configuration for the network endpoints to support HealthChecker.
pub fn network_endpoint_config() -> (
    Vec<ProtocolId>,
    Vec<ProtocolId>,
    QueueStyle,
    usize,
    Option<&'static IntCounterVec>,
) {
    (
        vec![ProtocolId::HealthCheckerRpc],
        vec![],
        QueueStyle::LIFO,
        NETWORK_CHANNEL_SIZE,
        Some(&counters::PENDING_HEALTH_CHECKER_NETWORK_EVENTS),
    )
}

impl NewNetworkSender for HealthCheckerNetworkSender {
    fn new(
        peer_mgr_reqs_tx: PeerManagerRequestSender,
        connection_reqs_tx: ConnectionRequestSender,
    ) -> Self {
        Self {
            inner: NetworkSender::new(peer_mgr_reqs_tx, connection_reqs_tx),
        }
    }
}

impl HealthCheckerNetworkSender {
    /// Send a HealthChecker Ping RPC request to remote peer `recipient`. Returns
    /// the remote peer's future `Pong` reply.
    ///
    /// The rpc request can be canceled at any point by dropping the returned
    /// future.
    pub async fn send_rpc(
        &mut self,
        recipient: PeerId,
        req_msg: HealthCheckerMsg,
        timeout: Duration,
    ) -> Result<HealthCheckerMsg, RpcError> {
        let protocol = ProtocolId::HealthCheckerRpc;
        self.inner
            .send_rpc(recipient, protocol, req_msg, timeout)
            .await
    }

    pub async fn disconnect_peer(&mut self, peer_id: PeerId) -> Result<(), NetworkError> {
        self.inner.disconnect_peer(peer_id).await
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum HealthCheckerMsg {
    Ping(Ping),
    Pong(Pong),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Ping(u32);

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Pong(u32);

/// The actor performing health checks by running the Ping protocol
pub struct HealthChecker<TTicker> {
    network_context: Arc<NetworkContext>,
    /// Ticker to trigger ping to a random peer. In production, the ticker is likely to be
    /// fixed duration interval timer.
    ticker: TTicker,
    /// Channel to send requests to Network layer.
    network_tx: HealthCheckerNetworkSender,
    /// Channel to receive notifications from Network layer about new/lost connections.
    network_rx: HealthCheckerNetworkEvents,
    /// Map from connected peer to last round of successful ping, and number of failures since
    /// then.
    connected: HashMap<PeerId, (u64, u64)>,
    /// Random-number generator.
    rng: SmallRng,
    /// Ping timmeout duration.
    ping_timeout: Duration,
    /// Number of successive ping failures we tolerate before declaring a node as unhealthy and
    /// disconnecting from it. In the future, this can be replaced with a more general failure
    /// detection policy.
    ping_failures_tolerated: u64,
    /// Counter incremented in each round of health checks
    round: u64,
}

impl<TTicker> HealthChecker<TTicker>
where
    TTicker: Stream + FusedStream + Unpin,
{
    /// Create new instance of the [`HealthChecker`] actor.
    pub fn new(
        network_context: Arc<NetworkContext>,
        ticker: TTicker,
        network_tx: HealthCheckerNetworkSender,
        network_rx: HealthCheckerNetworkEvents,
        ping_timeout: Duration,
        ping_failures_tolerated: u64,
    ) -> Self {
        HealthChecker {
            network_context,
            ticker,
            network_tx,
            network_rx,
            connected: HashMap::new(),
            rng: SmallRng::from_entropy(),
            ping_timeout,
            ping_failures_tolerated,
            round: 0,
        }
    }

    pub async fn start(mut self) {
        let mut tick_handlers = FuturesUnordered::new();
        info!(
            NetworkSchema::new(&self.network_context),
            "{} Health checker actor started", self.network_context
        );
        loop {
            futures::select! {
                event = self.network_rx.select_next_some() => {
                    match event {
                        Event::NewPeer(peer_id, _origin) => {
                            self.connected.insert(peer_id, (self.round, 0));
                        },
                        Event::LostPeer(peer_id, _origin) => {
                            self.connected.remove(&peer_id);
                        },
                        Event::RpcRequest(peer_id, msg, res_tx) => {
                            match msg {
                            HealthCheckerMsg::Ping(ping) => self.handle_ping_request(peer_id, ping, res_tx),
                            _ => {
                                warn!(
                                    SecurityEvent::InvalidHealthCheckerMsg,
                                    NetworkSchema::new(&self.network_context)
                                        .remote_peer(&peer_id),
                                    rpc_message = msg,
                                    "{} Unexpected RPC message from {}",
                                    self.network_context,
                                    peer_id
                                );
                            },
                            };
                        }
                        Event::Message(peer_id, msg) => {
                            error!(
                                SecurityEvent::InvalidNetworkEventHC,
                                NetworkSchema::new(&self.network_context)
                                    .remote_peer(&peer_id),
                                "{} Unexpected direct send from {} msg {:?}",
                                self.network_context,
                                peer_id,
                                msg,
                            );
                            debug_assert!(false, "Unexpected network event");
                        },
                    }
                }
                _ = self.ticker.select_next_some() => {
                    self.round += 1;
                    match self.sample_random_peer() {
                        Some(peer_id) => {
                            let nonce = self.sample_nonce();
                            trace!(
                                NetworkSchema::new(&self.network_context),
                                round = self.round,
                                "{} Will ping: {} for round: {} nonce: {}",
                                self.network_context, peer_id.short_str(),
                                self.round,
                                nonce
                            );

                            tick_handlers.push(
                                Self::ping_peer(
                                    self.network_context.clone(),
                                    self.network_tx.clone(),
                                    peer_id,
                                    self.round,
                                    nonce,
                                    self.ping_timeout));
                        }
                        None => {
                            trace!(
                                NetworkSchema::new(&self.network_context),
                                round = self.round,
                                "{} No connected peer to ping round: {}",
                                self.network_context,
                                self.round
                            );
                        }
                    }
                }
                res = tick_handlers.select_next_some() => {
                    let (peer_id, round, nonce, ping_result) = res;
                    self.handle_ping_response(peer_id, round, nonce, ping_result).await;
                }
                complete => {
                    break;
                }
            }
        }
        warn!(
            NetworkSchema::new(&self.network_context),
            "{} Health checker actor terminated", self.network_context
        );
    }

    fn handle_ping_request(
        &mut self,
        peer_id: PeerId,
        ping: Ping,
        res_tx: oneshot::Sender<Result<Bytes, RpcError>>,
    ) {
        let message = match lcs::to_bytes(&HealthCheckerMsg::Pong(Pong(ping.0))) {
            Ok(msg) => msg,
            Err(e) => {
                warn!(
                    NetworkSchema::new(&self.network_context),
                    error = ?e,
                    "{} Unable to serialize pong response: {}", self.network_context, e
                );
                return;
            }
        };
        trace!(
            NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
            "{} Sending Pong response to peer: {} with nonce: {}",
            self.network_context,
            peer_id.short_str(),
            ping.0,
        );
        let _ = res_tx.send(Ok(message.into()));
    }

    async fn handle_ping_response(
        &mut self,
        peer_id: PeerId,
        round: u64,
        req_nonce: u32,
        ping_result: Result<Pong, RpcError>,
    ) {
        match ping_result {
            Ok(pong) => {
                if pong.0 == req_nonce {
                    trace!(
                        NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
                        rount = round,
                        "{} Ping successful for peer: {} round: {}",
                        self.network_context,
                        peer_id.short_str(),
                        round
                    );
                    // Update last successful ping to current round.
                    self.connected
                        .entry(peer_id)
                        .and_modify(|(ref mut r, ref mut count)| {
                            if round > *r {
                                *r = round;
                                *count = 0;
                            }
                        });
                } else {
                    warn!(
                        SecurityEvent::InvalidHealthCheckerMsg,
                        NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
                        "{} Pong nonce doesn't match Ping nonce. Round: {}, Pong: {}, Ping: {}",
                        self.network_context,
                        round,
                        pong.0,
                        req_nonce
                    );
                    debug_assert!(false, "Pong nonce doesn't match our challenge Ping nonce");
                }
            }
            Err(err) => {
                warn!(
                    NetworkSchema::new(&self.network_context)
                        .remote_peer(&peer_id),
                    error = ?err,
                    round = round,
                    "{} Ping failed for peer: {} round: {} with error: {:?}",
                    self.network_context,
                    peer_id.short_str(),
                    round,
                    err
                );
                match self.connected.get_mut(&peer_id) {
                    None => {
                        // If we are no longer connected to the peer, we ignore ping
                        // failure.
                    }
                    Some((ref mut prev, ref mut failures)) => {
                        // If this is the result of an older ping, we ignore it.
                        if *prev > round {
                            return;
                        }
                        // Increment num of failures. If the ping failures are now more than
                        // `self.ping_failures_tolerated`, we disconnect from the node.
                        // The HealthChecker only performs the disconnect. It relies on
                        // ConnectivityManager or the remote peer to re-establish the connection.
                        *failures += 1;
                        if *failures > self.ping_failures_tolerated {
                            info!(
                                NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
                                "{} Disconnecting from peer: {}",
                                self.network_context,
                                peer_id.short_str()
                            );
                            if let Err(err) = self.network_tx.disconnect_peer(peer_id).await {
                                warn!(
                                    NetworkSchema::new(&self.network_context)
                                        .remote_peer(&peer_id),
                                    error = ?err,
                                    "{} Failed to disconnect from peer: {} with error: {:?}",
                                    self.network_context,
                                    peer_id.short_str(),
                                    err
                                );
                            }
                        }
                    }
                }
            }
        }
    }

    async fn ping_peer(
        network_context: Arc<NetworkContext>,
        mut network_tx: HealthCheckerNetworkSender,
        peer_id: PeerId,
        round: u64,
        nonce: u32,
        ping_timeout: Duration,
    ) -> (PeerId, u64, u32, Result<Pong, RpcError>) {
        trace!(
            NetworkSchema::new(&network_context).remote_peer(&peer_id),
            round = round,
            "{} Sending Ping request to peer: {} for round: {} nonce: {}",
            network_context,
            peer_id.short_str(),
            round,
            nonce
        );
        let res_pong_msg = network_tx
            .send_rpc(peer_id, HealthCheckerMsg::Ping(Ping(nonce)), ping_timeout)
            .await
            .and_then(|msg| match msg {
                HealthCheckerMsg::Pong(res) => Ok(res),
                _ => Err(RpcError::InvalidRpcResponse),
            });
        (peer_id, round, nonce, res_pong_msg)
    }

    fn sample_random_peer(&mut self) -> Option<PeerId> {
        let peers: Vec<_> = self.connected.keys().cloned().collect();
        peers.choose(&mut self.rng).cloned()
    }

    fn sample_nonce(&mut self) -> u32 {
        self.rng.gen::<u32>()
    }
}
<commit_msg>[network] Simplify Network Healthcheck<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Protocol used to ensure peer liveness
//!
//! The HealthChecker is responsible for ensuring liveness of all peers of a node.
//! It does so by periodically selecting a random connected peer and sending a Ping probe. A
//! healthy peer is expected to respond with a corresponding Pong message.
//!
//! If a certain number of successive liveness probes for a peer fail, the HealthChecker initiates a
//! disconnect from the peer. It relies on ConnectivityManager or the remote peer to re-establish
//! the connection.
//!
//! Future Work
//! -----------
//! We can make a few other improvements to the health checker. These are:
//! - Make the policy for interpreting ping failures pluggable
//! - Use successful inbound pings as a sign of remote note being healthy
//! - Ping a peer only in periods of no application-level communication with the peer
use crate::{
    constants::NETWORK_CHANNEL_SIZE,
    counters,
    error::NetworkError,
    logging::NetworkSchema,
    peer_manager::{ConnectionRequestSender, PeerManagerRequestSender},
    protocols::{
        network::{Event, NetworkEvents, NetworkSender, NewNetworkSender},
        rpc::error::RpcError,
    },
    ProtocolId,
};
use bytes::Bytes;
use channel::message_queues::QueueStyle;
use futures::{
    channel::oneshot,
    stream::{FusedStream, FuturesUnordered, Stream, StreamExt},
};
use libra_config::network_id::NetworkContext;
use libra_logger::prelude::*;
use libra_metrics::IntCounterVec;
use libra_types::PeerId;
use rand::{rngs::SmallRng, Rng, SeedableRng};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, sync::Arc, time::Duration};

pub mod builder;
#[cfg(test)]
mod test;

/// The interface from Network to HealthChecker layer.
///
/// `HealthCheckerNetworkEvents` is a `Stream` of `PeerManagerNotification` where the
/// raw `Bytes` rpc messages are deserialized into
/// `HealthCheckerMsg` types. `HealthCheckerNetworkEvents` is a thin wrapper
/// around an `channel::Receiver<PeerManagerNotification>`.
pub type HealthCheckerNetworkEvents = NetworkEvents<HealthCheckerMsg>;

/// The interface from HealthChecker to Networking layer.
///
/// This is a thin wrapper around a `NetworkSender<HealthCheckerMsg>`, so it is
/// easy to clone and send off to a separate task. For example, the rpc requests
/// return Futures that encapsulate the whole flow, from sending the request to
/// remote, to finally receiving the response and deserializing. It therefore
/// makes the most sense to make the rpc call on a separate async task, which
/// requires the `HealthCheckerNetworkSender` to be `Clone` and `Send`.
#[derive(Clone)]
pub struct HealthCheckerNetworkSender {
    inner: NetworkSender<HealthCheckerMsg>,
}

/// Configuration for the network endpoints to support HealthChecker.
pub fn network_endpoint_config() -> (
    Vec<ProtocolId>,
    Vec<ProtocolId>,
    QueueStyle,
    usize,
    Option<&'static IntCounterVec>,
) {
    (
        vec![ProtocolId::HealthCheckerRpc],
        vec![],
        QueueStyle::LIFO,
        NETWORK_CHANNEL_SIZE,
        Some(&counters::PENDING_HEALTH_CHECKER_NETWORK_EVENTS),
    )
}

impl NewNetworkSender for HealthCheckerNetworkSender {
    fn new(
        peer_mgr_reqs_tx: PeerManagerRequestSender,
        connection_reqs_tx: ConnectionRequestSender,
    ) -> Self {
        Self {
            inner: NetworkSender::new(peer_mgr_reqs_tx, connection_reqs_tx),
        }
    }
}

impl HealthCheckerNetworkSender {
    /// Send a HealthChecker Ping RPC request to remote peer `recipient`. Returns
    /// the remote peer's future `Pong` reply.
    ///
    /// The rpc request can be canceled at any point by dropping the returned
    /// future.
    pub async fn send_rpc(
        &mut self,
        recipient: PeerId,
        req_msg: HealthCheckerMsg,
        timeout: Duration,
    ) -> Result<HealthCheckerMsg, RpcError> {
        let protocol = ProtocolId::HealthCheckerRpc;
        self.inner
            .send_rpc(recipient, protocol, req_msg, timeout)
            .await
    }

    pub async fn disconnect_peer(&mut self, peer_id: PeerId) -> Result<(), NetworkError> {
        self.inner.disconnect_peer(peer_id).await
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum HealthCheckerMsg {
    Ping(Ping),
    Pong(Pong),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Ping(u32);

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Pong(u32);

/// The actor performing health checks by running the Ping protocol
pub struct HealthChecker<TTicker> {
    network_context: Arc<NetworkContext>,
    /// Ticker to trigger ping to a random peer. In production, the ticker is likely to be
    /// fixed duration interval timer.
    ticker: TTicker,
    /// Channel to send requests to Network layer.
    network_tx: HealthCheckerNetworkSender,
    /// Channel to receive notifications from Network layer about new/lost connections.
    network_rx: HealthCheckerNetworkEvents,
    /// Map from connected peer to last round of successful ping, and number of failures since
    /// then.
    connected: HashMap<PeerId, (u64, u64)>,
    /// Random-number generator.
    rng: SmallRng,
    /// Ping timmeout duration.
    ping_timeout: Duration,
    /// Number of successive ping failures we tolerate before declaring a node as unhealthy and
    /// disconnecting from it. In the future, this can be replaced with a more general failure
    /// detection policy.
    ping_failures_tolerated: u64,
    /// Counter incremented in each round of health checks
    round: u64,
}

impl<TTicker> HealthChecker<TTicker>
where
    TTicker: Stream + FusedStream + Unpin,
{
    /// Create new instance of the [`HealthChecker`] actor.
    pub fn new(
        network_context: Arc<NetworkContext>,
        ticker: TTicker,
        network_tx: HealthCheckerNetworkSender,
        network_rx: HealthCheckerNetworkEvents,
        ping_timeout: Duration,
        ping_failures_tolerated: u64,
    ) -> Self {
        HealthChecker {
            network_context,
            ticker,
            network_tx,
            network_rx,
            connected: HashMap::new(),
            rng: SmallRng::from_entropy(),
            ping_timeout,
            ping_failures_tolerated,
            round: 0,
        }
    }

    pub async fn start(mut self) {
        let mut tick_handlers = FuturesUnordered::new();
        info!(
            NetworkSchema::new(&self.network_context),
            "{} Health checker actor started", self.network_context
        );
        loop {
            futures::select! {
                event = self.network_rx.select_next_some() => {
                    match event {
                        Event::NewPeer(peer_id, _origin) => {
                            self.connected.insert(peer_id, (self.round, 0));
                        }
                        Event::LostPeer(peer_id, _origin) => {
                            self.connected.remove(&peer_id);
                        }
                        Event::RpcRequest(peer_id, msg, res_tx) => {
                            match msg {
                                HealthCheckerMsg::Ping(ping) => self.handle_ping_request(peer_id, ping, res_tx),
                                _ => {
                                    warn!(
                                        SecurityEvent::InvalidHealthCheckerMsg,
                                        NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
                                        rpc_message = msg,
                                        "{} Unexpected RPC message from {}",
                                        self.network_context,
                                        peer_id
                                    );
                                }
                            };
                        }
                        Event::Message(peer_id, msg) => {
                            error!(
                                SecurityEvent::InvalidNetworkEventHC,
                                NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
                                "{} Unexpected direct send from {} msg {:?}",
                                self.network_context,
                                peer_id,
                                msg,
                            );
                            debug_assert!(false, "Unexpected network event");
                        }
                    }
                }
                _ = self.ticker.select_next_some() => {
                    self.round += 1;

                    if self.connected.is_empty() {
                        trace!(
                            NetworkSchema::new(&self.network_context),
                            round = self.round,
                            "{} No connected peer to ping round: {}",
                            self.network_context,
                            self.round
                        );
                        continue
                    }

                    let peers: Vec<_> = self.connected.keys().cloned().collect();
                    for peer_id in peers {
                        let nonce = self.nonce();
                        trace!(
                            NetworkSchema::new(&self.network_context),
                            round = self.round,
                            "{} Will ping: {} for round: {} nonce: {}",
                            self.network_context,
                            peer_id.short_str(),
                            self.round,
                            nonce
                        );

                        tick_handlers.push(Self::ping_peer(
                            self.network_context.clone(),
                            self.network_tx.clone(),
                            peer_id,
                            self.round,
                            nonce,
                            self.ping_timeout,
                        ));
                    }
                }
                res = tick_handlers.select_next_some() => {
                    let (peer_id, round, nonce, ping_result) = res;
                    self.handle_ping_response(peer_id, round, nonce, ping_result).await;
                }
                complete => {
                    break;
                }
            }
        }
        warn!(
            NetworkSchema::new(&self.network_context),
            "{} Health checker actor terminated", self.network_context
        );
    }

    fn handle_ping_request(
        &mut self,
        peer_id: PeerId,
        ping: Ping,
        res_tx: oneshot::Sender<Result<Bytes, RpcError>>,
    ) {
        let message = match lcs::to_bytes(&HealthCheckerMsg::Pong(Pong(ping.0))) {
            Ok(msg) => msg,
            Err(e) => {
                warn!(
                    NetworkSchema::new(&self.network_context),
                    error = ?e,
                    "{} Unable to serialize pong response: {}", self.network_context, e
                );
                return;
            }
        };
        trace!(
            NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
            "{} Sending Pong response to peer: {} with nonce: {}",
            self.network_context,
            peer_id.short_str(),
            ping.0,
        );
        let _ = res_tx.send(Ok(message.into()));
    }

    async fn handle_ping_response(
        &mut self,
        peer_id: PeerId,
        round: u64,
        req_nonce: u32,
        ping_result: Result<Pong, RpcError>,
    ) {
        match ping_result {
            Ok(pong) => {
                if pong.0 == req_nonce {
                    trace!(
                        NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
                        rount = round,
                        "{} Ping successful for peer: {} round: {}",
                        self.network_context,
                        peer_id.short_str(),
                        round
                    );
                    // Update last successful ping to current round.
                    self.connected
                        .entry(peer_id)
                        .and_modify(|(ref mut r, ref mut count)| {
                            if round > *r {
                                *r = round;
                                *count = 0;
                            }
                        });
                } else {
                    warn!(
                        SecurityEvent::InvalidHealthCheckerMsg,
                        NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
                        "{} Pong nonce doesn't match Ping nonce. Round: {}, Pong: {}, Ping: {}",
                        self.network_context,
                        round,
                        pong.0,
                        req_nonce
                    );
                    debug_assert!(false, "Pong nonce doesn't match our challenge Ping nonce");
                }
            }
            Err(err) => {
                warn!(
                    NetworkSchema::new(&self.network_context)
                        .remote_peer(&peer_id),
                    error = ?err,
                    round = round,
                    "{} Ping failed for peer: {} round: {} with error: {:?}",
                    self.network_context,
                    peer_id.short_str(),
                    round,
                    err
                );
                match self.connected.get_mut(&peer_id) {
                    None => {
                        // If we are no longer connected to the peer, we ignore ping
                        // failure.
                    }
                    Some((ref mut prev, ref mut failures)) => {
                        // If this is the result of an older ping, we ignore it.
                        if *prev > round {
                            return;
                        }
                        // Increment num of failures. If the ping failures are now more than
                        // `self.ping_failures_tolerated`, we disconnect from the node.
                        // The HealthChecker only performs the disconnect. It relies on
                        // ConnectivityManager or the remote peer to re-establish the connection.
                        *failures += 1;
                        if *failures > self.ping_failures_tolerated {
                            info!(
                                NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
                                "{} Disconnecting from peer: {}",
                                self.network_context,
                                peer_id.short_str()
                            );
                            if let Err(err) = self.network_tx.disconnect_peer(peer_id).await {
                                warn!(
                                    NetworkSchema::new(&self.network_context)
                                        .remote_peer(&peer_id),
                                    error = ?err,
                                    "{} Failed to disconnect from peer: {} with error: {:?}",
                                    self.network_context,
                                    peer_id.short_str(),
                                    err
                                );
                            }
                        }
                    }
                }
            }
        }
    }

    async fn ping_peer(
        network_context: Arc<NetworkContext>,
        mut network_tx: HealthCheckerNetworkSender,
        peer_id: PeerId,
        round: u64,
        nonce: u32,
        ping_timeout: Duration,
    ) -> (PeerId, u64, u32, Result<Pong, RpcError>) {
        trace!(
            NetworkSchema::new(&network_context).remote_peer(&peer_id),
            round = round,
            "{} Sending Ping request to peer: {} for round: {} nonce: {}",
            network_context,
            peer_id.short_str(),
            round,
            nonce
        );
        let res_pong_msg = network_tx
            .send_rpc(peer_id, HealthCheckerMsg::Ping(Ping(nonce)), ping_timeout)
            .await
            .and_then(|msg| match msg {
                HealthCheckerMsg::Pong(res) => Ok(res),
                _ => Err(RpcError::InvalidRpcResponse),
            });
        (peer_id, round, nonce, res_pong_msg)
    }

    fn nonce(&mut self) -> u32 {
        self.rng.gen::<u32>()
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::f32::consts::{PI};
use std::num::{Float};
use cgmath::{Vector3, Vector, Rad, Angle, rad};
use common::types::{ZInt, ZFloat, MapPos};
use zgl::types::{VertexCoord, WorldPos};
use core::geom;

pub use core::geom::{HEX_IN_RADIUS, HEX_EX_RADIUS};

pub const MINIMAL_LIFT_HEIGHT: ZFloat = 0.01;

pub fn map_pos_to_world_pos(i: &MapPos) -> WorldPos {
    WorldPos{v: geom::map_pos_to_world_pos(i).extend(0.0)}
}

pub fn lift(v: Vector3<ZFloat>) -> Vector3<ZFloat> {
    let mut v = v;
    v.z += MINIMAL_LIFT_HEIGHT;
    v
}

pub fn index_to_circle_vertex(count: ZInt, i: ZInt) -> VertexCoord {
    let n = PI / 2.0 + 2.0 * PI * (i as ZFloat) / (count as ZFloat);
    VertexCoord {
        v: Vector3{x: n.cos(), y: n.sin(), z: 0.0}.mul_s(HEX_EX_RADIUS)
    }
}

pub fn index_to_hex_vertex(i: ZInt) -> VertexCoord {
    index_to_circle_vertex(6, i)
}

pub fn index_to_hex_vertex_s(scale: ZFloat, i: ZInt) -> VertexCoord {
    let v = index_to_hex_vertex(i).v.mul_s(scale);
    VertexCoord{v: v}
}

// TODO: ZFloat -> WorldDistance
pub fn dist(a: &WorldPos, b: &WorldPos) -> ZFloat {
    let dx = (b.v.x - a.v.x).abs();
    let dy = (b.v.y - a.v.y).abs();
    let dz = (b.v.z - a.v.z).abs();
    ((dx.powi(2) + dy.powi(2) + dz.powi(2)) as ZFloat).sqrt()
}

pub fn get_rot_angle(a: &WorldPos, b: &WorldPos) -> Rad<ZFloat> {
    let diff = b.v - a.v;
    let angle = Float::atan2(diff.x, diff.y);
    rad(-angle).normalize()
}

#[cfg(test)]
mod tests {
    use std::num::{Float};
    use cgmath::{Vector3};
    use common::types::{ZFloat};
    use zgl::types::{WorldPos};
    use super::{get_rot_angle, index_to_circle_vertex};

    const EPS: ZFloat = 0.001;

    #[test]
    fn test_get_rot_angle_30_deg() {
        let count = 12;
        for i in 0 .. count {
            let a = WorldPos{v: Vector3{x: 0.0, y: 0.0, z: 0.0}};
            let b = WorldPos{v: index_to_circle_vertex(count, i).v};
            let expected_angle = i as f32 * 360.0 / (count as f32);
            let angle = get_rot_angle(&a, &b);
            let diff = (expected_angle - angle.s).abs();
            assert!(diff < EPS, "{} != {}", expected_angle, angle.s);
        }
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>geom.rs: Fixed tests: deg -> rad<commit_after>// See LICENSE file for copyright and license details.

use std::f32::consts::{PI};
use std::num::{Float};
use cgmath::{Vector3, Vector, Rad, Angle, rad};
use common::types::{ZInt, ZFloat, MapPos};
use zgl::types::{VertexCoord, WorldPos};
use core::geom;

pub use core::geom::{HEX_IN_RADIUS, HEX_EX_RADIUS};

pub const MINIMAL_LIFT_HEIGHT: ZFloat = 0.01;

pub fn map_pos_to_world_pos(i: &MapPos) -> WorldPos {
    WorldPos{v: geom::map_pos_to_world_pos(i).extend(0.0)}
}

pub fn lift(v: Vector3<ZFloat>) -> Vector3<ZFloat> {
    let mut v = v;
    v.z += MINIMAL_LIFT_HEIGHT;
    v
}

pub fn index_to_circle_vertex(count: ZInt, i: ZInt) -> VertexCoord {
    let n = PI / 2.0 + 2.0 * PI * (i as ZFloat) / (count as ZFloat);
    VertexCoord {
        v: Vector3{x: n.cos(), y: n.sin(), z: 0.0}.mul_s(HEX_EX_RADIUS)
    }
}

pub fn index_to_hex_vertex(i: ZInt) -> VertexCoord {
    index_to_circle_vertex(6, i)
}

pub fn index_to_hex_vertex_s(scale: ZFloat, i: ZInt) -> VertexCoord {
    let v = index_to_hex_vertex(i).v.mul_s(scale);
    VertexCoord{v: v}
}

// TODO: ZFloat -> WorldDistance
pub fn dist(a: &WorldPos, b: &WorldPos) -> ZFloat {
    let dx = (b.v.x - a.v.x).abs();
    let dy = (b.v.y - a.v.y).abs();
    let dz = (b.v.z - a.v.z).abs();
    ((dx.powi(2) + dy.powi(2) + dz.powi(2)) as ZFloat).sqrt()
}

pub fn get_rot_angle(a: &WorldPos, b: &WorldPos) -> Rad<ZFloat> {
    let diff = b.v - a.v;
    let angle = Float::atan2(diff.x, diff.y);
    rad(-angle).normalize()
}

#[cfg(test)]
mod tests {
    use std::f32::consts::{PI};
    use std::num::{Float};
    use cgmath::{Vector3};
    use common::types::{ZFloat};
    use zgl::types::{WorldPos};
    use super::{get_rot_angle, index_to_circle_vertex};

    const EPS: ZFloat = 0.001;

    #[test]
    fn test_get_rot_angle_30_deg() {
        let count = 12;
        for i in 0 .. count {
            let a = WorldPos{v: Vector3{x: 0.0, y: 0.0, z: 0.0}};
            let b = WorldPos{v: index_to_circle_vertex(count, i).v};
            let expected_angle = i as f32 * (PI * 2.0) / (count as f32);
            let angle = get_rot_angle(&a, &b);
            let diff = (expected_angle - angle.s).abs();
            assert!(diff < EPS, "{} != {}", expected_angle, angle.s);
        }
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>use std::old_io::Command;
use std::old_io::process::StdioContainer;
#[cfg(target_os = "linux")]
use std::old_io::fs;

#[cfg(target_os = "linux")]
fn main() {
    Command::new("make")
            .arg("-C").arg("modules/oculus_sdk_linux/")
            .stdout(StdioContainer::InheritFd(1))
            .stderr(StdioContainer::InheritFd(2))
            .status()
            .ok().expect("Failed to build");
    fs::copy(&Path::new("modules/oculus_sdk_linux/LibOVR/Lib/Linux/Release/x86_64/libovr.a"),
             &Path::new(env!("OUT_DIR")).join(Path::new("libovr.a")))
            .ok().expect("Failed to move file");

    println!("cargo:rustc-flags=-L {} -l ovr:static", env!("OUT_DIR"));
}

#[cfg(target_os = "macos")]
fn main() {
    Command::new("xcodebuild")
            .arg("-project")
            .arg("modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj")
            .arg("build")
            .stdout(StdioContainer::InheritFd(1))
            .stderr(StdioContainer::InheritFd(2))
            .status()
            .ok().expect("Failed to build");
    Command::new("lipo")
            .arg("modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Release/libovr.a")
            .arg("-thin")
            .arg("x86_64")
            .arg("-output")
            .arg(Path::new(env!("OUT_DIR")).join(Path::new("libovr.a")).as_str().unwrap())
            .stdout(StdioContainer::InheritFd(1))
            .stderr(StdioContainer::InheritFd(2))
            .status()
            .ok().expect("Failed to lipo library");
    println!("cargo:rustc-flags=-L {} -l ovr:static", env!("OUT_DIR"));
}<commit_msg>update build.rs to latest stdlib<commit_after>use std::path::Path;
use std::process::Command;
#[cfg(target_os = "linux")]
use std::fs;

#[cfg(target_os = "linux")]
fn main() {
    Command::new("make")
            .arg("-C").arg("modules/oculus_sdk_linux/")
            .status()
            .ok().expect("Failed to build");
    fs::copy(&Path::new("modules/oculus_sdk_linux/LibOVR/Lib/Linux/Release/x86_64/libovr.a"),
             &Path::new(env!("OUT_DIR")).join(Path::new("libovr.a")))
            .ok().expect("Failed to move file");

    println!("cargo:rustc-flags=-L {} -l ovr:static", env!("OUT_DIR"));
}

#[cfg(target_os = "macos")]
fn main() {
    Command::new("xcodebuild")
            .arg("-project")
            .arg("modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj")
            .arg("build")
            .status()
            .ok().expect("Failed to build");
    Command::new("lipo")
            .arg("modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Release/libovr.a")
            .arg("-thin")
            .arg("x86_64")
            .arg("-output")
            .arg(Path::new(env!("OUT_DIR")).join(Path::new("libovr.a")).as_str().unwrap())
            .status()
            .ok().expect("Failed to lipo library");
    println!("cargo:rustc-flags=-L {} -l ovr:static", env!("OUT_DIR"));
}<|endoftext|>


<commit_before>use std::io::Command;
#[cfg(target_os = "linux")]
use std::io::fs;

#[cfg(target_os = "linux")]
fn main() {
    Command::new("make")
            .arg("-C").arg("modules/oculus_sdk_linux/")
            .status()
            .ok().expect("Failed to build");
    fs::copy(&Path::new("modules/oculus_sdk_linux/LibOVR/Lib/Linux/Release/x86_64/libovr.a"),
             &Path::new(env!("OUT_DIR")).join(Path::new("libovr.a")))
            .ok().expect("Failed to move file");

    println!("cargo:rustc-flags=-L {} -l ovr:static", env!("OUT_DIR"));
}

#[cfg(target_os = "macos")]
fn main() {
    Command::new("xcodebuild")
            .arg("-project")
            .arg("modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj")
            .arg("build")
            .status()
            .ok().expect("Failed to build");
    Command::new("lipo")
            .arg("modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Release/libovr.a")
            .arg("-thin")
            .arg("x86_64")
            .arg("-output")
            .arg(Path::new(env!("OUT_DIR")).join(Path::new("libovr.a")).as_str().unwrap())
            .status()
            .ok().expect("Failed to lipo library");
    println!("cargo:rustc-flags=-L {} -l ovr:static", env!("OUT_DIR"));
}<commit_msg>use stdout stderr for build script<commit_after>use std::io::Command;
use std::io::process::StdioContainer;
#[cfg(target_os = "linux")]
use std::io::fs;

#[cfg(target_os = "linux")]
fn main() {
    Command::new("make")
            .arg("-C").arg("modules/oculus_sdk_linux/")
            .status()
            .stdout(StdioContainer::InheritFd(1))
            .stderr(StdioContainer::InheritFd(2))
            .ok().expect("Failed to build");
    fs::copy(&Path::new("modules/oculus_sdk_linux/LibOVR/Lib/Linux/Release/x86_64/libovr.a"),
             &Path::new(env!("OUT_DIR")).join(Path::new("libovr.a")))
            .ok().expect("Failed to move file");

    println!("cargo:rustc-flags=-L {} -l ovr:static", env!("OUT_DIR"));
}

#[cfg(target_os = "macos")]
fn main() {
    Command::new("xcodebuild")
            .arg("-project")
            .arg("modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj")
            .arg("build")
            .stdout(StdioContainer::InheritFd(1))
            .stderr(StdioContainer::InheritFd(2))
            .status()
            .ok().expect("Failed to build");
    Command::new("lipo")
            .arg("modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Release/libovr.a")
            .arg("-thin")
            .arg("x86_64")
            .arg("-output")
            .arg(Path::new(env!("OUT_DIR")).join(Path::new("libovr.a")).as_str().unwrap())
            .stdout(StdioContainer::InheritFd(1))
            .stderr(StdioContainer::InheritFd(2))
            .status()
            .ok().expect("Failed to lipo library");
    println!("cargo:rustc-flags=-L {} -l ovr:static", env!("OUT_DIR"));
}<|endoftext|>


<commit_before>// ignore-macos
// ignore-windows

#![feature(main)]

#[warn(clippy::main_recursion)]
#[allow(unconditional_recursion)]
#[main]
fn a() {
    println!("Hello, World!");
    a();
}
<commit_msg>Remove #[main] attribute.<commit_after>// ignore-macos
// ignore-windows

#![feature(rustc_attrs)]

#[warn(clippy::main_recursion)]
#[allow(unconditional_recursion)]
#[rustc_main]
fn a() {
    println!("Hello, World!");
    a();
}
<|endoftext|>


<commit_before>// edition:2018
// run-pass

// Test that we can use async fns with multiple arbitrary lifetimes.

#![feature(async_await, await_macro)]

async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}

fn main() {
    let _ = multiple_elided_lifetimes(&22, &44);
}
<commit_msg>Update src/test/ui/async-await/multiple-lifetimes/elided.rs<commit_after>// edition:2018
// run-pass

// Test that we can use async fns with multiple arbitrary lifetimes.

#![feature(async_await)]

async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}

fn main() {
    let _ = multiple_elided_lifetimes(&22, &44);
}
<|endoftext|>


<commit_before>/// Expression for computing the per-frame score for a scene from that frame's total mass and total
/// mass count and the tick count.
// TODO(zstewar1): Find a better way to handle parsing. S-expressions as a yaml string?
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum Expression {
    /// The current tick.
    Tick,
    /// The total mass for the frame.
    TotalMass,
    /// The number of masses for the frame.
    MassCount,
    /// A floating point constant.
    Constant(f64),
    /// The product of a series of expressions.
    Multiply(Vec<Expression>),
    /// The sum of a series of expressions.
    Add(Vec<Expression>),
    /// A series of expressions raised to the power of each next one.
    Power(Vec<Expression>),
    /// A series of expressions subtracted from each other *or* the negation of the single input
    /// expression.
    Subtract(Vec<Expression>),
    /// A series of expressions successively divided by each other.
    Divide(Vec<Expression>),
}

impl Expression {
    /// Evaluate the expression given the scoring function inputs.
    pub fn eval(&self, tick: f64, total_mass: f64, mass_count: f64) -> f64 {
        match self {
            Expression::Tick => tick,
            Expression::TotalMass => total_mass,
            Expression::MassCount => mass_count,
            Expression::Constant(value) => *value,
            Expression::Multiply(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc * next),
            Expression::Add(ref subexprs) => if subexprs.len() == 1 {
                subexprs[0].eval(tick, total_mass, mass_count)
            } else {
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc + next)
            },
            Expression::Power(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc.powf(next)),
            Expression::Subtract(ref subexprs) => if subexprs.len() == 1 {
                -subexprs[0].eval(tick, total_mass, mass_count)
            } else {
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc - next)
            },
            Expression::Divide(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc / next),
        }
    }
}

fn fold_eval<F>(
    tick: f64, total_mass: f64, mass_count: f64,
    items: &[Expression],
    mut func: F,
) -> f64
    where F: FnMut(f64, f64) -> f64,
{
    assert!(items.len() >= 2);
    let mut iter = items.iter();
    let first = iter.next().unwrap().eval(tick, total_mass, mass_count);
    iter.fold(first, |acc, next| func(acc, next.eval(tick, total_mass, mass_count)))
}
<commit_msg>add header to scoring_function<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Expression for computing the per-frame score for a scene from that frame's total mass and total
/// mass count and the tick count.
// TODO(zstewar1): Find a better way to handle parsing. S-expressions as a yaml string?
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum Expression {
    /// The current tick.
    Tick,
    /// The total mass for the frame.
    TotalMass,
    /// The number of masses for the frame.
    MassCount,
    /// A floating point constant.
    Constant(f64),
    /// The product of a series of expressions.
    Multiply(Vec<Expression>),
    /// The sum of a series of expressions.
    Add(Vec<Expression>),
    /// A series of expressions raised to the power of each next one.
    Power(Vec<Expression>),
    /// A series of expressions subtracted from each other *or* the negation of the single input
    /// expression.
    Subtract(Vec<Expression>),
    /// A series of expressions successively divided by each other.
    Divide(Vec<Expression>),
}

impl Expression {
    /// Evaluate the expression given the scoring function inputs.
    pub fn eval(&self, tick: f64, total_mass: f64, mass_count: f64) -> f64 {
        match self {
            Expression::Tick => tick,
            Expression::TotalMass => total_mass,
            Expression::MassCount => mass_count,
            Expression::Constant(value) => *value,
            Expression::Multiply(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc * next),
            Expression::Add(ref subexprs) => if subexprs.len() == 1 {
                subexprs[0].eval(tick, total_mass, mass_count)
            } else {
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc + next)
            },
            Expression::Power(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc.powf(next)),
            Expression::Subtract(ref subexprs) => if subexprs.len() == 1 {
                -subexprs[0].eval(tick, total_mass, mass_count)
            } else {
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc - next)
            },
            Expression::Divide(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc / next),
        }
    }
}

fn fold_eval<F>(
    tick: f64, total_mass: f64, mass_count: f64,
    items: &[Expression],
    mut func: F,
) -> f64
    where F: FnMut(f64, f64) -> f64,
{
    assert!(items.len() >= 2);
    let mut iter = items.iter();
    let first = iter.next().unwrap().eval(tick, total_mass, mass_count);
    iter.fold(first, |acc, next| func(acc, next.eval(tick, total_mass, mass_count)))
}
<|endoftext|>


<commit_before>use crate::{BeaconChain, BeaconChainTypes};
use eth2_hashing::hash;
use lighthouse_bootstrap::Bootstrapper;
use merkle_proof::MerkleTree;
use rayon::prelude::*;
use slog::Logger;
use ssz::{Decode, Encode};
use state_processing::initialize_beacon_state_from_eth1;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::SystemTime;
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 {
        genesis_state: Box<BeaconState<T::EthSpec>>,
        genesis_block: Box<BeaconBlock<T::EthSpec>>,
    },
    LoadFromStore,
}

pub struct BeaconChainBuilder<T: BeaconChainTypes> {
    build_strategy: BuildStrategy<T>,
    spec: ChainSpec,
    log: Logger,
}

impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
    pub fn recent_genesis(
        validator_count: usize,
        minutes: u64,
        spec: ChainSpec,
        log: Logger,
    ) -> Result<Self, String> {
        Self::quick_start(recent_genesis_time(minutes), validator_count, spec, log)
    }

    pub fn quick_start(
        genesis_time: u64,
        validator_count: usize,
        spec: ChainSpec,
        log: Logger,
    ) -> Result<Self, String> {
        let genesis_state = interop_genesis_state(validator_count, genesis_time, &spec)?;

        Ok(Self::from_genesis_state(genesis_state, spec, log))
    }

    pub fn yaml_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
        let file = File::open(file.clone())
            .map_err(|e| format!("Unable to open YAML genesis state file {:?}: {:?}", file, e))?;

        let genesis_state = serde_yaml::from_reader(file)
            .map_err(|e| format!("Unable to parse YAML genesis state file: {:?}", e))?;

        Ok(Self::from_genesis_state(genesis_state, spec, log))
    }

    pub fn ssz_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
        let mut file = File::open(file.clone())
            .map_err(|e| format!("Unable to open SSZ genesis state file {:?}: {:?}", file, e))?;

        let mut bytes = vec![];
        file.read_to_end(&mut bytes)
            .map_err(|e| format!("Failed to read SSZ file: {:?}", e))?;

        let genesis_state = BeaconState::from_ssz_bytes(&bytes)
            .map_err(|e| format!("Unable to parse SSZ genesis state file: {:?}", e))?;

        Ok(Self::from_genesis_state(genesis_state, spec, log))
    }

    pub fn json_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
        let file = File::open(file.clone())
            .map_err(|e| format!("Unable to open JSON genesis state file {:?}: {:?}", file, e))?;

        let genesis_state = serde_json::from_reader(file)
            .map_err(|e| format!("Unable to parse JSON genesis state file: {:?}", e))?;

        Ok(Self::from_genesis_state(genesis_state, spec, log))
    }

    pub fn http_bootstrap(server: &str, spec: ChainSpec, log: Logger) -> Result<Self, String> {
        let bootstrapper = Bootstrapper::from_server_string(server.to_string())
            .map_err(|e| format!("Failed to initialize bootstrap client: {}", e))?;

        let (genesis_state, genesis_block) = bootstrapper
            .genesis()
            .map_err(|e| format!("Failed to bootstrap genesis state: {}", e))?;

        Ok(Self {
            build_strategy: BuildStrategy::FromGenesis {
                genesis_block: Box::new(genesis_block),
                genesis_state: Box::new(genesis_state),
            },
            spec,
            log,
        })
    }

    fn from_genesis_state(
        genesis_state: BeaconState<T::EthSpec>,
        spec: ChainSpec,
        log: Logger,
    ) -> Self {
        Self {
            build_strategy: BuildStrategy::FromGenesis {
                genesis_block: Box::new(genesis_block(&genesis_state, &spec)),
                genesis_state: Box::new(genesis_state),
            },
            spec,
            log,
        }
    }

    pub fn from_store(spec: ChainSpec, log: Logger) -> Self {
        Self {
            build_strategy: BuildStrategy::LoadFromStore,
            spec,
            log,
        }
    }

    pub fn build(self, store: Arc<T::Store>) -> Result<BeaconChain<T>, String> {
        Ok(match self.build_strategy {
            BuildStrategy::LoadFromStore => BeaconChain::from_store(store, self.spec, self.log)
                .map_err(|e| format!("Error loading BeaconChain from database: {:?}", e))?
                .ok_or_else(|| format!("Unable to find exising BeaconChain in database."))?,
            BuildStrategy::FromGenesis {
                genesis_block,
                genesis_state,
            } => BeaconChain::from_genesis(
                store,
                genesis_state.as_ref().clone(),
                genesis_block.as_ref().clone(),
                self.spec,
                self.log,
            )
            .map_err(|e| format!("Failed to initialize new beacon chain: {:?}", e))?,
        })
    }
}

fn genesis_block<T: EthSpec>(genesis_state: &BeaconState<T>, spec: &ChainSpec) -> BeaconBlock<T> {
    let mut genesis_block = BeaconBlock::empty(&spec);

    genesis_block.state_root = genesis_state.canonical_root();

    genesis_block
}

/// 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
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;

    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.
fn recent_genesis_time(minutes: u64) -> u64 {
    let now = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs();
    let secs_after_last_period = now.checked_rem(minutes * 60).unwrap_or(0);
    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"
        );
    }
}
<commit_msg>Fix interop eth1 blockhash<commit_after>use crate::{BeaconChain, BeaconChainTypes};
use eth2_hashing::hash;
use lighthouse_bootstrap::Bootstrapper;
use merkle_proof::MerkleTree;
use rayon::prelude::*;
use slog::Logger;
use ssz::{Decode, Encode};
use state_processing::initialize_beacon_state_from_eth1;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::SystemTime;
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 {
        genesis_state: Box<BeaconState<T::EthSpec>>,
        genesis_block: Box<BeaconBlock<T::EthSpec>>,
    },
    LoadFromStore,
}

pub struct BeaconChainBuilder<T: BeaconChainTypes> {
    build_strategy: BuildStrategy<T>,
    spec: ChainSpec,
    log: Logger,
}

impl<T: BeaconChainTypes> BeaconChainBuilder<T> {
    pub fn recent_genesis(
        validator_count: usize,
        minutes: u64,
        spec: ChainSpec,
        log: Logger,
    ) -> Result<Self, String> {
        Self::quick_start(recent_genesis_time(minutes), validator_count, spec, log)
    }

    pub fn quick_start(
        genesis_time: u64,
        validator_count: usize,
        spec: ChainSpec,
        log: Logger,
    ) -> Result<Self, String> {
        let genesis_state = interop_genesis_state(validator_count, genesis_time, &spec)?;

        Ok(Self::from_genesis_state(genesis_state, spec, log))
    }

    pub fn yaml_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
        let file = File::open(file.clone())
            .map_err(|e| format!("Unable to open YAML genesis state file {:?}: {:?}", file, e))?;

        let genesis_state = serde_yaml::from_reader(file)
            .map_err(|e| format!("Unable to parse YAML genesis state file: {:?}", e))?;

        Ok(Self::from_genesis_state(genesis_state, spec, log))
    }

    pub fn ssz_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
        let mut file = File::open(file.clone())
            .map_err(|e| format!("Unable to open SSZ genesis state file {:?}: {:?}", file, e))?;

        let mut bytes = vec![];
        file.read_to_end(&mut bytes)
            .map_err(|e| format!("Failed to read SSZ file: {:?}", e))?;

        let genesis_state = BeaconState::from_ssz_bytes(&bytes)
            .map_err(|e| format!("Unable to parse SSZ genesis state file: {:?}", e))?;

        Ok(Self::from_genesis_state(genesis_state, spec, log))
    }

    pub fn json_state(file: &PathBuf, spec: ChainSpec, log: Logger) -> Result<Self, String> {
        let file = File::open(file.clone())
            .map_err(|e| format!("Unable to open JSON genesis state file {:?}: {:?}", file, e))?;

        let genesis_state = serde_json::from_reader(file)
            .map_err(|e| format!("Unable to parse JSON genesis state file: {:?}", e))?;

        Ok(Self::from_genesis_state(genesis_state, spec, log))
    }

    pub fn http_bootstrap(server: &str, spec: ChainSpec, log: Logger) -> Result<Self, String> {
        let bootstrapper = Bootstrapper::from_server_string(server.to_string())
            .map_err(|e| format!("Failed to initialize bootstrap client: {}", e))?;

        let (genesis_state, genesis_block) = bootstrapper
            .genesis()
            .map_err(|e| format!("Failed to bootstrap genesis state: {}", e))?;

        Ok(Self {
            build_strategy: BuildStrategy::FromGenesis {
                genesis_block: Box::new(genesis_block),
                genesis_state: Box::new(genesis_state),
            },
            spec,
            log,
        })
    }

    fn from_genesis_state(
        genesis_state: BeaconState<T::EthSpec>,
        spec: ChainSpec,
        log: Logger,
    ) -> Self {
        Self {
            build_strategy: BuildStrategy::FromGenesis {
                genesis_block: Box::new(genesis_block(&genesis_state, &spec)),
                genesis_state: Box::new(genesis_state),
            },
            spec,
            log,
        }
    }

    pub fn from_store(spec: ChainSpec, log: Logger) -> Self {
        Self {
            build_strategy: BuildStrategy::LoadFromStore,
            spec,
            log,
        }
    }

    pub fn build(self, store: Arc<T::Store>) -> Result<BeaconChain<T>, String> {
        Ok(match self.build_strategy {
            BuildStrategy::LoadFromStore => BeaconChain::from_store(store, self.spec, self.log)
                .map_err(|e| format!("Error loading BeaconChain from database: {:?}", e))?
                .ok_or_else(|| format!("Unable to find exising BeaconChain in database."))?,
            BuildStrategy::FromGenesis {
                genesis_block,
                genesis_state,
            } => BeaconChain::from_genesis(
                store,
                genesis_state.as_ref().clone(),
                genesis_block.as_ref().clone(),
                self.spec,
                self.log,
            )
            .map_err(|e| format!("Failed to initialize new beacon chain: {:?}", e))?,
        })
    }
}

fn genesis_block<T: EthSpec>(genesis_state: &BeaconState<T>, spec: &ChainSpec) -> BeaconBlock<T> {
    let mut genesis_block = BeaconBlock::empty(&spec);

    genesis_block.state_root = genesis_state.canonical_root();

    genesis_block
}

/// 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
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(&[0x42; 32]);
    let eth1_timestamp = 2_u64.pow(40);
    let amount = spec.max_effective_balance;

    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.
fn recent_genesis_time(minutes: u64) -> u64 {
    let now = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs();
    let secs_after_last_period = now.checked_rem(minutes * 60).unwrap_or(0);
    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(&[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.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"
        );
    }
}
<|endoftext|>


<commit_before>extern crate "parser-combinators" as parser_combinators;
use self::parser_combinators::{between, spaces, many1, sep_by, satisfy, Parser, ParserExt, ParseResult};
use self::parser_combinators::primitives::{State, Stream};
use ast;

<commit_msg>Start on parsing with parser_combinators<commit_after>extern crate "parser-combinators" as parser_combinators;
use self::parser_combinators::{between, spaces, many1, sep_by, satisfy, Parser, ParserExt, ParseResult};
use self::parser_combinators::primitives::{State, Stream};
use ast;

fn expr<I>(input: State<I>) -> ParseResult(ast::ExprNode, I)
    where I: Stream<Item=char> {
        unimplemented!()
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_basic_ident() {
        assert_eq!(
            (expr as fn (_) -> _).parse("ident"),
            Ok((ast::NameNode { name: "ident" }, ""))
            );
    }

}<|endoftext|>


<commit_before>#![allow(clippy::write_literal)]

mod c;
mod csharp;
mod java;
mod jni_cpp;
mod kotlin;
mod node;
mod python;
mod ruby;
mod swift;
mod typescript;
mod wasm;
mod wasm_bindgen;

use std::fs::{create_dir_all, remove_dir_all, File};
use std::io::{BufWriter, Read, Result};
use std::path::PathBuf;
use std::rc::Rc;
use structopt::StructOpt;
use syn::{
    parse_file, FnArg, Item, ItemFn, Lit, Meta, Pat, ReturnType, Signature, Type as SynType,
    Visibility,
};

#[derive(StructOpt)]
#[structopt(about = "Generates bindings for livesplit-core")]
pub struct Opt {
    #[structopt(
        long = "ruby-lib-path",
        help = "The path of the library for the Ruby bindings",
        default_value = "../liblivesplit_core.so"
    )]
    ruby_lib_path: String,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TypeKind {
    Value,
    Ref,
    RefMut,
}

#[derive(Debug)]
pub struct Type {
    kind: TypeKind,
    is_custom: bool,
    is_nullable: bool,
    name: String,
}

#[derive(Debug)]
pub struct Function {
    name: String,
    class: String,
    method: String,
    inputs: Vec<(String, Type)>,
    output: Type,
    comments: Vec<String>,
    class_comments: Rc<Vec<String>>,
}

impl Function {
    fn is_static(&self) -> bool {
        if let Some((name, _)) = self.inputs.get(0) {
            name != "this"
        } else {
            true
        }
    }

    fn has_return_type(&self) -> bool {
        self.output.name != "()"
    }
}

#[derive(Debug, Default)]
pub struct Class {
    comments: Rc<Vec<String>>,
    static_fns: Vec<Function>,
    shared_fns: Vec<Function>,
    mut_fns: Vec<Function>,
    own_fns: Vec<Function>,
}

fn get_type(ty: &SynType) -> Type {
    match ty {
        SynType::Reference(reference) => {
            let mut ty = get_type(&reference.elem);
            ty.kind = if reference.mutability.is_some() {
                TypeKind::RefMut
            } else {
                TypeKind::Ref
            };
            ty
        }
        SynType::Ptr(ptr) => {
            let mut ty = get_type(&ptr.elem);
            ty.kind = if ptr.mutability.is_some() {
                TypeKind::RefMut
            } else {
                TypeKind::Ref
            };
            ty
        }
        SynType::Path(path) => {
            if let Some(segment) = path.path.segments.iter().last() {
                let mut name = segment.ident.to_string();
                let is_nullable = if let Some(rest) = name.strip_prefix("Nullable") {
                    name = rest.to_string();
                    true
                } else {
                    false
                };

                if let Some(rest) = name.strip_prefix("Owned") {
                    name = rest.to_string();
                }
                if name == "TimingMethod" || name == "TimerPhase" {
                    name = String::from("u8");
                }
                let is_custom = match &name as &str {
                    "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "()" | "bool"
                    | "c_char" | "usize" | "isize" | "f32" | "f64" | "Json" => false,
                    _ => true,
                };
                Type {
                    kind: TypeKind::Value,
                    is_custom,
                    is_nullable,
                    name,
                }
            } else {
                panic!("Weird path")
            }
        }
        _ => panic!("Weird type"),
    }
}

fn main() {
    let opt = Opt::from_args();

    let mut contents = String::new();
    File::open("../src/lib.rs")
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();

    let file = parse_file(&contents).unwrap();

    let mut functions = Vec::new();

    for item in &file.items {
        if let Item::Mod(module) = item {
            contents.clear();
            File::open(format!("../src/{}.rs", module.ident))
                .unwrap()
                .read_to_string(&mut contents)
                .unwrap();
            let file = parse_file(&contents).unwrap();

            let class_comments = Rc::new(
                file.attrs
                    .iter()
                    .filter_map(|a| match a.parse_meta() {
                        Ok(Meta::NameValue(v)) => Some(v),
                        _ => None,
                    })
                    .filter(|m| m.path.is_ident("doc"))
                    .filter_map(|m| match m.lit {
                        Lit::Str(s) => Some(s.value().trim().to_string()),
                        _ => None,
                    })
                    .collect::<Vec<_>>(),
            );

            for item in &file.items {
                if let Item::Fn(ItemFn {
                    vis: Visibility::Public(_),
                    attrs,
                    sig:
                        Signature {
                            abi,
                            ident,
                            inputs,
                            output,
                            ..
                        },
                    ..
                }) = item
                {
                    if abi
                        .as_ref()
                        .and_then(|a| a.name.as_ref())
                        .map_or(false, |n| n.value() == "C")
                        && attrs
                            .iter()
                            .filter_map(|a| match a.parse_meta() {
                                Ok(Meta::Path(w)) => Some(w),
                                _ => None,
                            })
                            .any(|w| w.is_ident("no_mangle"))
                    {
                        let comments = attrs
                            .iter()
                            .filter_map(|a| match a.parse_meta() {
                                Ok(Meta::NameValue(v)) => Some(v),
                                _ => None,
                            })
                            .filter(|m| m.path.is_ident("doc"))
                            .filter_map(|m| match m.lit {
                                Lit::Str(s) => Some(s.value().trim().to_string()),
                                _ => None,
                            })
                            .collect();

                        let output = if let ReturnType::Type(_, ty) = output {
                            get_type(ty)
                        } else {
                            Type {
                                kind: TypeKind::Value,
                                is_custom: false,
                                is_nullable: false,
                                name: String::from("()"),
                            }
                        };

                        let inputs = inputs
                            .iter()
                            .map(|i| {
                                let c = match i {
                                    FnArg::Typed(c) => c,
                                    _ => panic!("Found a weird fn argument"),
                                };
                                let name = if let Pat::Ident(ident) = &*c.pat {
                                    ident.ident.to_string()
                                } else {
                                    String::from("parameter")
                                };
                                (name, get_type(&c.ty))
                            })
                            .collect();

                        let name = ident.to_string();
                        let class;
                        let method;
                        {
                            let mut splits = name.splitn(2, '_');
                            class = splits.next().unwrap().to_string();
                            method = splits.next().unwrap().to_string();
                        }

                        functions.push(Function {
                            name,
                            class,
                            method,
                            output,
                            inputs,
                            comments,
                            class_comments: class_comments.clone(),
                        });
                    }
                }
            }
        }
    }

    write_files(&fns_to_classes(functions), &opt).unwrap();
}

use std::collections::BTreeMap;

fn fns_to_classes(functions: Vec<Function>) -> BTreeMap<String, Class> {
    let mut classes = BTreeMap::new();

    for function in functions {
        let class = classes
            .entry(function.class.clone())
            .or_insert_with(Class::default);

        class.comments = function.class_comments.clone();

        let kind = if let Some((name, ty)) = function.inputs.get(0) {
            if name == "this" {
                Some(ty.kind)
            } else {
                None
            }
        } else {
            None
        };
        match kind {
            Some(TypeKind::Value) => class.own_fns.push(function),
            Some(TypeKind::Ref) => class.shared_fns.push(function),
            Some(TypeKind::RefMut) => class.mut_fns.push(function),
            None => class.static_fns.push(function),
        }
    }

    classes
}

fn write_files(classes: &BTreeMap<String, Class>, opt: &Opt) -> Result<()> {
    let mut path = PathBuf::from("..");
    path.push("bindings");

    remove_dir_all(&path).ok();
    create_dir_all(&path)?;

    path.push("node");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        node::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        node::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("wasm");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        wasm::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        wasm::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("wasm_bindgen");
    create_dir_all(&path)?;
    {
        path.push("index.js");
        wasm_bindgen::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("index.ts");
        wasm_bindgen::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("LiveSplitCore.cs");
    csharp::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("java");
    create_dir_all(&path)?;
    java::write(&path, classes)?;
    path.pop();

    path.push("kotlin");
    create_dir_all(&path)?;
    kotlin::write(&path, classes)?;
    path.pop();

    path.push("LiveSplitCore.rb");
    ruby::write(BufWriter::new(File::create(&path)?), classes, opt)?;
    path.pop();

    path.push("livesplit_core.h");
    c::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("livesplit_core.py");
    python::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("swift");
    create_dir_all(&path)?;
    swift::write(&path, classes)?;
    path.pop();

    Ok(())
}
<commit_msg>Clean up `capi/bind_gen/main`<commit_after>#![allow(clippy::write_literal)]

mod c;
mod csharp;
mod java;
mod jni_cpp;
mod kotlin;
mod node;
mod python;
mod ruby;
mod swift;
mod typescript;
mod wasm;
mod wasm_bindgen;

use std::collections::BTreeMap;
use std::fs::{self, create_dir_all, remove_dir_all, File};
use std::io::{BufWriter, Read, Result};
use std::path::PathBuf;
use std::rc::Rc;
use structopt::StructOpt;
use syn::{
    parse_file, FnArg, Item, ItemFn, Lit, Meta, Pat, ReturnType, Signature, Type as SynType,
    Visibility,
};

#[derive(StructOpt)]
#[structopt(about = "Generates bindings for livesplit-core")]
pub struct Opt {
    #[structopt(
        long = "ruby-lib-path",
        help = "The path of the library for the Ruby bindings",
        default_value = "../liblivesplit_core.so"
    )]
    ruby_lib_path: String,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TypeKind {
    Value,
    Ref,
    RefMut,
}

#[derive(Debug)]
pub struct Type {
    kind: TypeKind,
    is_custom: bool,
    is_nullable: bool,
    name: String,
}

#[derive(Debug)]
pub struct Function {
    name: String,
    class: String,
    method: String,
    inputs: Vec<(String, Type)>,
    output: Type,
    comments: Vec<String>,
    class_comments: Rc<Vec<String>>,
}

impl Function {
    fn is_static(&self) -> bool {
        if let Some((name, _)) = self.inputs.get(0) {
            name != "this"
        } else {
            true
        }
    }

    fn has_return_type(&self) -> bool {
        self.output.name != "()"
    }
}

#[derive(Debug, Default)]
pub struct Class {
    comments: Rc<Vec<String>>,
    static_fns: Vec<Function>,
    shared_fns: Vec<Function>,
    mut_fns: Vec<Function>,
    own_fns: Vec<Function>,
}

fn get_type(ty: &SynType) -> Type {
    match ty {
        SynType::Reference(reference) => {
            let mut ty = get_type(&reference.elem);
            ty.kind = if reference.mutability.is_some() {
                TypeKind::RefMut
            } else {
                TypeKind::Ref
            };
            ty
        }
        SynType::Ptr(ptr) => {
            let mut ty = get_type(&ptr.elem);
            ty.kind = if ptr.mutability.is_some() {
                TypeKind::RefMut
            } else {
                TypeKind::Ref
            };
            ty
        }
        SynType::Path(path) => {
            let segment = path.path.segments.iter().last().expect("Weird path");
            let mut name = segment.ident.to_string();
            let is_nullable = if let Some(rest) = name.strip_prefix("Nullable") {
                name = rest.to_string();
                true
            } else {
                false
            };

            if let Some(rest) = name.strip_prefix("Owned") {
                name = rest.to_string();
            }
            if name == "TimingMethod" || name == "TimerPhase" {
                name.clear();
                name += "u8";
            }
            let is_custom = match &name as &str {
                "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "()" | "bool"
                | "c_char" | "usize" | "isize" | "f32" | "f64" | "Json" => false,
                _ => true,
            };
            Type {
                kind: TypeKind::Value,
                is_custom,
                is_nullable,
                name,
            }
        }
        _ => panic!("Weird type"),
    }
}

fn get_comment(attrs: &Vec<syn::Attribute>) -> Vec<String> {
    attrs
        .iter()
        .filter_map(|a| match a.parse_meta() {
            Ok(Meta::NameValue(v)) if v.path.is_ident("doc") => Some(v),
            _ => None,
        })
        .filter_map(|m| match m.lit {
            Lit::Str(s) => Some(s.value().trim().to_string()),
            _ => None,
        })
        .collect()
}

fn main() {
    let opt = Opt::from_args();

    let mut contents = fs::read_to_string("../src/lib.rs").unwrap();
    let file = parse_file(&contents).unwrap();

    let mut functions = Vec::new();

    for item in &file.items {
        let module = match item {
            Item::Mod(m) => m,
            _ => continue,
        };

        contents.clear();
        File::open(format!("../src/{}.rs", module.ident))
            .unwrap()
            .read_to_string(&mut contents)
            .unwrap();
        let file = parse_file(&contents).unwrap();

        let class_comments = Rc::new(get_comment(&file.attrs));

        for item in &file.items {
            let ItemFn {
                attrs,
                sig:
                    Signature {
                        abi,
                        ident,
                        inputs,
                        output,
                        ..
                    },
                ..
            } = match item {
                Item::Fn(i) if matches!(i.vis, Visibility::Public(_)) => i,
                _ => continue,
            };

            if abi
                .as_ref()
                .and_then(|a| a.name.as_ref())
                .map_or(true, |n| n.value() != "C")
                || attrs.iter().all(|a| match a.parse_meta() {
                    Ok(Meta::Path(w)) => !w.is_ident("no_mangle"),
                    _ => true,
                })
            {
                // Not `extern "C"` or not `#[no_mangle]`.
                continue;
            }

            let comments = get_comment(&attrs);

            let output = if let ReturnType::Type(_, ty) = output {
                get_type(ty)
            } else {
                Type {
                    kind: TypeKind::Value,
                    is_custom: false,
                    is_nullable: false,
                    name: String::from("()"),
                }
            };

            let inputs = inputs
                .iter()
                .map(|i| match i {
                    FnArg::Typed(c) => c,
                    _ => panic!("Found a weird fn argument"),
                })
                .map(|c| {
                    let name = match &*c.pat {
                        Pat::Ident(ident) => ident.ident.to_string(),
                        _ => String::from("parameter"),
                    };
                    (name, get_type(&c.ty))
                })
                .collect();

            let name = ident.to_string();
            let (class, method) = name.split_once('_').unwrap();
            let class = class.to_string();
            let method = method.to_string();

            functions.push(Function {
                name,
                class,
                method,
                output,
                inputs,
                comments,
                class_comments: class_comments.clone(),
            });
        }
    }

    write_files(&fns_to_classes(functions), &opt).unwrap();
}

fn fns_to_classes(functions: Vec<Function>) -> BTreeMap<String, Class> {
    let mut classes: BTreeMap<String, Class> = BTreeMap::new();

    for function in functions {
        let class = classes.entry(function.class.clone()).or_default();

        class.comments = function.class_comments.clone();

        match function.inputs.get(0) {
            Some((name, ty)) if name == "this" => match ty.kind {
                TypeKind::Value => class.own_fns.push(function),
                TypeKind::Ref => class.shared_fns.push(function),
                TypeKind::RefMut => class.mut_fns.push(function),
            },
            _ => class.static_fns.push(function),
        }
    }

    classes
}

fn write_files(classes: &BTreeMap<String, Class>, opt: &Opt) -> Result<()> {
    let mut path = PathBuf::from("..");
    path.push("bindings");

    drop(remove_dir_all(&path));
    create_dir_all(&path)?;

    path.push("node");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        node::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        node::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("wasm");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        wasm::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        wasm::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("wasm_bindgen");
    create_dir_all(&path)?;
    {
        path.push("index.js");
        wasm_bindgen::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("index.ts");
        wasm_bindgen::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("LiveSplitCore.cs");
    csharp::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("java");
    create_dir_all(&path)?;
    java::write(&path, classes)?;
    path.pop();

    path.push("kotlin");
    create_dir_all(&path)?;
    kotlin::write(&path, classes)?;
    path.pop();

    path.push("LiveSplitCore.rb");
    ruby::write(BufWriter::new(File::create(&path)?), classes, opt)?;
    path.pop();

    path.push("livesplit_core.h");
    c::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("livesplit_core.py");
    python::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("swift");
    create_dir_all(&path)?;
    swift::write(&path, classes)?;
    path.pop();

    Ok(())
}
<|endoftext|>