<commit_before>//
// Copyright 2020 The Project Oak Authors
//
// 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.
//

use crate::{
    io::Receiver,
    node::{
        grpc::{codec::VecCodec, from_abi_status, from_tonic_status, invocation::Invocation},
        ConfigurationError, Node,
    },
    runtime::RuntimeProxy,
};
use log::{debug, error, info};
use oak_abi::{
    proto::oak::{
        application::GrpcClientConfiguration,
        encap::{GrpcRequest, GrpcResponse},
    },
    Handle, OakStatus,
};
use tokio::sync::oneshot;
use tonic::transport::{Certificate, Channel, ClientTlsConfig, Uri};

/// Struct that represents a gRPC client pseudo-Node.
pub struct GrpcClientNode {
    /// Pseudo-Node name.
    node_name: String,
    /// Handler that sends gRPC requests to an external gRPC service and returns gRPC responses.
    handler: GrpcRequestHandler,
}

/// Checks if URI contains the "Host" element.
fn check_uri(uri: &Uri) -> Result<(), ConfigurationError> {
    uri.authority()
        .filter(|authority| !authority.host().is_empty())
        .map(|_| ())
        .ok_or(ConfigurationError::NoHostElement)
}

impl GrpcClientNode {
    /// Creates a new [`GrpcClientNode`] instance, but does not start it.
    pub fn new(
        node_name: &str,
        config: GrpcClientConfiguration,
        root_tls_certificate: Certificate,
    ) -> Result<Self, ConfigurationError> {
        let uri = config.uri.parse().map_err(|error| {
            error!("Error parsing URI {}: {:?}", config.uri, error);
            ConfigurationError::IncorrectURI
        })?;
        check_uri(&uri)?;
        Ok(Self {
            node_name: node_name.to_string(),
            handler: GrpcRequestHandler::new(uri, root_tls_certificate),
        })
    }

    /// Main loop that handles gRPC invocations from the `handle`, sends gRPC requests to an
    /// external gRPC service and writes gRPC responses back to the invocation channel.
    async fn handle_loop(
        &mut self,
        runtime: RuntimeProxy,
        handle: Handle,
    ) -> Result<(), OakStatus> {
        // Create a [`Receiver`] used for reading gRPC invocations.
        let receiver = Receiver::<Invocation>::new(handle);
        loop {
            debug!("Waiting for gRPC invocations");
            // Read a gRPC invocation from the [`Receiver`].
            let invocation = receiver.receive(&runtime).map_err(|error| {
                error!("Couldn't receive the invocation: {:?}", error);
                error
            })?;

            // Receive a request from the invocation channel.
            let request = invocation.receive_request(&runtime).map_err(|error| {
                error!(
                    "Couldn't receive gRPC request from the invocation: {:?}",
                    error
                );
                error
            })?;
            debug!("Incoming gRPC request: {:?}", request);

            // Send an unary request to an external gRPC service and wait for the response.
            let response = self
                .handler
                .unary_request(request)
                .await
                .unwrap_or_else(|error| {
                    error!("Couldn't send gRPC request: {:?}", error);
                    GrpcResponse {
                        rsp_msg: vec![],
                        status: Some(from_abi_status(error)),
                        last: true,
                    }
                });

            // Send a response back to the invocation channel.
            debug!("Sending gRPC response: {:?}", response);
            invocation
                .send_response(response, &runtime)
                .map_err(|error| {
                    error!("Couldn't send gRPC response to the invocation: {:?}", error);
                    error
                })?;
        }
    }
}

/// Oak Node implementation for the gRPC client pseudo-Node.
impl Node for GrpcClientNode {
    fn run(
        mut self: Box<Self>,
        runtime: RuntimeProxy,
        handle: Handle,
        notify_receiver: oneshot::Receiver<()>,
    ) {
        // Create an Async runtime for executing futures.
        // https://docs.rs/tokio/
        let mut async_runtime = tokio::runtime::Builder::new()
            // Use simple scheduler that runs all tasks on the current-thread.
            // https://docs.rs/tokio/0.2.16/tokio/runtime/index.html#basic-scheduler
            .basic_scheduler()
            // Enables the I/O driver.
            // Necessary for using net, process, signal, and I/O types on the Tokio runtime.
            .enable_io()
            // Enables the time driver.
            // Necessary for creating a Tokio Runtime.
            .enable_time()
            .build()
            .expect("Couldn't create an Async runtime");

        // Listen to incoming gRPC invocations.
        info!(
            "{}: Starting gRPC client pseudo-Node thread",
            self.node_name
        );
        async_runtime.block_on(futures::future::select(
            Box::pin(self.handle_loop(runtime, handle)),
            notify_receiver,
        ));
        info!("{}: Exiting gRPC client pseudo-Node thread", self.node_name);
    }
}

/// Sends gRPC requests to an external gRPC service and returns gRPC responses.
struct GrpcRequestHandler {
    /// The URI component of a gRPC server endpoint. Must contain the "Host" element.
    /// https://docs.rs/tonic/0.2.1/tonic/transport/struct.Uri.html
    uri: Uri,
    /// Loaded PEM encoded X.509 TLS root certificate file used to authenticate an external gRPC
    /// service.
    root_tls_certificate: Certificate,
    /// A gRPC client dispatcher that wraps a gRPC connection and encodes/decodes messages via a
    /// provided codec. The value is assigned using [`GrpcRequestHandler::connect`] upon receiving
    /// the first gRPC request.
    dispatcher: Option<tonic::client::Grpc<tonic::transport::channel::Channel>>,
}

impl GrpcRequestHandler {
    fn new(uri: Uri, root_tls_certificate: Certificate) -> Self {
        Self {
            uri,
            root_tls_certificate,
            dispatcher: None,
        }
    }

    /// Creates a TLS connection to an external gRPC service.
    async fn connect(&mut self) -> Result<(), OakStatus> {
        debug!("Connecting to {}", self.uri);

        // Create a TLS configuration.
        let tls_config = ClientTlsConfig::new().ca_certificate(self.root_tls_certificate.clone());

        // Connect to a remote gRPC service.
        let connection = Channel::builder(self.uri.clone())
            .tls_config(tls_config)
            .connect()
            .await
            .map_err(|error| {
                error!("Couldn't connect to {}: {:?}", self.uri, error);
                OakStatus::ErrInternal
            })?;

        self.dispatcher = Some(tonic::client::Grpc::new(connection));
        debug!("Connected to {}", self.uri);
        Ok(())
    }

    /// Checks whether the [`GrpcRequestHandler::dispatcher`] is able to accept new requests.
    async fn ready(&mut self) -> Result<(), OakStatus> {
        self.dispatcher
            .as_mut()
            .expect("Handler is not connected to a gRPC service")
            .ready()
            .await
            .map_err(|error| {
                error!("Service was not ready: {}", error);
                OakStatus::ErrInternal
            })?;
        Ok(())
    }

    /// Sends an unary gRPC request wrapped in [`GrpcResponse`] to an external gRPC service.
    async fn unary_request(&mut self, request: GrpcRequest) -> Result<GrpcResponse, OakStatus> {
        // Connect to an external gRPC service.
        self.connect().await?;
        self.ready().await?;

        let codec = VecCodec::default();
        let path = request.method_name.parse().map_err(|error| {
            error!("Invalid URI {}: {}", request.method_name, error);
            OakStatus::ErrInternal
        })?;

        // Send gRPC request and wait for the response.
        self.dispatcher
            .as_mut()
            .expect("Handler is not connected to a gRPC service")
            .unary(tonic::Request::new(request.req_msg), path, codec)
            .await
            .map(|response| GrpcResponse {
                rsp_msg: response.into_inner(),
                status: Some(from_tonic_status(tonic::Status::ok(""))),
                last: true,
            })
            .or_else(|status| {
                Ok(GrpcResponse {
                    rsp_msg: vec![],
                    status: Some(from_tonic_status(status)),
                    last: true,
                })
            })
    }
}
<commit_msg>runtime: fold GrpcRequestHandler into GrpcClientNode<commit_after>//
// Copyright 2020 The Project Oak Authors
//
// 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.
//

use crate::{
    io::Receiver,
    node::{
        grpc::{codec::VecCodec, from_abi_status, from_tonic_status, invocation::Invocation},
        ConfigurationError, Node,
    },
    runtime::RuntimeProxy,
};
use log::{debug, error, info};
use oak_abi::{
    proto::oak::{
        application::GrpcClientConfiguration,
        encap::{GrpcRequest, GrpcResponse},
    },
    Handle, OakStatus,
};
use tokio::sync::oneshot;
use tonic::transport::{Certificate, Channel, ClientTlsConfig, Uri};

/// Struct that represents a gRPC client pseudo-Node.
pub struct GrpcClientNode {
    /// Pseudo-Node name.
    node_name: String,
    /// The URI component of a gRPC server endpoint. Must contain the "Host" element.
    /// https://docs.rs/tonic/0.2.1/tonic/transport/struct.Uri.html
    uri: Uri,
    /// Loaded PEM encoded X.509 TLS root certificate file used to authenticate an external gRPC
    /// service.
    root_tls_certificate: Certificate,
    /// A gRPC client dispatcher that wraps a gRPC connection and encodes/decodes messages via a
    /// provided codec. The value is assigned using [`GrpcClientNode::connect`] upon receiving
    /// the first gRPC request.
    dispatcher: Option<tonic::client::Grpc<tonic::transport::channel::Channel>>,
}

/// Checks if URI contains the "Host" element.
fn check_uri(uri: &Uri) -> Result<(), ConfigurationError> {
    uri.authority()
        .filter(|authority| !authority.host().is_empty())
        .map(|_| ())
        .ok_or(ConfigurationError::NoHostElement)
}

impl GrpcClientNode {
    /// Creates a new [`GrpcClientNode`] instance, but does not start it.
    pub fn new(
        node_name: &str,
        config: GrpcClientConfiguration,
        root_tls_certificate: Certificate,
    ) -> Result<Self, ConfigurationError> {
        let uri = config.uri.parse().map_err(|error| {
            error!("Error parsing URI {}: {:?}", config.uri, error);
            ConfigurationError::IncorrectURI
        })?;
        check_uri(&uri)?;
        Ok(Self {
            node_name: node_name.to_string(),
            uri,
            root_tls_certificate,
            dispatcher: None,
        })
    }

    /// Main loop that handles gRPC invocations from the `handle`, sends gRPC requests to an
    /// external gRPC service and writes gRPC responses back to the invocation channel.
    async fn handle_loop(
        &mut self,
        runtime: RuntimeProxy,
        handle: Handle,
    ) -> Result<(), OakStatus> {
        // Create a [`Receiver`] used for reading gRPC invocations.
        let receiver = Receiver::<Invocation>::new(handle);
        loop {
            debug!("Waiting for gRPC invocations");
            // Read a gRPC invocation from the [`Receiver`].
            let invocation = receiver.receive(&runtime).map_err(|error| {
                error!("Couldn't receive the invocation: {:?}", error);
                error
            })?;

            // Receive a request from the invocation channel.
            let request = invocation.receive_request(&runtime).map_err(|error| {
                error!(
                    "Couldn't receive gRPC request from the invocation: {:?}",
                    error
                );
                error
            })?;
            debug!("Incoming gRPC request: {:?}", request);

            // Send an unary request to an external gRPC service and wait for the response.
            let response = self.unary_request(request).await.unwrap_or_else(|error| {
                error!("Couldn't send gRPC request: {:?}", error);
                GrpcResponse {
                    rsp_msg: vec![],
                    status: Some(from_abi_status(error)),
                    last: true,
                }
            });

            // Send a response back to the invocation channel.
            debug!("Sending gRPC response: {:?}", response);
            invocation
                .send_response(response, &runtime)
                .map_err(|error| {
                    error!("Couldn't send gRPC response to the invocation: {:?}", error);
                    error
                })?;
        }
    }

    /// Creates a TLS connection to an external gRPC service.
    async fn connect(&mut self) -> Result<(), OakStatus> {
        debug!("Connecting to {}", self.uri);

        // Create a TLS configuration.
        let tls_config = ClientTlsConfig::new().ca_certificate(self.root_tls_certificate.clone());

        // Connect to a remote gRPC service.
        let connection = Channel::builder(self.uri.clone())
            .tls_config(tls_config)
            .connect()
            .await
            .map_err(|error| {
                error!("Couldn't connect to {}: {:?}", self.uri, error);
                OakStatus::ErrInternal
            })?;

        self.dispatcher = Some(tonic::client::Grpc::new(connection));
        debug!("Connected to {}", self.uri);
        Ok(())
    }

    /// Checks whether the [`GrpcRequestHandler::dispatcher`] is able to accept new requests.
    async fn ready(&mut self) -> Result<(), OakStatus> {
        self.dispatcher
            .as_mut()
            .expect("Handler is not connected to a gRPC service")
            .ready()
            .await
            .map_err(|error| {
                error!("Service was not ready: {}", error);
                OakStatus::ErrInternal
            })?;
        Ok(())
    }

    /// Sends an unary gRPC request wrapped in [`GrpcResponse`] to an external gRPC service.
    async fn unary_request(&mut self, request: GrpcRequest) -> Result<GrpcResponse, OakStatus> {
        // Connect to an external gRPC service.
        self.connect().await?;
        self.ready().await?;

        let codec = VecCodec::default();
        let path = request.method_name.parse().map_err(|error| {
            error!("Invalid URI {}: {}", request.method_name, error);
            OakStatus::ErrInternal
        })?;

        // Send gRPC request and wait for the response.
        self.dispatcher
            .as_mut()
            .expect("Handler is not connected to a gRPC service")
            .unary(tonic::Request::new(request.req_msg), path, codec)
            .await
            .map(|response| GrpcResponse {
                rsp_msg: response.into_inner(),
                status: Some(from_tonic_status(tonic::Status::ok(""))),
                last: true,
            })
            .or_else(|status| {
                Ok(GrpcResponse {
                    rsp_msg: vec![],
                    status: Some(from_tonic_status(status)),
                    last: true,
                })
            })
    }
}

/// Oak Node implementation for the gRPC client pseudo-Node.
impl Node for GrpcClientNode {
    fn run(
        mut self: Box<Self>,
        runtime: RuntimeProxy,
        handle: Handle,
        notify_receiver: oneshot::Receiver<()>,
    ) {
        // Create an Async runtime for executing futures.
        // https://docs.rs/tokio/
        let mut async_runtime = tokio::runtime::Builder::new()
            // Use simple scheduler that runs all tasks on the current-thread.
            // https://docs.rs/tokio/0.2.16/tokio/runtime/index.html#basic-scheduler
            .basic_scheduler()
            // Enables the I/O driver.
            // Necessary for using net, process, signal, and I/O types on the Tokio runtime.
            .enable_io()
            // Enables the time driver.
            // Necessary for creating a Tokio Runtime.
            .enable_time()
            .build()
            .expect("Couldn't create an Async runtime");

        // Listen to incoming gRPC invocations.
        info!(
            "{}: Starting gRPC client pseudo-Node thread",
            self.node_name
        );
        async_runtime.block_on(futures::future::select(
            Box::pin(self.handle_loop(runtime, handle)),
            notify_receiver,
        ));
        info!("{}: Exiting gRPC client pseudo-Node thread", self.node_name);
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_map::HashMap;
use std::str;
use std::sync::Arc;

use regex::Regex;

use core::{SourceMap, Package, PackageId, PackageSet, Resolve, Target, Profile};
use util::{self, CargoResult, ChainError, internal, Config, profile};
use util::human;

use super::TargetConfig;
use super::custom_build::BuildState;
use super::fingerprint::Fingerprint;
use super::layout::{Layout, LayoutProxy};
use super::{Kind, Compilation, BuildConfig};
use super::{ProcessEngine, ExecEngine};

#[derive(Debug, Copy)]
pub enum Platform {
    Target,
    Plugin,
    PluginAndTarget,
}

pub struct Context<'a, 'b: 'a> {
    pub config: &'a Config<'b>,
    pub resolve: &'a Resolve,
    pub sources: &'a SourceMap<'a>,
    pub compilation: Compilation,
    pub build_state: Arc<BuildState>,
    pub exec_engine: Arc<Box<ExecEngine>>,
    pub fingerprints: HashMap<(&'a PackageId, &'a Target, Kind), Fingerprint>,

    env: &'a str,
    host: Layout,
    target: Option<Layout>,
    target_triple: String,
    host_dylib: Option<(String, String)>,
    host_exe: String,
    package_set: &'a PackageSet,
    target_dylib: Option<(String, String)>,
    target_exe: String,
    requirements: HashMap<(&'a PackageId, &'a str), Platform>,
    build_config: BuildConfig,
}

impl<'a, 'b: 'a> Context<'a, 'b> {
    pub fn new(env: &'a str,
               resolve: &'a Resolve,
               sources: &'a SourceMap<'a>,
               deps: &'a PackageSet,
               config: &'a Config<'b>,
               host: Layout,
               target_layout: Option<Layout>,
               root_pkg: &Package,
               build_config: BuildConfig) -> CargoResult<Context<'a, 'b>> {
        let target = build_config.requested_target.clone();
        let target = target.as_ref().map(|s| &s[]);
        let (target_dylib, target_exe) = try!(Context::filename_parts(target));
        let (host_dylib, host_exe) = if build_config.requested_target.is_none() {
            (target_dylib.clone(), target_exe.clone())
        } else {
            try!(Context::filename_parts(None))
        };
        let target_triple = target.unwrap_or(config.rustc_host()).to_string();
        Ok(Context {
            target_triple: target_triple,
            env: env,
            host: host,
            target: target_layout,
            resolve: resolve,
            sources: sources,
            package_set: deps,
            config: config,
            target_dylib: target_dylib,
            target_exe: target_exe,
            host_dylib: host_dylib,
            host_exe: host_exe,
            requirements: HashMap::new(),
            compilation: Compilation::new(root_pkg),
            build_state: Arc::new(BuildState::new(build_config.clone(), deps)),
            build_config: build_config,
            exec_engine: Arc::new(Box::new(ProcessEngine) as Box<ExecEngine>),
            fingerprints: HashMap::new(),
        })
    }

    /// Run `rustc` to discover the dylib prefix/suffix for the target
    /// specified as well as the exe suffix
    fn filename_parts(target: Option<&str>)
                      -> CargoResult<(Option<(String, String)>, String)> {
        let process = try!(util::process("rustc"))
                           .arg("-")
                           .arg("--crate-name").arg("-")
                           .arg("--crate-type").arg("dylib")
                           .arg("--crate-type").arg("bin")
                           .arg("--print=file-names");
        let process = match target {
            Some(s) => process.arg("--target").arg(s),
            None => process,
        };
        let output = try!(process.exec_with_output());

        let error = str::from_utf8(&output.error).unwrap();
        let output = str::from_utf8(&output.output).unwrap();
        let mut lines = output.lines();
        let nodylib = Regex::new("unsupported crate type.*dylib").unwrap();
        let nobin = Regex::new("unsupported crate type.*bin").unwrap();
        let dylib = if nodylib.is_match(error) {
            None
        } else {
            let dylib_parts: Vec<&str> = lines.next().unwrap().trim()
                                              .split('-').collect();
            assert!(dylib_parts.len() == 2,
                    "rustc --print-file-name output has changed");
            Some((dylib_parts[0].to_string(), dylib_parts[1].to_string()))
        };

        let exe_suffix = if nobin.is_match(error) {
            String::new()
        } else {
            lines.next().unwrap().trim()
                 .split('-').skip(1).next().unwrap().to_string()
        };
        Ok((dylib, exe_suffix.to_string()))
    }

    /// Prepare this context, ensuring that all filesystem directories are in
    /// place.
    pub fn prepare(&mut self, pkg: &'a Package) -> CargoResult<()> {
        let _p = profile::start("preparing layout");

        try!(self.host.prepare().chain_error(|| {
            internal(format!("couldn't prepare build directories for `{}`",
                             pkg.name()))
        }));
        match self.target {
            Some(ref mut target) => {
                try!(target.prepare().chain_error(|| {
                    internal(format!("couldn't prepare build directories \
                                      for `{}`", pkg.name()))
                }));
            }
            None => {}
        }

        let targets = pkg.targets().iter();
        for target in targets.filter(|t| t.profile().is_compile()) {
            self.build_requirements(pkg, target, Platform::Target);
        }

        let jobs = self.jobs();
        self.compilation.extra_env.insert("NUM_JOBS".to_string(),
                                          Some(jobs.to_string()));
        self.compilation.root_output =
                self.layout(pkg, Kind::Target).proxy().dest().clone();
        self.compilation.deps_output =
                self.layout(pkg, Kind::Target).proxy().deps().clone();

        return Ok(());
    }

    fn build_requirements(&mut self, pkg: &'a Package, target: &'a Target,
                          req: Platform) {
        let req = if target.profile().is_for_host() {Platform::Plugin} else {req};
        match self.requirements.entry((pkg.package_id(), target.name())) {
            Occupied(mut entry) => match (*entry.get(), req) {
                (Platform::Plugin, Platform::Plugin) |
                (Platform::PluginAndTarget, Platform::Plugin) |
                (Platform::Target, Platform::Target) |
                (Platform::PluginAndTarget, Platform::Target) |
                (Platform::PluginAndTarget, Platform::PluginAndTarget) => return,
                _ => *entry.get_mut() = entry.get().combine(req),
            },
            Vacant(entry) => { entry.insert(req); }
        };

        for &(pkg, dep) in self.dep_targets(pkg, target).iter() {
            self.build_requirements(pkg, dep, req);
        }

        match pkg.targets().iter().find(|t| t.profile().is_custom_build()) {
            Some(custom_build) => {
                self.build_requirements(pkg, custom_build, Platform::Plugin);
            }
            None => {}
        }
    }

    pub fn get_requirement(&self, pkg: &'a Package,
                           target: &'a Target) -> Platform {
        let default = if target.profile().is_for_host() {
            Platform::Plugin
        } else {
            Platform::Target
        };
        self.requirements.get(&(pkg.package_id(), target.name()))
            .map(|a| *a).unwrap_or(default)
    }

    /// Returns the appropriate directory layout for either a plugin or not.
    pub fn layout(&self, pkg: &Package, kind: Kind) -> LayoutProxy {
        let primary = pkg.package_id() == self.resolve.root();
        match kind {
            Kind::Host => LayoutProxy::new(&self.host, primary),
            Kind::Target =>  LayoutProxy::new(self.target.as_ref()
                                                .unwrap_or(&self.host),
                                            primary),
        }
    }

    /// Returns the appropriate output directory for the specified package and
    /// target.
    pub fn out_dir(&self, pkg: &Package, kind: Kind, target: &Target) -> Path {
        let out_dir = self.layout(pkg, kind);
        if target.profile().is_custom_build() {
            out_dir.build(pkg)
        } else if target.is_example() {
            out_dir.examples().clone()
        } else {
            out_dir.root().clone()
        }
    }

    /// Return the (prefix, suffix) pair for dynamic libraries.
    ///
    /// If `plugin` is true, the pair corresponds to the host platform,
    /// otherwise it corresponds to the target platform.
    fn dylib(&self, kind: Kind) -> CargoResult<(&str, &str)> {
        let (triple, pair) = if kind == Kind::Host {
            (self.config.rustc_host(), &self.host_dylib)
        } else {
            (self.target_triple.as_slice(), &self.target_dylib)
        };
        match *pair {
            None => return Err(human(format!("dylib outputs are not supported \
                                              for {}", triple))),
            Some((ref s1, ref s2)) => Ok((s1, s2)),
        }
    }

    /// Return the target triple which this context is targeting.
    pub fn target_triple(&self) -> &str {
        &self.target_triple
    }

    /// Return the exact filename of the target.
    pub fn target_filenames(&self, target: &Target) -> CargoResult<Vec<String>> {
        let stem = target.file_stem();

        let mut ret = Vec::new();
        if target.is_example() || target.is_bin() ||
           target.profile().is_test() {
            ret.push(format!("{}{}", stem,
                             if target.profile().is_for_host() {
                                 &self.host_exe
                             } else {
                                 &self.target_exe
                             }));
        } else {
            if target.is_dylib() {
                let plugin = target.profile().is_for_host();
                let kind = if plugin {Kind::Host} else {Kind::Target};
                let (prefix, suffix) = try!(self.dylib(kind));
                ret.push(format!("{}{}{}", prefix, stem, suffix));
            }
            if target.is_rlib() {
                ret.push(format!("lib{}.rlib", stem));
            }
            if target.is_staticlib() {
                ret.push(format!("lib{}.a", stem));
            }
        }
        assert!(ret.len() > 0);
        return Ok(ret);
    }

    /// For a package, return all targets which are registered as dependencies
    /// for that package.
    pub fn dep_targets(&self, pkg: &Package, target: &Target)
                       -> Vec<(&'a Package, &'a Target)> {
        let deps = match self.resolve.deps(pkg.package_id()) {
            None => return vec!(),
            Some(deps) => deps,
        };
        deps.map(|id| self.get_package(id)).filter(|dep| {
            let pkg_dep = pkg.dependencies().iter().find(|d| {
                d.name() == dep.name()
            }).unwrap();

            // If this target is a build command, then we only want build
            // dependencies, otherwise we want everything *other than* build
            // dependencies.
            let is_correct_dep =
                target.profile().is_custom_build() == pkg_dep.is_build();

            // If this dependency is *not* a transitive dependency, then it
            // only applies to test/example targets
            let is_actual_dep = pkg_dep.is_transitive() ||
                                target.profile().is_test() ||
                                target.is_example();

            is_correct_dep && is_actual_dep
        }).filter_map(|pkg| {
            pkg.targets().iter().find(|&t| self.is_relevant_target(t))
               .map(|t| (pkg, t))
        }).collect()
    }

    /// Gets a package for the given package id.
    pub fn get_package(&self, id: &PackageId) -> &'a Package {
        self.package_set.iter()
            .find(|pkg| id == pkg.package_id())
            .expect("Should have found package")
    }

    pub fn env(&self) -> &str {
        // The "doc-all" environment just means to document everything (see
        // below), but we want to canonicalize that the the "doc" profile
        // environment, so do that here.
        if self.env == "doc-all" {"doc"} else {self.env}
    }

    pub fn is_relevant_target(&self, target: &Target) -> bool {
        target.is_lib() && match self.env {
            "doc" | "test" => target.profile().is_compile(),
            // doc-all == document everything, so look for doc targets and
            //            compile targets in dependencies
            "doc-all" => target.profile().is_compile() ||
                         (target.profile().env() == "doc" &&
                          target.profile().is_doc()),
            _ => target.profile().env() == self.env &&
                 !target.profile().is_test(),
        }
    }

    /// Get the user-specified linker for a particular host or target
    pub fn linker(&self, kind: Kind) -> Option<&str> {
        self.target_config(kind).linker.as_ref().map(|s| s.as_slice())
    }

    /// Get the user-specified `ar` program for a particular host or target
    pub fn ar(&self, kind: Kind) -> Option<&str> {
        self.target_config(kind).ar.as_ref().map(|s| s.as_slice())
    }

    /// Get the target configuration for a particular host or target
    fn target_config(&self, kind: Kind) -> &TargetConfig {
        match kind {
            Kind::Host => &self.build_config.host,
            Kind::Target => &self.build_config.target,
        }
    }

    /// Number of jobs specified for this build
    pub fn jobs(&self) -> u32 { self.build_config.jobs }

    /// Requested (not actual) target for the build
    pub fn requested_target(&self) -> Option<&str> {
        self.build_config.requested_target.as_ref().map(|s| &s[])
    }

    /// Calculate the actual profile to use for a target's compliation.
    ///
    /// This may involve overriding some options such as debug information,
    /// rpath, opt level, etc.
    pub fn profile(&self, target: &Target) -> Profile {
        let mut profile = target.profile().clone();
        let root_package = self.get_package(self.resolve.root());
        for target in root_package.manifest().targets().iter() {
            let root_profile = target.profile();
            if root_profile.env() != profile.env() { continue }
            profile = profile.set_opt_level(root_profile.opt_level())
                             .set_debug(root_profile.debug())
                             .set_rpath(root_profile.rpath())
        }
        profile
    }
}

impl Platform {
    pub fn combine(self, other: Platform) -> Platform {
        match (self, other) {
            (Platform::Target, Platform::Target) => Platform::Target,
            (Platform::Plugin, Platform::Plugin) => Platform::Plugin,
            _ => Platform::PluginAndTarget,
        }
    }

    pub fn includes(self, kind: Kind) -> bool {
        match (self, kind) {
            (Platform::PluginAndTarget, _) |
            (Platform::Target, Kind::Target) |
            (Platform::Plugin, Kind::Host) => true,
            _ => false,
        }
    }

    pub fn each_kind<F>(self, mut f: F) where F: FnMut(Kind) {
        match self {
            Platform::Target => f(Kind::Target),
            Platform::Plugin => f(Kind::Host),
            Platform::PluginAndTarget => { f(Kind::Target); f(Kind::Host); }
        }
    }
}
<commit_msg>Use an underscore instead of a hyphen as a dummy crate name<commit_after>use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_map::HashMap;
use std::str;
use std::sync::Arc;

use regex::Regex;

use core::{SourceMap, Package, PackageId, PackageSet, Resolve, Target, Profile};
use util::{self, CargoResult, ChainError, internal, Config, profile};
use util::human;

use super::TargetConfig;
use super::custom_build::BuildState;
use super::fingerprint::Fingerprint;
use super::layout::{Layout, LayoutProxy};
use super::{Kind, Compilation, BuildConfig};
use super::{ProcessEngine, ExecEngine};

#[derive(Debug, Copy)]
pub enum Platform {
    Target,
    Plugin,
    PluginAndTarget,
}

pub struct Context<'a, 'b: 'a> {
    pub config: &'a Config<'b>,
    pub resolve: &'a Resolve,
    pub sources: &'a SourceMap<'a>,
    pub compilation: Compilation,
    pub build_state: Arc<BuildState>,
    pub exec_engine: Arc<Box<ExecEngine>>,
    pub fingerprints: HashMap<(&'a PackageId, &'a Target, Kind), Fingerprint>,

    env: &'a str,
    host: Layout,
    target: Option<Layout>,
    target_triple: String,
    host_dylib: Option<(String, String)>,
    host_exe: String,
    package_set: &'a PackageSet,
    target_dylib: Option<(String, String)>,
    target_exe: String,
    requirements: HashMap<(&'a PackageId, &'a str), Platform>,
    build_config: BuildConfig,
}

impl<'a, 'b: 'a> Context<'a, 'b> {
    pub fn new(env: &'a str,
               resolve: &'a Resolve,
               sources: &'a SourceMap<'a>,
               deps: &'a PackageSet,
               config: &'a Config<'b>,
               host: Layout,
               target_layout: Option<Layout>,
               root_pkg: &Package,
               build_config: BuildConfig) -> CargoResult<Context<'a, 'b>> {
        let target = build_config.requested_target.clone();
        let target = target.as_ref().map(|s| &s[]);
        let (target_dylib, target_exe) = try!(Context::filename_parts(target));
        let (host_dylib, host_exe) = if build_config.requested_target.is_none() {
            (target_dylib.clone(), target_exe.clone())
        } else {
            try!(Context::filename_parts(None))
        };
        let target_triple = target.unwrap_or(config.rustc_host()).to_string();
        Ok(Context {
            target_triple: target_triple,
            env: env,
            host: host,
            target: target_layout,
            resolve: resolve,
            sources: sources,
            package_set: deps,
            config: config,
            target_dylib: target_dylib,
            target_exe: target_exe,
            host_dylib: host_dylib,
            host_exe: host_exe,
            requirements: HashMap::new(),
            compilation: Compilation::new(root_pkg),
            build_state: Arc::new(BuildState::new(build_config.clone(), deps)),
            build_config: build_config,
            exec_engine: Arc::new(Box::new(ProcessEngine) as Box<ExecEngine>),
            fingerprints: HashMap::new(),
        })
    }

    /// Run `rustc` to discover the dylib prefix/suffix for the target
    /// specified as well as the exe suffix
    fn filename_parts(target: Option<&str>)
                      -> CargoResult<(Option<(String, String)>, String)> {
        let process = try!(util::process("rustc"))
                           .arg("-")
                           .arg("--crate-name").arg("_")
                           .arg("--crate-type").arg("dylib")
                           .arg("--crate-type").arg("bin")
                           .arg("--print=file-names");
        let process = match target {
            Some(s) => process.arg("--target").arg(s),
            None => process,
        };
        let output = try!(process.exec_with_output());

        let error = str::from_utf8(&output.error).unwrap();
        let output = str::from_utf8(&output.output).unwrap();
        let mut lines = output.lines();
        let nodylib = Regex::new("unsupported crate type.*dylib").unwrap();
        let nobin = Regex::new("unsupported crate type.*bin").unwrap();
        let dylib = if nodylib.is_match(error) {
            None
        } else {
            let dylib_parts: Vec<&str> = lines.next().unwrap().trim()
                                              .split('_').collect();
            assert!(dylib_parts.len() == 2,
                    "rustc --print-file-name output has changed");
            Some((dylib_parts[0].to_string(), dylib_parts[1].to_string()))
        };

        let exe_suffix = if nobin.is_match(error) {
            String::new()
        } else {
            lines.next().unwrap().trim()
                 .split('_').skip(1).next().unwrap().to_string()
        };
        Ok((dylib, exe_suffix.to_string()))
    }

    /// Prepare this context, ensuring that all filesystem directories are in
    /// place.
    pub fn prepare(&mut self, pkg: &'a Package) -> CargoResult<()> {
        let _p = profile::start("preparing layout");

        try!(self.host.prepare().chain_error(|| {
            internal(format!("couldn't prepare build directories for `{}`",
                             pkg.name()))
        }));
        match self.target {
            Some(ref mut target) => {
                try!(target.prepare().chain_error(|| {
                    internal(format!("couldn't prepare build directories \
                                      for `{}`", pkg.name()))
                }));
            }
            None => {}
        }

        let targets = pkg.targets().iter();
        for target in targets.filter(|t| t.profile().is_compile()) {
            self.build_requirements(pkg, target, Platform::Target);
        }

        let jobs = self.jobs();
        self.compilation.extra_env.insert("NUM_JOBS".to_string(),
                                          Some(jobs.to_string()));
        self.compilation.root_output =
                self.layout(pkg, Kind::Target).proxy().dest().clone();
        self.compilation.deps_output =
                self.layout(pkg, Kind::Target).proxy().deps().clone();

        return Ok(());
    }

    fn build_requirements(&mut self, pkg: &'a Package, target: &'a Target,
                          req: Platform) {
        let req = if target.profile().is_for_host() {Platform::Plugin} else {req};
        match self.requirements.entry((pkg.package_id(), target.name())) {
            Occupied(mut entry) => match (*entry.get(), req) {
                (Platform::Plugin, Platform::Plugin) |
                (Platform::PluginAndTarget, Platform::Plugin) |
                (Platform::Target, Platform::Target) |
                (Platform::PluginAndTarget, Platform::Target) |
                (Platform::PluginAndTarget, Platform::PluginAndTarget) => return,
                _ => *entry.get_mut() = entry.get().combine(req),
            },
            Vacant(entry) => { entry.insert(req); }
        };

        for &(pkg, dep) in self.dep_targets(pkg, target).iter() {
            self.build_requirements(pkg, dep, req);
        }

        match pkg.targets().iter().find(|t| t.profile().is_custom_build()) {
            Some(custom_build) => {
                self.build_requirements(pkg, custom_build, Platform::Plugin);
            }
            None => {}
        }
    }

    pub fn get_requirement(&self, pkg: &'a Package,
                           target: &'a Target) -> Platform {
        let default = if target.profile().is_for_host() {
            Platform::Plugin
        } else {
            Platform::Target
        };
        self.requirements.get(&(pkg.package_id(), target.name()))
            .map(|a| *a).unwrap_or(default)
    }

    /// Returns the appropriate directory layout for either a plugin or not.
    pub fn layout(&self, pkg: &Package, kind: Kind) -> LayoutProxy {
        let primary = pkg.package_id() == self.resolve.root();
        match kind {
            Kind::Host => LayoutProxy::new(&self.host, primary),
            Kind::Target =>  LayoutProxy::new(self.target.as_ref()
                                                .unwrap_or(&self.host),
                                            primary),
        }
    }

    /// Returns the appropriate output directory for the specified package and
    /// target.
    pub fn out_dir(&self, pkg: &Package, kind: Kind, target: &Target) -> Path {
        let out_dir = self.layout(pkg, kind);
        if target.profile().is_custom_build() {
            out_dir.build(pkg)
        } else if target.is_example() {
            out_dir.examples().clone()
        } else {
            out_dir.root().clone()
        }
    }

    /// Return the (prefix, suffix) pair for dynamic libraries.
    ///
    /// If `plugin` is true, the pair corresponds to the host platform,
    /// otherwise it corresponds to the target platform.
    fn dylib(&self, kind: Kind) -> CargoResult<(&str, &str)> {
        let (triple, pair) = if kind == Kind::Host {
            (self.config.rustc_host(), &self.host_dylib)
        } else {
            (self.target_triple.as_slice(), &self.target_dylib)
        };
        match *pair {
            None => return Err(human(format!("dylib outputs are not supported \
                                              for {}", triple))),
            Some((ref s1, ref s2)) => Ok((s1, s2)),
        }
    }

    /// Return the target triple which this context is targeting.
    pub fn target_triple(&self) -> &str {
        &self.target_triple
    }

    /// Return the exact filename of the target.
    pub fn target_filenames(&self, target: &Target) -> CargoResult<Vec<String>> {
        let stem = target.file_stem();

        let mut ret = Vec::new();
        if target.is_example() || target.is_bin() ||
           target.profile().is_test() {
            ret.push(format!("{}{}", stem,
                             if target.profile().is_for_host() {
                                 &self.host_exe
                             } else {
                                 &self.target_exe
                             }));
        } else {
            if target.is_dylib() {
                let plugin = target.profile().is_for_host();
                let kind = if plugin {Kind::Host} else {Kind::Target};
                let (prefix, suffix) = try!(self.dylib(kind));
                ret.push(format!("{}{}{}", prefix, stem, suffix));
            }
            if target.is_rlib() {
                ret.push(format!("lib{}.rlib", stem));
            }
            if target.is_staticlib() {
                ret.push(format!("lib{}.a", stem));
            }
        }
        assert!(ret.len() > 0);
        return Ok(ret);
    }

    /// For a package, return all targets which are registered as dependencies
    /// for that package.
    pub fn dep_targets(&self, pkg: &Package, target: &Target)
                       -> Vec<(&'a Package, &'a Target)> {
        let deps = match self.resolve.deps(pkg.package_id()) {
            None => return vec!(),
            Some(deps) => deps,
        };
        deps.map(|id| self.get_package(id)).filter(|dep| {
            let pkg_dep = pkg.dependencies().iter().find(|d| {
                d.name() == dep.name()
            }).unwrap();

            // If this target is a build command, then we only want build
            // dependencies, otherwise we want everything *other than* build
            // dependencies.
            let is_correct_dep =
                target.profile().is_custom_build() == pkg_dep.is_build();

            // If this dependency is *not* a transitive dependency, then it
            // only applies to test/example targets
            let is_actual_dep = pkg_dep.is_transitive() ||
                                target.profile().is_test() ||
                                target.is_example();

            is_correct_dep && is_actual_dep
        }).filter_map(|pkg| {
            pkg.targets().iter().find(|&t| self.is_relevant_target(t))
               .map(|t| (pkg, t))
        }).collect()
    }

    /// Gets a package for the given package id.
    pub fn get_package(&self, id: &PackageId) -> &'a Package {
        self.package_set.iter()
            .find(|pkg| id == pkg.package_id())
            .expect("Should have found package")
    }

    pub fn env(&self) -> &str {
        // The "doc-all" environment just means to document everything (see
        // below), but we want to canonicalize that the the "doc" profile
        // environment, so do that here.
        if self.env == "doc-all" {"doc"} else {self.env}
    }

    pub fn is_relevant_target(&self, target: &Target) -> bool {
        target.is_lib() && match self.env {
            "doc" | "test" => target.profile().is_compile(),
            // doc-all == document everything, so look for doc targets and
            //            compile targets in dependencies
            "doc-all" => target.profile().is_compile() ||
                         (target.profile().env() == "doc" &&
                          target.profile().is_doc()),
            _ => target.profile().env() == self.env &&
                 !target.profile().is_test(),
        }
    }

    /// Get the user-specified linker for a particular host or target
    pub fn linker(&self, kind: Kind) -> Option<&str> {
        self.target_config(kind).linker.as_ref().map(|s| s.as_slice())
    }

    /// Get the user-specified `ar` program for a particular host or target
    pub fn ar(&self, kind: Kind) -> Option<&str> {
        self.target_config(kind).ar.as_ref().map(|s| s.as_slice())
    }

    /// Get the target configuration for a particular host or target
    fn target_config(&self, kind: Kind) -> &TargetConfig {
        match kind {
            Kind::Host => &self.build_config.host,
            Kind::Target => &self.build_config.target,
        }
    }

    /// Number of jobs specified for this build
    pub fn jobs(&self) -> u32 { self.build_config.jobs }

    /// Requested (not actual) target for the build
    pub fn requested_target(&self) -> Option<&str> {
        self.build_config.requested_target.as_ref().map(|s| &s[])
    }

    /// Calculate the actual profile to use for a target's compliation.
    ///
    /// This may involve overriding some options such as debug information,
    /// rpath, opt level, etc.
    pub fn profile(&self, target: &Target) -> Profile {
        let mut profile = target.profile().clone();
        let root_package = self.get_package(self.resolve.root());
        for target in root_package.manifest().targets().iter() {
            let root_profile = target.profile();
            if root_profile.env() != profile.env() { continue }
            profile = profile.set_opt_level(root_profile.opt_level())
                             .set_debug(root_profile.debug())
                             .set_rpath(root_profile.rpath())
        }
        profile
    }
}

impl Platform {
    pub fn combine(self, other: Platform) -> Platform {
        match (self, other) {
            (Platform::Target, Platform::Target) => Platform::Target,
            (Platform::Plugin, Platform::Plugin) => Platform::Plugin,
            _ => Platform::PluginAndTarget,
        }
    }

    pub fn includes(self, kind: Kind) -> bool {
        match (self, kind) {
            (Platform::PluginAndTarget, _) |
            (Platform::Target, Kind::Target) |
            (Platform::Plugin, Kind::Host) => true,
            _ => false,
        }
    }

    pub fn each_kind<F>(self, mut f: F) where F: FnMut(Kind) {
        match self {
            Platform::Target => f(Kind::Target),
            Platform::Plugin => f(Kind::Host),
            Platform::PluginAndTarget => { f(Kind::Target); f(Kind::Host); }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Grin Developers
//
// 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.

//! Provides the JSON/HTTP API for wallets to receive payments. Because
//! receiving money in MimbleWimble requires an interactive exchange, a
//! wallet server that's running at all time is required in many cases.

use bodyparser;
use iron::prelude::*;
use iron::Handler;
use iron::status;
use serde_json;

use api;
use core::consensus::reward;
use core::core::{build, Block, Output, Transaction, TxKernel};
use core::ser;
use keychain::{BlindingFactor, Identifier, Keychain};
use types::*;
use util;
use util::LOGGER;

/// Dummy wrapper for the hex-encoded serialized transaction.
#[derive(Serialize, Deserialize)]
pub struct TxWrapper {
	pub tx_hex: String,
}

pub fn receive_json_tx_str(
	config: &WalletConfig,
	keychain: &Keychain,
	json_tx: &str,
) -> Result<(), Error> {
	let partial_tx = serde_json::from_str(json_tx).unwrap();
	receive_json_tx(config, keychain, &partial_tx)
}

/// Receive an already well formed JSON transaction issuance and finalize the
/// transaction, adding our receiving output, to broadcast to the rest of the
/// network.
pub fn receive_json_tx(
	config: &WalletConfig,
	keychain: &Keychain,
	partial_tx: &PartialTx,
) -> Result<(), Error> {
	let (amount, blinding, tx) = read_partial_tx(keychain, partial_tx)?;
	let final_tx = receive_transaction(config, keychain, amount, blinding, tx)?;
	let tx_hex = util::to_hex(ser::ser_vec(&final_tx).unwrap());

	let url = format!("{}/v1/pool/push", config.check_node_api_http_addr.as_str());
	api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex })
		.map_err(|e| Error::Node(e))?;
	Ok(())
}

/// Component used to receive coins, implements all the receiving end of the
/// wallet REST API as well as some of the command-line operations.
#[derive(Clone)]
pub struct WalletReceiver {
	pub keychain: Keychain,
	pub config: WalletConfig,
}

impl Handler for WalletReceiver {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let struct_body = req.get::<bodyparser::Struct<PartialTx>>();

		if let Ok(Some(partial_tx)) = struct_body {
			receive_json_tx(&self.config, &self.keychain, &partial_tx)
				.map_err(|e| {
					error!(LOGGER, "Problematic partial tx, looks like this: {:?}", partial_tx);
					api::Error::Internal(
						format!("Error processing partial transaction: {:?}", e),
					)})
				.unwrap();
			Ok(Response::with(status::Ok))
		} else {
			Ok(Response::with((status::BadRequest, "")))
		}
	}
}

fn retrieve_existing_key(
	wallet_data: &WalletData,
	key_id: Identifier,
) -> (Identifier, u32) {
	if let Some(existing) = wallet_data.get_output(&key_id) {
		let key_id = existing.key_id.clone();
		let derivation = existing.n_child;
		(key_id, derivation)
	} else {
		panic!("should never happen");
	}
}

fn next_available_key(
	wallet_data: &WalletData,
	keychain: &Keychain,
) -> (Identifier, u32) {
	let root_key_id = keychain.root_key_id();
	let derivation = wallet_data.next_child(root_key_id.clone());
	let key_id = keychain.derive_key_id(derivation).unwrap();
	(key_id, derivation)
}

/// Build a coinbase output and the corresponding kernel
pub fn receive_coinbase(
	config: &WalletConfig,
	keychain: &Keychain,
	block_fees: &BlockFees,
) -> Result<(Output, TxKernel, BlockFees), Error> {
	let root_key_id = keychain.root_key_id();

	// Now acquire the wallet lock and write the new output.
	let (key_id, derivation) = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
		let key_id = block_fees.key_id();
		let (key_id, derivation) = match key_id {
			Some(key_id) => retrieve_existing_key(&wallet_data, key_id),
			None => next_available_key(&wallet_data, keychain),
		};

		// track the new output and return the stuff needed for reward
		wallet_data.add_output(OutputData {
			root_key_id: root_key_id.clone(),
			key_id: key_id.clone(),
			n_child: derivation,
			value: reward(block_fees.fees),
			status: OutputStatus::Unconfirmed,
			height: 0,
			lock_height: 0,
			is_coinbase: true,
		});

		(key_id, derivation)
	})?;

	debug!(
		LOGGER,
		"Received coinbase and built candidate output - {:?}, {:?}, {}",
		root_key_id.clone(),
		key_id.clone(),
		derivation,
	);

	debug!(LOGGER, "block_fees - {:?}", block_fees);

	let mut block_fees = block_fees.clone();
	block_fees.key_id = Some(key_id.clone());

	debug!(LOGGER, "block_fees updated - {:?}", block_fees);

	let (out, kern) = Block::reward_output(&keychain, &key_id, block_fees.fees)?;
	Ok((out, kern, block_fees))
}

/// Builds a full transaction from the partial one sent to us for transfer
fn receive_transaction(
	config: &WalletConfig,
	keychain: &Keychain,
	amount: u64,
	blinding: BlindingFactor,
	partial: Transaction,
) -> Result<Transaction, Error> {
	let root_key_id = keychain.root_key_id();

	// double check the fee amount included in the partial tx
 // we don't necessarily want to just trust the sender
 // we could just overwrite the fee here (but we won't) due to the ecdsa sig
	let fee = tx_fee(partial.inputs.len(), partial.outputs.len() + 1, None);
	if fee != partial.fee {
		return Err(Error::FeeDispute {
			sender_fee: partial.fee,
			recipient_fee: fee,
		});
	}

	let out_amount = amount - fee;

	// operate within a lock on wallet data
	let (key_id, derivation) = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
		let (key_id, derivation) = next_available_key(&wallet_data, keychain);

		wallet_data.add_output(OutputData {
			root_key_id: root_key_id.clone(),
			key_id: key_id.clone(),
			n_child: derivation,
			value: out_amount,
			status: OutputStatus::Unconfirmed,
			height: 0,
			lock_height: 0,
			is_coinbase: false,
		});

		(key_id, derivation)
	})?;

	let (tx_final, _) = build::transaction(
		vec![
			build::initial_tx(partial),
			build::with_excess(blinding),
			build::output(out_amount, key_id.clone()),
		// build::with_fee(fee_amount),
		],
		keychain,
	)?;

	// make sure the resulting transaction is valid (could have been lied to on
 // excess).
	tx_final.validate()?;

	debug!(
		LOGGER,
		"Received txn and built output - {:?}, {:?}, {}",
		root_key_id.clone(),
		key_id.clone(),
		derivation,
	);

	Ok(tx_final)
}
<commit_msg>Remove reserved output on wallet receive failure<commit_after>// Copyright 2017 The Grin Developers
//
// 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.

//! Provides the JSON/HTTP API for wallets to receive payments. Because
//! receiving money in MimbleWimble requires an interactive exchange, a
//! wallet server that's running at all time is required in many cases.

use bodyparser;
use iron::prelude::*;
use iron::Handler;
use iron::status;
use serde_json;

use api;
use core::consensus::reward;
use core::core::{build, Block, Output, Transaction, TxKernel};
use core::ser;
use keychain::{BlindingFactor, Identifier, Keychain};
use types::*;
use util;
use util::LOGGER;

/// Dummy wrapper for the hex-encoded serialized transaction.
#[derive(Serialize, Deserialize)]
pub struct TxWrapper {
	pub tx_hex: String,
}

pub fn receive_json_tx_str(
	config: &WalletConfig,
	keychain: &Keychain,
	json_tx: &str,
) -> Result<(), Error> {
	let partial_tx = serde_json::from_str(json_tx).unwrap();
	receive_json_tx(config, keychain, &partial_tx)
}

/// Receive an already well formed JSON transaction issuance and finalize the
/// transaction, adding our receiving output, to broadcast to the rest of the
/// network.
pub fn receive_json_tx(
	config: &WalletConfig,
	keychain: &Keychain,
	partial_tx: &PartialTx,
) -> Result<(), Error> {

	// reading the partial transaction and finalizing it, adding our output
	let (amount, blinding, tx) = read_partial_tx(keychain, partial_tx)?;
	let (final_tx, key_id) = receive_transaction(config, keychain, amount, blinding, tx)?;
	let tx_hex = util::to_hex(ser::ser_vec(&final_tx).unwrap());

	// pushing to the transaction pool, in case of error the output that was
	// reserved needs to be removed
	let url = format!("{}/v1/pool/push", config.check_node_api_http_addr.as_str());
	let res = api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex });
	if let Err(e) = res {
		WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
			wallet_data.delete_output(&key_id);
		})?;
		Err(Error::Node(e))
	} else {
		Ok(())
	}
}

/// Component used to receive coins, implements all the receiving end of the
/// wallet REST API as well as some of the command-line operations.
#[derive(Clone)]
pub struct WalletReceiver {
	pub keychain: Keychain,
	pub config: WalletConfig,
}

impl Handler for WalletReceiver {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let struct_body = req.get::<bodyparser::Struct<PartialTx>>();

		if let Ok(Some(partial_tx)) = struct_body {
			receive_json_tx(&self.config, &self.keychain, &partial_tx)
				.map_err(|e| {
					error!(LOGGER, "Problematic partial tx, looks like this: {:?}", partial_tx);
					api::Error::Internal(
						format!("Error processing partial transaction: {:?}", e),
					)})
				.unwrap();
			Ok(Response::with(status::Ok))
		} else {
			Ok(Response::with((status::BadRequest, "")))
		}
	}
}

fn retrieve_existing_key(
	wallet_data: &WalletData,
	key_id: Identifier,
) -> (Identifier, u32) {
	if let Some(existing) = wallet_data.get_output(&key_id) {
		let key_id = existing.key_id.clone();
		let derivation = existing.n_child;
		(key_id, derivation)
	} else {
		panic!("should never happen");
	}
}

fn next_available_key(
	wallet_data: &WalletData,
	keychain: &Keychain,
) -> (Identifier, u32) {
	let root_key_id = keychain.root_key_id();
	let derivation = wallet_data.next_child(root_key_id.clone());
	let key_id = keychain.derive_key_id(derivation).unwrap();
	(key_id, derivation)
}

/// Build a coinbase output and the corresponding kernel
pub fn receive_coinbase(
	config: &WalletConfig,
	keychain: &Keychain,
	block_fees: &BlockFees,
) -> Result<(Output, TxKernel, BlockFees), Error> {
	let root_key_id = keychain.root_key_id();

	// Now acquire the wallet lock and write the new output.
	let (key_id, derivation) = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
		let key_id = block_fees.key_id();
		let (key_id, derivation) = match key_id {
			Some(key_id) => retrieve_existing_key(&wallet_data, key_id),
			None => next_available_key(&wallet_data, keychain),
		};

		// track the new output and return the stuff needed for reward
		wallet_data.add_output(OutputData {
			root_key_id: root_key_id.clone(),
			key_id: key_id.clone(),
			n_child: derivation,
			value: reward(block_fees.fees),
			status: OutputStatus::Unconfirmed,
			height: 0,
			lock_height: 0,
			is_coinbase: true,
		});

		(key_id, derivation)
	})?;

	debug!(
		LOGGER,
		"Received coinbase and built candidate output - {:?}, {:?}, {}",
		root_key_id.clone(),
		key_id.clone(),
		derivation,
	);

	debug!(LOGGER, "block_fees - {:?}", block_fees);

	let mut block_fees = block_fees.clone();
	block_fees.key_id = Some(key_id.clone());

	debug!(LOGGER, "block_fees updated - {:?}", block_fees);

	let (out, kern) = Block::reward_output(&keychain, &key_id, block_fees.fees)?;
	Ok((out, kern, block_fees))
}

/// Builds a full transaction from the partial one sent to us for transfer
fn receive_transaction(
	config: &WalletConfig,
	keychain: &Keychain,
	amount: u64,
	blinding: BlindingFactor,
	partial: Transaction,
) -> Result<(Transaction, Identifier), Error> {

	let root_key_id = keychain.root_key_id();

	// double check the fee amount included in the partial tx
 // we don't necessarily want to just trust the sender
 // we could just overwrite the fee here (but we won't) due to the ecdsa sig
	let fee = tx_fee(partial.inputs.len(), partial.outputs.len() + 1, None);
	if fee != partial.fee {
		return Err(Error::FeeDispute {
			sender_fee: partial.fee,
			recipient_fee: fee,
		});
	}

	let out_amount = amount - fee;

	// operate within a lock on wallet data
	let (key_id, derivation) = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
		let (key_id, derivation) = next_available_key(&wallet_data, keychain);

		wallet_data.add_output(OutputData {
			root_key_id: root_key_id.clone(),
			key_id: key_id.clone(),
			n_child: derivation,
			value: out_amount,
			status: OutputStatus::Unconfirmed,
			height: 0,
			lock_height: 0,
			is_coinbase: false,
		});

		(key_id, derivation)
	})?;

	let (tx_final, _) = build::transaction(
		vec![
			build::initial_tx(partial),
			build::with_excess(blinding),
			build::output(out_amount, key_id.clone()),
		// build::with_fee(fee_amount),
		],
		keychain,
	)?;

	// make sure the resulting transaction is valid (could have been lied to on
 // excess).
	tx_final.validate()?;

	debug!(
		LOGGER,
		"Received txn and built output - {:?}, {:?}, {}",
		root_key_id.clone(),
		key_id.clone(),
		derivation,
	);

	Ok((tx_final, key_id))
}
<|endoftext|>


<commit_before>//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 3 optional args:
//   square image size, defaults to 80_u
//   yield frequency, defaults to 10_u (yield every 10 spawns)
//   output path, default is "" (no output), "-" means stdout
//
//  in the shootout, they use 16000 as image size
//  yield frequency doesn't seem to have much effect
//
//  writes pbm image to output path

#[legacy_modes];

extern mod std;
use io::WriterUtil;
use std::map::HashMap;

struct cmplx {
    re: f64,
    im: f64
}

impl cmplx : ops::Mul<cmplx,cmplx> {
    pure fn mul(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl cmplx : ops::Add<cmplx,cmplx> {
    pure fn add(x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

type line = {i: uint, b: ~[u8]};

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx) -> bool
{
    let mut z = cmplx {re: 0f64, im: 0f64};
    let mut i = 0;
    let mut in = true;
    while i < 50 {
        z = z*z + x;
        if cabs(z) >= 4f64 {
            in = false;
            break;
        }
        i += 1;
    }
    in
}

fn fillbyte(x: cmplx, incr: f64) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint, ch: comm::Chan<line>) -> ()
{
    let mut crv = ~[];
    let incr = 2f64/(size as f64);
    let y = incr*(i as f64) - 1f64;
    let xincr = 8f64*incr;
    for uint::range(0_u, size/8_u) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5f64, im: y};
        crv.push(fillbyte(x, incr));
    };
    comm::send(ch, {i:i, b:crv});
}

type devnull = {dn: int};

impl devnull: io::Writer {
    fn write(_b: &[const u8]) {}
    fn seek(+_i: int, +_s: io::SeekStyle) {}
    fn tell() -> uint {0_u}
    fn flush() -> int {0}
    fn get_type() -> io::WriterType { io::File }
}

fn writer(path: ~str, writech: comm::Chan<comm::Chan<line>>, size: uint)
{
    let p: comm::Port<line> = comm::Port();
    let ch = comm::Chan(&p);
    comm::send(writech, ch);
    let cout: io::Writer = match path {
        ~"" => {
            {dn: 0} as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                &io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line(~"P4");
    cout.write_line(fmt!("%u %u", size, size));
    let lines: HashMap<uint, ~[u8]> = HashMap();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = comm::recv(p);
        if aline.i == done {
            debug!("W %u", aline.i);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                if lines.contains_key(prev) {
                    debug!("WS %u", prev);
                    cout.write(lines.get(prev));
                    done += 1_u;
                    lines.remove(prev);
                    prev += 1_u;
                }
                else {
                    break
                }
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, aline.b);
        };
        i += 1_u;
    }
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000", ~"10"]
    } else {
        args
    };

    let path = if vec::len(args) < 4_u { ~"" }
    else { args[3] };

    let yieldevery = if vec::len(args) < 3_u { 10_u }
    else { uint::from_str(args[2]).get() };

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let writep = comm::Port();
    let writech = comm::Chan(&writep);
    do task::spawn {
        writer(path, writech, size);
    };
    let ch = comm::recv(writep);
    for uint::range(0_u, size) |j| {
        task::spawn(|| chanmb(j, size, ch) );
        if j % yieldevery == 0_u {
            debug!("Y %u", j);
            task::yield();
        };
    };
}
<commit_msg>test: Fix shootout-mandelbrot.rs. rs=bustage<commit_after>//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 3 optional args:
//   square image size, defaults to 80_u
//   yield frequency, defaults to 10_u (yield every 10 spawns)
//   output path, default is "" (no output), "-" means stdout
//
//  in the shootout, they use 16000 as image size
//  yield frequency doesn't seem to have much effect
//
//  writes pbm image to output path

#[legacy_modes];

extern mod std;
use io::WriterUtil;
use std::map::HashMap;

struct cmplx {
    re: f64,
    im: f64
}

impl cmplx : ops::Mul<cmplx,cmplx> {
    pure fn mul(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl cmplx : ops::Add<cmplx,cmplx> {
    pure fn add(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

type line = {i: uint, b: ~[u8]};

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx) -> bool
{
    let mut z = cmplx {re: 0f64, im: 0f64};
    let mut i = 0;
    let mut in = true;
    while i < 50 {
        z = z*z + x;
        if cabs(z) >= 4f64 {
            in = false;
            break;
        }
        i += 1;
    }
    in
}

fn fillbyte(x: cmplx, incr: f64) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint, ch: comm::Chan<line>) -> ()
{
    let mut crv = ~[];
    let incr = 2f64/(size as f64);
    let y = incr*(i as f64) - 1f64;
    let xincr = 8f64*incr;
    for uint::range(0_u, size/8_u) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5f64, im: y};
        crv.push(fillbyte(x, incr));
    };
    comm::send(ch, {i:i, b:crv});
}

type devnull = {dn: int};

impl devnull: io::Writer {
    fn write(_b: &[const u8]) {}
    fn seek(+_i: int, +_s: io::SeekStyle) {}
    fn tell() -> uint {0_u}
    fn flush() -> int {0}
    fn get_type() -> io::WriterType { io::File }
}

fn writer(path: ~str, writech: comm::Chan<comm::Chan<line>>, size: uint)
{
    let p: comm::Port<line> = comm::Port();
    let ch = comm::Chan(&p);
    comm::send(writech, ch);
    let cout: io::Writer = match path {
        ~"" => {
            {dn: 0} as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                &io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line(~"P4");
    cout.write_line(fmt!("%u %u", size, size));
    let lines: HashMap<uint, ~[u8]> = HashMap();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = comm::recv(p);
        if aline.i == done {
            debug!("W %u", aline.i);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                if lines.contains_key(prev) {
                    debug!("WS %u", prev);
                    cout.write(lines.get(prev));
                    done += 1_u;
                    lines.remove(prev);
                    prev += 1_u;
                }
                else {
                    break
                }
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, copy aline.b);    // FIXME: bad for perf
        };
        i += 1_u;
    }
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000", ~"10"]
    } else {
        args
    };

    let path = if vec::len(args) < 4_u { ~"" }
    else { copy args[3] };  // FIXME: bad for perf

    let yieldevery = if vec::len(args) < 3_u { 10_u }
    else { uint::from_str(args[2]).get() };

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let writep = comm::Port();
    let writech = comm::Chan(&writep);
    do task::spawn |move path| {
        writer(copy path, writech, size);
    };
    let ch = comm::recv(writep);
    for uint::range(0_u, size) |j| {
        task::spawn(|| chanmb(j, size, ch) );
        if j % yieldevery == 0_u {
            debug!("Y %u", j);
            task::yield();
        };
    };
}
<|endoftext|>


<commit_before>use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;

use regex::Regex;

use crate::Solution;

struct Rule {
    c: char,
    min: usize,
    max: usize,
    sample: String,
}

impl Rule {
    fn matches1(&self) -> bool {
        let occurrences = self.sample.matches(self.c).count();

        occurrences >= self.min && occurrences <= self.max
    }

    fn matches2(&self) -> bool {
        let c = self.c as u8;
        let s = self.sample.as_bytes();

        (s[self.min - 1] == c) ^ (s[self.max - 1] == c)
    }
}

fn read_rules(input: &mut dyn Read) -> Vec<Rule> {
    let parser = Regex::new(r"^(\d+)-(\d+) ([a-z]): ([a-z]+)$").unwrap();

    let mut reader = BufReader::new(input);
    let mut buffer = String::new();

    let mut rules = Vec::new();

    while let Ok(read) = reader.read_line(&mut buffer) {
        if read == 0 {
            break;
        }

        let cap = parser.captures(buffer.trim()).unwrap();

        rules.push(Rule {
            c: cap[3].chars().next().unwrap(),
            min: cap[1].parse().unwrap(),
            max: cap[2].parse().unwrap(),
            sample: cap[4].to_owned(),
        });

        buffer.clear();
    }

    rules
}

#[derive(Default)]
pub struct Day02;

impl Solution for Day02 {
    fn part1(&mut self, input: &mut dyn Read) -> String {
        read_rules(input)
            .into_iter()
            .filter(Rule::matches1)
            .count()
            .to_string()
    }

    fn part2(&mut self, input: &mut dyn Read) -> String {
        read_rules(input)
            .into_iter()
            .filter(Rule::matches2)
            .count()
            .to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const SAMPLE: &[u8] = include_bytes!("../samples/02.txt");

    #[test]
    fn sample_part1() {
        let mut implementation = Day02;

        let result = implementation.part1(&mut SAMPLE.as_ref());
        assert_eq!("2", &result);
    }

    #[test]
    fn sample_part2() {
        let mut implementation = Day02;

        let result = implementation.part2(&mut SAMPLE.as_ref());
        assert_eq!("1", &result);
    }
}
<commit_msg>Rewrite day 2 with fewer allocations<commit_after>use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;

use regex::Regex;

use crate::Solution;

fn matches1(min: usize, max: usize, c: char, sample: &str) -> bool {
    let occurrences = sample.matches(c).count();

    occurrences >= min && occurrences <= max
}

fn matches2(first: usize, second: usize, c: char, sample: &str) -> bool {
    let c = c as u8;
    let s = sample.as_bytes();

    (s[first - 1] == c) ^ (s[second - 1] == c)
}

fn read_rules<M>(input: &mut dyn Read, matcher: M) -> usize
where
    M: for<'r> Fn(usize, usize, char, &'r str) -> bool,
{
    let parser = Regex::new(r"^(\d+)-(\d+) ([a-z]): ([a-z]+)$").unwrap();

    let mut reader = BufReader::new(input);
    let mut buffer = String::new();

    let mut matching = 0;

    while let Ok(read) = reader.read_line(&mut buffer) {
        if read == 0 {
            break;
        }

        let cap = parser.captures(buffer.trim()).unwrap();

        let first = cap[1].parse().unwrap();
        let second = cap[2].parse().unwrap();
        let c = cap[3].chars().next().unwrap();
        let sample = &cap[4];

        if matcher(first, second, c, sample) {
            matching += 1
        }

        buffer.clear()
    }

    matching
}

#[derive(Default)]
pub struct Day02;

impl Solution for Day02 {
    fn part1(&mut self, input: &mut dyn Read) -> String {
        read_rules(input, matches1).to_string()
    }

    fn part2(&mut self, input: &mut dyn Read) -> String {
        read_rules(input, matches2).to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const SAMPLE: &[u8] = include_bytes!("../samples/02.txt");

    #[test]
    fn sample_part1() {
        let mut implementation = Day02;

        let result = implementation.part1(&mut SAMPLE.as_ref());
        assert_eq!("2", &result);
    }

    #[test]
    fn sample_part2() {
        let mut implementation = Day02;

        let result = implementation.part2(&mut SAMPLE.as_ref());
        assert_eq!("1", &result);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::io::Read;
use std::ops::Add;
use std::ops::Sub;

use nom::bytes::complete::tag;
use nom::character::complete::newline;
use nom::combinator::map;
use nom::multi::many1;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use nom::sequence::preceded;
use nom::sequence::terminated;
use nom::sequence::tuple;
use nom::IResult;

use crate::common::read_input;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
struct Point3(pub [i32; 3]);

impl Point3 {
    pub fn manhattan(&self) -> i32 {
        self.0.into_iter().map(i32::abs).sum()
    }
}

impl Sub for Point3 {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        Point3([
            self.0[0] - rhs.0[0],
            self.0[1] - rhs.0[1],
            self.0[2] - rhs.0[2],
        ])
    }
}

impl Add for Point3 {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Point3([
            self.0[0] + rhs.0[0],
            self.0[1] + rhs.0[1],
            self.0[2] + rhs.0[2],
        ])
    }
}

struct Rotations<'a> {
    points: &'a [Point3],
    axes: [usize; 3],
    rotation_index: usize,
}

impl<'a> Rotations<'a> {
    const ROTATIONS: [[i32; 3]; 8] = [
        [1, 1, 1],
        [1, 1, -1],
        [1, -1, 1],
        [1, -1, -1],
        [-1, 1, 1],
        [-1, 1, -1],
        [-1, -1, 1],
        [-1, -1, -1],
    ];

    pub fn new(points: &'a [Point3]) -> Self {
        Self {
            points,
            axes: [0, 1, 2],
            rotation_index: 0,
        }
    }
}

impl Iterator for Rotations<'_> {
    type Item = Vec<Point3>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rotation_index >= Self::ROTATIONS.len() {
            if !next_permutation(&mut self.axes) {
                return None;
            }

            self.rotation_index = 0;
        }

        let axes = &self.axes;
        let rot = &Self::ROTATIONS[self.rotation_index];

        let result = self
            .points
            .iter()
            .map(|Point3(coords)| {
                Point3([
                    coords[axes[0]] * rot[0],
                    coords[axes[1]] * rot[1],
                    coords[axes[2]] * rot[2],
                ])
            })
            .collect();

        self.rotation_index += 1;

        Some(result)
    }
}

fn parse_point(input: &[u8]) -> IResult<&[u8], Point3> {
    use nom::character::complete::char;
    use nom::character::complete::i32;

    map(
        tuple((i32, preceded(char(','), i32), preceded(char(','), i32))),
        |(x, y, z)| Point3([x, y, z]),
    )(input)
}

fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<Vec<Point3>>> {
    use nom::character::complete::i32;
    let parse_header = delimited(tag("--- scanner "), i32, tag(" ---\n"));

    let parse_scanner = preceded(parse_header, many1(terminated(parse_point, newline)));
    separated_list1(newline, parse_scanner)(input)
}

fn try_overlap(
    correct: &[(Point3, HashSet<Point3>)],
    candidate: &[Point3],
) -> Option<(Point3, Vec<Point3>)> {
    let mut relative = HashSet::new();
    for rot in Rotations::new(candidate) {
        for &start in &rot {
            relative.clear();

            relative.extend(rot.iter().map(|&other| other - start));

            if let Some((base, _)) = correct.iter().find(|(_, correct_relative)| {
                correct_relative.intersection(&relative).count() >= 12
            }) {
                // Found a solution, build the correct output
                let translated = relative.drain().map(|point| point + *base).collect();

                return Some((start - *base, translated));
            }
        }
    }

    None
}

pub fn part1(input: &mut dyn Read) -> String {
    let mut scanners = read_input(input, parse_input);

    let mut points: HashSet<_> = scanners[0].iter().copied().collect();

    let mut todo = vec![std::mem::take(&mut scanners[0])];

    while let Some(matched) = todo.pop() {
        if scanners.iter().all(Vec::is_empty) {
            break;
        }

        let relative: Vec<(Point3, HashSet<Point3>)> = matched
            .iter()
            .map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
            .collect();

        for candidate in &mut scanners {
            if candidate.is_empty() {
                continue;
            }

            if let Some((_, result)) = try_overlap(&relative, candidate) {
                points.extend(result.iter().copied());
                todo.push(result);
                candidate.clear();
            }
        }
    }

    points.len().to_string()
}

pub fn part2(input: &mut dyn Read) -> String {
    let mut scanners = read_input(input, parse_input);

    let mut found_scanners = vec![Point3::default()];
    let mut todo = vec![std::mem::take(&mut scanners[0])];

    while let Some(matched) = todo.pop() {
        if scanners.iter().all(Vec::is_empty) {
            break;
        }

        let relative: Vec<(Point3, HashSet<Point3>)> = matched
            .iter()
            .map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
            .collect();

        for candidate in &mut scanners {
            if candidate.is_empty() {
                continue;
            }

            if let Some((new_offset, result)) = try_overlap(&relative, candidate) {
                todo.push(result);
                candidate.clear();

                found_scanners.push(new_offset);
            }
        }
    }

    found_scanners
        .iter()
        .flat_map(|&first| {
            found_scanners
                .iter()
                .map(move |&second| (first - second).manhattan())
        })
        .max()
        .unwrap()
        .to_string()
}

pub fn next_permutation<T: Ord>(list: &mut [T]) -> bool {
    // Based on: https://en.cppreference.com/w/cpp/algorithm/next_permutation
    if list.len() <= 1 {
        return false;
    }

    if let Some((i, val1)) = list
        .windows(2)
        .enumerate()
        .rev()
        .find_map(|(i, window)| (window[0] < window[1]).then(|| (i, &window[0])))
    {
        let it2 = list
            .iter()
            .enumerate()
            .skip(i + 1)
            .rev()
            .find_map(|(idx, val2)| (val1 < val2).then(|| idx))
            .expect("Unreachable, ascending pair exists");

        list.swap(i, it2);
        list[(i + 1)..].reverse();
        true
    } else {
        list.reverse();
        false
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/19.txt");

    #[test]
    fn test_next_permutation() {
        let mut list = [1, 2, 3];

        assert!(next_permutation(&mut list));
        assert_eq!(list, [1, 3, 2]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [2, 1, 3]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [2, 3, 1]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [3, 1, 2]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [3, 2, 1]);

        // Note the negation!
        assert!(!next_permutation(&mut list));
        assert_eq!(list, [1, 2, 3]);

        // 24 is a bit too much to write out, but we can check the number
        let mut list2 = [1, 2, 3, 4];
        for _ in 1..24 {
            assert!(next_permutation(&mut list2));
        }

        // Should be back to the start
        assert!(!next_permutation(&mut list2));
        assert_eq!(list2, [1, 2, 3, 4]);
    }

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 79);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 3621);
    }
}
<commit_msg>Filter out the worst repetition<commit_after>use std::collections::HashSet;
use std::io::Read;
use std::ops::Add;
use std::ops::Sub;

use nom::bytes::complete::tag;
use nom::character::complete::newline;
use nom::combinator::map;
use nom::multi::many1;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use nom::sequence::preceded;
use nom::sequence::terminated;
use nom::sequence::tuple;
use nom::IResult;

use crate::common::read_input;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
struct Point3(pub [i32; 3]);

impl Point3 {
    pub fn manhattan(&self) -> i32 {
        self.0.into_iter().map(i32::abs).sum()
    }
}

impl Sub for Point3 {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        Point3([
            self.0[0] - rhs.0[0],
            self.0[1] - rhs.0[1],
            self.0[2] - rhs.0[2],
        ])
    }
}

impl Add for Point3 {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Point3([
            self.0[0] + rhs.0[0],
            self.0[1] + rhs.0[1],
            self.0[2] + rhs.0[2],
        ])
    }
}

struct Rotations<'a> {
    points: &'a [Point3],
    axes: [usize; 3],
    rotation_index: usize,
}

impl<'a> Rotations<'a> {
    const ROTATIONS: [[i32; 3]; 8] = [
        [1, 1, 1],
        [1, 1, -1],
        [1, -1, 1],
        [1, -1, -1],
        [-1, 1, 1],
        [-1, 1, -1],
        [-1, -1, 1],
        [-1, -1, -1],
    ];

    pub fn new(points: &'a [Point3]) -> Self {
        Self {
            points,
            axes: [0, 1, 2],
            rotation_index: 0,
        }
    }
}

impl Iterator for Rotations<'_> {
    type Item = Vec<Point3>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rotation_index >= Self::ROTATIONS.len() {
            if !next_permutation(&mut self.axes) {
                return None;
            }

            self.rotation_index = 0;
        }

        let axes = &self.axes;
        let rot = &Self::ROTATIONS[self.rotation_index];

        let result = self
            .points
            .iter()
            .map(|Point3(coords)| {
                Point3([
                    coords[axes[0]] * rot[0],
                    coords[axes[1]] * rot[1],
                    coords[axes[2]] * rot[2],
                ])
            })
            .collect();

        self.rotation_index += 1;

        Some(result)
    }
}

fn parse_point(input: &[u8]) -> IResult<&[u8], Point3> {
    use nom::character::complete::char;
    use nom::character::complete::i32;

    map(
        tuple((i32, preceded(char(','), i32), preceded(char(','), i32))),
        |(x, y, z)| Point3([x, y, z]),
    )(input)
}

fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<Vec<Point3>>> {
    use nom::character::complete::i32;
    let parse_header = delimited(tag("--- scanner "), i32, tag(" ---\n"));

    let parse_scanner = preceded(parse_header, many1(terminated(parse_point, newline)));
    separated_list1(newline, parse_scanner)(input)
}

fn try_overlap(
    correct: &[(Point3, HashSet<Point3>)],
    candidate: &[Point3],
) -> Option<(Point3, Vec<Point3>)> {
    let mut relative = HashSet::new();
    for rot in Rotations::new(candidate) {
        for &start in &rot {
            relative.clear();

            relative.extend(rot.iter().map(|&other| other - start));

            if let Some((base, _)) = correct.iter().find(|(_, correct_relative)| {
                correct_relative.intersection(&relative).count() >= 12
            }) {
                // Found a solution, build the correct output
                let translated = relative.drain().map(|point| point + *base).collect();

                return Some((start - *base, translated));
            }
        }
    }

    None
}

fn parts_common(input: &mut dyn Read) -> (HashSet<Point3>, Vec<Point3>) {
    let mut scanners = read_input(input, parse_input);

    let mut points: HashSet<_> = scanners[0].iter().copied().collect();

    let mut todo = vec![std::mem::take(&mut scanners[0])];
    let mut scanners_found = vec![Point3::default()];

    while let Some(matched) = todo.pop() {
        if scanners.iter().all(Vec::is_empty) {
            break;
        }

        let relative: Vec<(Point3, HashSet<Point3>)> = matched
            .iter()
            .map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
            .collect();

        for candidate in &mut scanners {
            if candidate.is_empty() {
                continue;
            }

            if let Some((scanner, result)) = try_overlap(&relative, candidate) {
                scanners_found.push(scanner);
                points.extend(result.iter().copied());
                todo.push(result);
                candidate.clear();
            }
        }
    }

    (points, scanners_found)
}

pub fn part1(input: &mut dyn Read) -> String {
    let (points, _) = parts_common(input);
    points.len().to_string()
}

pub fn part2(input: &mut dyn Read) -> String {
    let (_, scanners) = parts_common(input);

    scanners
        .iter()
        .flat_map(|&first| {
            scanners
                .iter()
                .map(move |&second| (first - second).manhattan())
        })
        .max()
        .unwrap()
        .to_string()
}

pub fn next_permutation<T: Ord>(list: &mut [T]) -> bool {
    // Based on: https://en.cppreference.com/w/cpp/algorithm/next_permutation
    if list.len() <= 1 {
        return false;
    }

    if let Some((i, val1)) = list
        .windows(2)
        .enumerate()
        .rev()
        .find_map(|(i, window)| (window[0] < window[1]).then(|| (i, &window[0])))
    {
        let it2 = list
            .iter()
            .enumerate()
            .skip(i + 1)
            .rev()
            .find_map(|(idx, val2)| (val1 < val2).then(|| idx))
            .expect("Unreachable, ascending pair exists");

        list.swap(i, it2);
        list[(i + 1)..].reverse();
        true
    } else {
        list.reverse();
        false
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/19.txt");

    #[test]
    fn test_next_permutation() {
        let mut list = [1, 2, 3];

        assert!(next_permutation(&mut list));
        assert_eq!(list, [1, 3, 2]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [2, 1, 3]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [2, 3, 1]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [3, 1, 2]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [3, 2, 1]);

        // Note the negation!
        assert!(!next_permutation(&mut list));
        assert_eq!(list, [1, 2, 3]);

        // 24 is a bit too much to write out, but we can check the number
        let mut list2 = [1, 2, 3, 4];
        for _ in 1..24 {
            assert!(next_permutation(&mut list2));
        }

        // Should be back to the start
        assert!(!next_permutation(&mut list2));
        assert_eq!(list2, [1, 2, 3, 4]);
    }

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 79);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 3621);
    }
}
<|endoftext|>