<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_snake_case)]

// Error messages for EXXXX errors.
// Each message should start and end with a new line, and be wrapped to 80 characters.
// In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable.
register_long_diagnostics! {

E0178: r##"
In types, the `+` type operator has low precedence, so it is often necessary
to use parentheses.

For example:

```compile_fail,E0178
trait Foo {}

struct Bar<'a> {
    w: &'a Foo + Copy,   // error, use &'a (Foo + Copy)
    x: &'a Foo + 'a,     // error, use &'a (Foo + 'a)
    y: &'a mut Foo + 'a, // error, use &'a mut (Foo + 'a)
    z: fn() -> Foo + 'a, // error, use fn() -> (Foo + 'a)
}
```

More details can be found in [RFC 438].

[RFC 438]: https://github.com/rust-lang/rfcs/pull/438
"##,

E0534: r##"
The `inline` attribute was malformed.

Erroneous code example:

```ignore (compile_fail not working here; see Issue #43707)
#[inline()] // error: expected one argument
pub fn something() {}

fn main() {}
```

The parenthesized `inline` attribute requires the parameter to be specified:

```
#[inline(always)]
fn something() {}
```

or:

```
#[inline(never)]
fn something() {}
```

Alternatively, a paren-less version of the attribute may be used to hint the
compiler about inlining opportunity:

```
#[inline]
fn something() {}
```

For more information about the inline attribute, read:
https://doc.rust-lang.org/reference.html#inline-attributes
"##,

E0535: r##"
An unknown argument was given to the `inline` attribute.

Erroneous code example:

```ignore (compile_fail not working here; see Issue #43707)
#[inline(unknown)] // error: invalid argument
pub fn something() {}

fn main() {}
```

The `inline` attribute only supports two arguments:

 * always
 * never

All other arguments given to the `inline` attribute will return this error.
Example:

```
#[inline(never)] // ok!
pub fn something() {}

fn main() {}
```

For more information about the inline attribute, https:
read://doc.rust-lang.org/reference.html#inline-attributes
"##,

E0536: r##"
The `not` cfg-predicate was malformed.

Erroneous code example:

```compile_fail,E0536
#[cfg(not())] // error: expected 1 cfg-pattern
pub fn something() {}

pub fn main() {}
```

The `not` predicate expects one cfg-pattern. Example:

```
#[cfg(not(target_os = "linux"))] // ok!
pub fn something() {}

pub fn main() {}
```

For more information about the cfg attribute, read:
https://doc.rust-lang.org/reference.html#conditional-compilation
"##,

E0537: r##"
An unknown predicate was used inside the `cfg` attribute.

Erroneous code example:

```compile_fail,E0537
#[cfg(unknown())] // error: invalid predicate `unknown`
pub fn something() {}

pub fn main() {}
```

The `cfg` attribute supports only three kinds of predicates:

 * any
 * all
 * not

Example:

```
#[cfg(not(target_os = "linux"))] // ok!
pub fn something() {}

pub fn main() {}
```

For more information about the cfg attribute, read:
https://doc.rust-lang.org/reference.html#conditional-compilation
"##,

E0552: r##"
A unrecognized representation attribute was used.

Erroneous code example:

```compile_fail,E0552
#[repr(D)] // error: unrecognized representation hint
struct MyStruct {
    my_field: usize
}
```

You can use a `repr` attribute to tell the compiler how you want a struct or
enum to be laid out in memory.

Make sure you're using one of the supported options:

```
#[repr(C)] // ok!
struct MyStruct {
    my_field: usize
}
```

For more information about specifying representations, see the ["Alternative
Representations" section] of the Rustonomicon.

["Alternative Representations" section]: https://doc.rust-lang.org/nomicon/other-reprs.html
"##,

E0554: r##"
Feature attributes are only allowed on the nightly release channel. Stable or
beta compilers will not comply.

Example of erroneous code (on a stable compiler):

```ignore (depends on release channel)
#![feature(non_ascii_idents)] // error: #![feature] may not be used on the
                              //        stable release channel
```

If you need the feature, make sure to use a nightly release of the compiler
(but be warned that the feature may be removed or altered in the future).
"##,

E0558: r##"
The `export_name` attribute was malformed.

Erroneous code example:

```compile_fail,E0558
#[export_name] // error: export_name attribute has invalid format
pub fn something() {}

fn main() {}
```

The `export_name` attribute expects a string in order to determine the name of
the exported symbol. Example:

```
#[export_name = "some_function"] // ok!
pub fn something() {}

fn main() {}
```
"##,

E0565: r##"
A literal was used in an attribute that doesn't support literals.

Erroneous code example:

```ignore (compile_fail not working here; see Issue #43707)
#![feature(attr_literals)]

#[inline("always")] // error: unsupported literal
pub fn something() {}
```

Literals in attributes are new and largely unsupported. Work to support literals
where appropriate is ongoing. Try using an unquoted name instead:

```
#[inline(always)]
pub fn something() {}
```
"##,

E0583: r##"
A file wasn't found for an out-of-line module.

Erroneous code example:

```ignore (compile_fail not working here; see Issue #43707)
mod file_that_doesnt_exist; // error: file not found for module

fn main() {}
```

Please be sure that a file corresponding to the module exists. If you
want to use a module named `file_that_doesnt_exist`, you need to have a file
named `file_that_doesnt_exist.rs` or `file_that_doesnt_exist/mod.rs` in the
same directory.
"##,

E0585: r##"
A documentation comment that doesn't document anything was found.

Erroneous code example:

```compile_fail,E0585
fn main() {
    // The following doc comment will fail:
    /// This is a useless doc comment!
}
```

Documentation comments need to be followed by items, including functions,
types, modules, etc. Examples:

```
/// I'm documenting the following struct:
struct Foo;

/// I'm documenting the following function:
fn foo() {}
```
"##,

E0586: r##"
An inclusive range was used with no end.

Erroneous code example:

```compile_fail,E0586
#![feature(inclusive_range_syntax)]

fn main() {
    let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
    let x = &tmp[1...]; // error: inclusive range was used with no end
}
```

An inclusive range needs an end in order to *include* it. If you just need a
start and no end, use a non-inclusive range (with `..`):

```
fn main() {
    let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
    let x = &tmp[1..]; // ok!
}
```

Or put an end to your inclusive range:

```
#![feature(inclusive_range_syntax)]

fn main() {
    let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
    let x = &tmp[1...3]; // ok!
}
```
"##,

}

register_diagnostics! {
    E0538, // multiple [same] items
    E0539, // incorrect meta item
    E0540, // multiple rustc_deprecated attributes
    E0541, // unknown meta item
    E0542, // missing 'since'
    E0543, // missing 'reason'
    E0544, // multiple stability levels
    E0545, // incorrect 'issue'
    E0546, // missing 'feature'
    E0547, // missing 'issue'
    E0548, // incorrect stability attribute type
    E0549, // rustc_deprecated attribute must be paired with either stable or unstable attribute
    E0550, // multiple deprecated attributes
    E0551, // incorrect meta item
    E0555, // malformed feature attribute, expected #![feature(...)]
    E0556, // malformed feature, expected just one word
    E0557, // feature has been removed
    E0584, // file for module `..` found at both .. and ..
    E0589, // invalid `repr(align)` attribute
}
<commit_msg>extended information for E0557 feature has been removed<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_snake_case)]

// Error messages for EXXXX errors.
// Each message should start and end with a new line, and be wrapped to 80 characters.
// In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable.
register_long_diagnostics! {

E0178: r##"
In types, the `+` type operator has low precedence, so it is often necessary
to use parentheses.

For example:

```compile_fail,E0178
trait Foo {}

struct Bar<'a> {
    w: &'a Foo + Copy,   // error, use &'a (Foo + Copy)
    x: &'a Foo + 'a,     // error, use &'a (Foo + 'a)
    y: &'a mut Foo + 'a, // error, use &'a mut (Foo + 'a)
    z: fn() -> Foo + 'a, // error, use fn() -> (Foo + 'a)
}
```

More details can be found in [RFC 438].

[RFC 438]: https://github.com/rust-lang/rfcs/pull/438
"##,

E0534: r##"
The `inline` attribute was malformed.

Erroneous code example:

```ignore (compile_fail not working here; see Issue #43707)
#[inline()] // error: expected one argument
pub fn something() {}

fn main() {}
```

The parenthesized `inline` attribute requires the parameter to be specified:

```
#[inline(always)]
fn something() {}
```

or:

```
#[inline(never)]
fn something() {}
```

Alternatively, a paren-less version of the attribute may be used to hint the
compiler about inlining opportunity:

```
#[inline]
fn something() {}
```

For more information about the inline attribute, read:
https://doc.rust-lang.org/reference.html#inline-attributes
"##,

E0535: r##"
An unknown argument was given to the `inline` attribute.

Erroneous code example:

```ignore (compile_fail not working here; see Issue #43707)
#[inline(unknown)] // error: invalid argument
pub fn something() {}

fn main() {}
```

The `inline` attribute only supports two arguments:

 * always
 * never

All other arguments given to the `inline` attribute will return this error.
Example:

```
#[inline(never)] // ok!
pub fn something() {}

fn main() {}
```

For more information about the inline attribute, https:
read://doc.rust-lang.org/reference.html#inline-attributes
"##,

E0536: r##"
The `not` cfg-predicate was malformed.

Erroneous code example:

```compile_fail,E0536
#[cfg(not())] // error: expected 1 cfg-pattern
pub fn something() {}

pub fn main() {}
```

The `not` predicate expects one cfg-pattern. Example:

```
#[cfg(not(target_os = "linux"))] // ok!
pub fn something() {}

pub fn main() {}
```

For more information about the cfg attribute, read:
https://doc.rust-lang.org/reference.html#conditional-compilation
"##,

E0537: r##"
An unknown predicate was used inside the `cfg` attribute.

Erroneous code example:

```compile_fail,E0537
#[cfg(unknown())] // error: invalid predicate `unknown`
pub fn something() {}

pub fn main() {}
```

The `cfg` attribute supports only three kinds of predicates:

 * any
 * all
 * not

Example:

```
#[cfg(not(target_os = "linux"))] // ok!
pub fn something() {}

pub fn main() {}
```

For more information about the cfg attribute, read:
https://doc.rust-lang.org/reference.html#conditional-compilation
"##,

E0552: r##"
A unrecognized representation attribute was used.

Erroneous code example:

```compile_fail,E0552
#[repr(D)] // error: unrecognized representation hint
struct MyStruct {
    my_field: usize
}
```

You can use a `repr` attribute to tell the compiler how you want a struct or
enum to be laid out in memory.

Make sure you're using one of the supported options:

```
#[repr(C)] // ok!
struct MyStruct {
    my_field: usize
}
```

For more information about specifying representations, see the ["Alternative
Representations" section] of the Rustonomicon.

["Alternative Representations" section]: https://doc.rust-lang.org/nomicon/other-reprs.html
"##,

E0554: r##"
Feature attributes are only allowed on the nightly release channel. Stable or
beta compilers will not comply.

Example of erroneous code (on a stable compiler):

```ignore (depends on release channel)
#![feature(non_ascii_idents)] // error: #![feature] may not be used on the
                              //        stable release channel
```

If you need the feature, make sure to use a nightly release of the compiler
(but be warned that the feature may be removed or altered in the future).
"##,

E0557: r##"
A feature attribute named a feature that has been removed.

Erroneous code example:

```compile_fail,E0557
#![feature(managed_boxes)] // error: feature has been removed
```

Delete the offending feature attribute.
"##,

E0558: r##"
The `export_name` attribute was malformed.

Erroneous code example:

```compile_fail,E0558
#[export_name] // error: export_name attribute has invalid format
pub fn something() {}

fn main() {}
```

The `export_name` attribute expects a string in order to determine the name of
the exported symbol. Example:

```
#[export_name = "some_function"] // ok!
pub fn something() {}

fn main() {}
```
"##,

E0565: r##"
A literal was used in an attribute that doesn't support literals.

Erroneous code example:

```ignore (compile_fail not working here; see Issue #43707)
#![feature(attr_literals)]

#[inline("always")] // error: unsupported literal
pub fn something() {}
```

Literals in attributes are new and largely unsupported. Work to support literals
where appropriate is ongoing. Try using an unquoted name instead:

```
#[inline(always)]
pub fn something() {}
```
"##,

E0583: r##"
A file wasn't found for an out-of-line module.

Erroneous code example:

```ignore (compile_fail not working here; see Issue #43707)
mod file_that_doesnt_exist; // error: file not found for module

fn main() {}
```

Please be sure that a file corresponding to the module exists. If you
want to use a module named `file_that_doesnt_exist`, you need to have a file
named `file_that_doesnt_exist.rs` or `file_that_doesnt_exist/mod.rs` in the
same directory.
"##,

E0585: r##"
A documentation comment that doesn't document anything was found.

Erroneous code example:

```compile_fail,E0585
fn main() {
    // The following doc comment will fail:
    /// This is a useless doc comment!
}
```

Documentation comments need to be followed by items, including functions,
types, modules, etc. Examples:

```
/// I'm documenting the following struct:
struct Foo;

/// I'm documenting the following function:
fn foo() {}
```
"##,

E0586: r##"
An inclusive range was used with no end.

Erroneous code example:

```compile_fail,E0586
#![feature(inclusive_range_syntax)]

fn main() {
    let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
    let x = &tmp[1...]; // error: inclusive range was used with no end
}
```

An inclusive range needs an end in order to *include* it. If you just need a
start and no end, use a non-inclusive range (with `..`):

```
fn main() {
    let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
    let x = &tmp[1..]; // ok!
}
```

Or put an end to your inclusive range:

```
#![feature(inclusive_range_syntax)]

fn main() {
    let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
    let x = &tmp[1...3]; // ok!
}
```
"##,

}

register_diagnostics! {
    E0538, // multiple [same] items
    E0539, // incorrect meta item
    E0540, // multiple rustc_deprecated attributes
    E0541, // unknown meta item
    E0542, // missing 'since'
    E0543, // missing 'reason'
    E0544, // multiple stability levels
    E0545, // incorrect 'issue'
    E0546, // missing 'feature'
    E0547, // missing 'issue'
    E0548, // incorrect stability attribute type
    E0549, // rustc_deprecated attribute must be paired with either stable or unstable attribute
    E0550, // multiple deprecated attributes
    E0551, // incorrect meta item
    E0555, // malformed feature attribute, expected #![feature(...)]
    E0556, // malformed feature, expected just one word
    E0584, // file for module `..` found at both .. and ..
    E0589, // invalid `repr(align)` attribute
}
<|endoftext|>


<commit_before>use clap::{App, Arg, AppSettings, SubCommand, ArgMatches};
use common;
use multirust::{Result, Cfg, Error};
use multirust_dist::manifest::Component;
use multirust_utils::utils;
use self_update;
use std::path::Path;

pub fn main() -> Result<()> {
    try!(::self_update::cleanup_self_updater());

    let ref matches = cli().get_matches();
    let verbose = matches.is_present("verbose");
    let ref cfg = try!(common::set_globals(verbose));

    if try!(maybe_upgrade_data(cfg, matches)) {
        return Ok(())
    }

    try!(cfg.check_metadata_version());

    match matches.subcommand() {
        ("default", Some(m)) => try!(default_(cfg, m)),
        ("update", Some(m)) => try!(update(cfg, m)),
        ("run", Some(m)) => try!(run(cfg, m)),
        ("which", Some(m)) => try!(which(cfg, m)),
        ("show", Some(_)) => try!(show(cfg)),
        ("target", Some(c)) => {
            match c.subcommand() {
                ("list", Some(_)) => try!(target_list(cfg)),
                ("add", Some(m)) => try!(target_add(cfg, m)),
                ("remove", Some(m)) => try!(target_remove(cfg, m)),
                (_, _) => unreachable!(),
            }
        }
        ("toolchain", Some(c)) => {
            match c.subcommand() {
                ("list", Some(_)) => try!(common::list_toolchains(cfg)),
                ("link", Some(m)) => try!(toolchain_link(cfg, m)),
                ("remove", Some(m)) => try!(toolchain_remove(cfg, m)),
                (_, _) => unreachable!(),
            }
        }
        ("override", Some(c)) => {
            match c.subcommand() {
                ("list", Some(_)) => try!(common::list_overrides(cfg)),
                ("add", Some(m)) => try!(override_add(cfg, m)),
                ("remove", Some(_)) => try!(override_remove(cfg)),
                (_ ,_) => unreachable!(),
            }
        }
        ("doc", Some(m)) => try!(doc(cfg, m)),
        ("self", Some(c)) => {
            match c.subcommand() {
                ("update", Some(_)) => try!(self_update::update()),
                ("uninstall", Some(m)) => try!(self_uninstall(m)),
                (_ ,_) => unreachable!(),
            }
        }
        (_, _) => {
            try!(update_all_channels(cfg, &matches));
        }
    }

    Ok(())
}

pub fn cli() -> App<'static, 'static> {
    App::new("rustup")
        .version("0.1.1")
        .about("The Rust toolchain installer")
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::DeriveDisplayOrder)
        .arg(Arg::with_name("verbose")
            .help("Enable verbose output")
            .short("v")
            .long("verbose"))
        .arg(Arg::with_name("no-self-update")
            .help("Don't perform self update when running the `rustup` command")
            .long("no-self-update")
            .takes_value(false)
            .hidden(true))
        .subcommand(SubCommand::with_name("default")
            .about("Set the default toolchain")
            .arg(Arg::with_name("toolchain")
                .required(true)))
        .subcommand(SubCommand::with_name("update")
            .about("Install or update a toolchain from a Rust distribution channel")
            .arg(Arg::with_name("toolchain")
                .required(false)))
        .subcommand(SubCommand::with_name("run")
            .about("Run a command with an environment configured for a given toolchain")
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("toolchain")
                .required(true))
            .arg(Arg::with_name("command")
                .required(true).multiple(true)))
        .subcommand(SubCommand::with_name("which")
            .about("Display which binary will be run for a given command")
            .arg(Arg::with_name("command")
                .required(true)))
        .subcommand(SubCommand::with_name("show")
            .about("Show the active toolchain"))
        .subcommand(SubCommand::with_name("target")
            .about("Modify a toolchain's supported targets")
            .setting(AppSettings::SubcommandRequired)
            .subcommand(SubCommand::with_name("list")
                .about("List installed and available targets"))
            .subcommand(SubCommand::with_name("add")
                .about("Add a target to a Rust toolchain")
                .arg(Arg::with_name("target")
                    .required(true)))
            .subcommand(SubCommand::with_name("remove")
                .about("Remove a target  from a Rust toolchain")
                .arg(Arg::with_name("target")
                    .required(true))
                .arg(Arg::with_name("toolchain"))))
        .subcommand(SubCommand::with_name("toolchain")
            .about("Modify or query the installed toolchains")
            .setting(AppSettings::SubcommandRequired)
            .subcommand(SubCommand::with_name("list")
                .about("List installed toolchains"))
            .subcommand(SubCommand::with_name("link")
                .about("Create a custom toolchain by symlinking to a directory")
                .arg(Arg::with_name("toolchain")
                    .required(true))
                .arg(Arg::with_name("path")
                    .required(true)))
            .subcommand(SubCommand::with_name("remove")
                .about("Uninstall a toolchain")
                .arg(Arg::with_name("toolchain")
                     .required(true))))
        .subcommand(SubCommand::with_name("override")
            .about("Modify directory toolchain overrides")
            .setting(AppSettings::SubcommandRequired)
            .subcommand(SubCommand::with_name("list")
                .about("List directory toolchain overrides"))
            .subcommand(SubCommand::with_name("add")
                .about("Set the override toolchain for a directory")
                .arg(Arg::with_name("toolchain")
                     .required(true)))
            .subcommand(SubCommand::with_name("remove")
                .about("Remove the override toolchain for a directory")))
        .subcommand(SubCommand::with_name("self")
            .about("Modify the rustup installation")
            .setting(AppSettings::SubcommandRequired)
            .subcommand(SubCommand::with_name("update")
                .about("Downloadand and install updates to rustup"))
            .subcommand(SubCommand::with_name("uninstall")
                .about("Uninstall rustup.")
                .arg(Arg::with_name("no-prompt")
                     .short("y")))
            .subcommand(SubCommand::with_name("upgrade-data")
                .about("Upgrade the internal data format.")))
}

fn maybe_upgrade_data(cfg: &Cfg, m: &ArgMatches) -> Result<bool> {
    match m.subcommand() {
        ("self", Some(c)) => {
            match c.subcommand() {
                ("upgrade-data", Some(_)) => {
                    try!(cfg.upgrade_data());
                    Ok(true)
                }
                _ => Ok(false),
            }
        }
        _ => Ok(false)
    }
}

fn update_all_channels(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    common::update_all_channels(cfg, !m.is_present("no-self-update"))
}

fn default_(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let ref toolchain = try!(cfg.get_toolchain(toolchain, false));

    let status = if !toolchain.is_custom() {
        Some(try!(toolchain.install_from_dist_if_not_installed()))
    } else if !toolchain.exists() {
        return Err(Error::ToolchainNotInstalled(toolchain.name().to_string()));
    } else {
        None
    };

    try!(toolchain.make_default());

    if let Some(status) = status {
        println!("");
        try!(common::show_channel_update(cfg, toolchain.name(), Ok(status)));
    }

    Ok(())
}

fn update(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let toolchain = if let Some(name) = m.value_of("toolchain") {
        try!(cfg.get_toolchain(name, false))
    } else {
        let ref cwd = try!(utils::current_dir());
        let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd));
        toolchain
    };

    let status = if !toolchain.is_custom() {
        Some(try!(toolchain.install_from_dist()))
    } else if !toolchain.exists() {
        return Err(Error::ToolchainNotInstalled(toolchain.name().to_string()));
    } else {
        None
    };

    if let Some(status) = status {
        println!("");
        try!(common::show_channel_update(cfg, toolchain.name(), Ok(status)));
    }

    Ok(())
}

fn run(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let args = m.values_of("command").unwrap();
    let args: Vec<_> = args.collect();
    let cmd = try!(cfg.create_command_for_toolchain(toolchain, args[0]));

    common::run_inner(cmd, &args)
}

fn which(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let binary = m.value_of("command").expect("");

    let binary_path = try!(cfg.which_binary(&try!(utils::current_dir()), binary))
                          .expect("binary not found");

    try!(utils::assert_is_file(&binary_path));

    println!("{}", binary_path.display());

    Ok(())
}

fn show(cfg: &Cfg) -> Result<()> {
    let ref cwd = try!(utils::current_dir());
    let override_ = try!(cfg.find_override(cwd));
    if let Some((toolchain, reason)) = override_ {
        println!("{} ({})", toolchain.name(), reason);
        return Ok(());
    }

    let toolchain = try!(cfg.find_default());
    if let Some(toolchain) = toolchain {
        println!("{} (default toolchain)", toolchain.name());
        return Ok(());
    }

    println!("no active toolchain");

    Ok(())
}

fn target_list(cfg: &Cfg) -> Result<()> {
    let ref cwd = try!(utils::current_dir());
    let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd));

    common::list_targets(&toolchain)
}

fn target_add(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let target = m.value_of("target").expect("");
    let ref cwd = try!(utils::current_dir());
    let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd));
    let new_component = Component {
        pkg: "rust-std".to_string(),
        target: target.to_string(),
    };

    toolchain.add_component(new_component)
}

fn target_remove(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let target = m.value_of("target").expect("");
    let ref cwd = try!(utils::current_dir());
    let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd));
    let new_component = Component {
        pkg: "rust-std".to_string(),
        target: target.to_string(),
    };

    toolchain.remove_component(new_component)
}

fn toolchain_link(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let ref path = m.value_of("path").expect("");
    let toolchain = try!(cfg.get_toolchain(toolchain, true));

    toolchain.install_from_dir(Path::new(path), true)
}

fn toolchain_remove(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let toolchain = try!(cfg.get_toolchain(toolchain, false));

    toolchain.remove()
}

fn override_add(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let toolchain = try!(cfg.get_toolchain(toolchain, false));

    let status = if !toolchain.is_custom() {
        Some(try!(toolchain.install_from_dist_if_not_installed()))
    } else if !toolchain.exists() {
        return Err(Error::ToolchainNotInstalled(toolchain.name().to_string()));
    } else {
        None
    };

    try!(toolchain.make_override(&try!(utils::current_dir())));

    if let Some(status) = status {
        println!("");
        try!(common::show_channel_update(cfg, toolchain.name(), Ok(status)));
    }

    Ok(())
}

fn override_remove(cfg: &Cfg) -> Result<()> {
    let ref path = try!(utils::current_dir());

    let ref override_db = cfg.override_db;
    let notify_handler = cfg.notify_handler.as_ref();

    if try!(override_db.find(path, notify_handler)).is_none() {
        info!("no override toolchain for '{}'", path.display());
        return Ok(());
    }

    try!(override_db.remove(path, &cfg.temp_cfg, notify_handler));
    info!("override toolchain for '{}' removed", path.display());
    Ok(())
}

fn doc(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let doc_url = if m.is_present("book") {
        "book/index.html"
    } else if m.is_present("std") {
        "std/index.html"
    } else {
        "index.html"
    };

    cfg.open_docs_for_dir(&try!(utils::current_dir()), doc_url)
}

fn self_uninstall(m: &ArgMatches) -> Result<()> {
    let no_prompt = m.is_present("no-prompt");

    self_update::uninstall(no_prompt)
}
<commit_msg>Display help when not given a subcommand. Fixes #193<commit_after>use clap::{App, Arg, AppSettings, SubCommand, ArgMatches};
use common;
use multirust::{Result, Cfg, Error};
use multirust_dist::manifest::Component;
use multirust_utils::utils;
use self_update;
use std::path::Path;

pub fn main() -> Result<()> {
    try!(::self_update::cleanup_self_updater());

    let ref matches = cli().get_matches();
    let verbose = matches.is_present("verbose");
    let ref cfg = try!(common::set_globals(verbose));

    if try!(maybe_upgrade_data(cfg, matches)) {
        return Ok(())
    }

    try!(cfg.check_metadata_version());

    match matches.subcommand() {
        ("default", Some(m)) => try!(default_(cfg, m)),
        ("update", Some(m)) => try!(update(cfg, m)),
        ("run", Some(m)) => try!(run(cfg, m)),
        ("which", Some(m)) => try!(which(cfg, m)),
        ("show", Some(_)) => try!(show(cfg)),
        ("target", Some(c)) => {
            match c.subcommand() {
                ("list", Some(_)) => try!(target_list(cfg)),
                ("add", Some(m)) => try!(target_add(cfg, m)),
                ("remove", Some(m)) => try!(target_remove(cfg, m)),
                (_, _) => unreachable!(),
            }
        }
        ("toolchain", Some(c)) => {
            match c.subcommand() {
                ("list", Some(_)) => try!(common::list_toolchains(cfg)),
                ("link", Some(m)) => try!(toolchain_link(cfg, m)),
                ("remove", Some(m)) => try!(toolchain_remove(cfg, m)),
                (_, _) => unreachable!(),
            }
        }
        ("override", Some(c)) => {
            match c.subcommand() {
                ("list", Some(_)) => try!(common::list_overrides(cfg)),
                ("add", Some(m)) => try!(override_add(cfg, m)),
                ("remove", Some(_)) => try!(override_remove(cfg)),
                (_ ,_) => unreachable!(),
            }
        }
        ("doc", Some(m)) => try!(doc(cfg, m)),
        ("self", Some(c)) => {
            match c.subcommand() {
                ("update", Some(_)) => try!(self_update::update()),
                ("uninstall", Some(m)) => try!(self_uninstall(m)),
                (_ ,_) => unreachable!(),
            }
        }
        (_, _) => {
            try!(update_all_channels(cfg, &matches));
        }
    }

    Ok(())
}

pub fn cli() -> App<'static, 'static> {
    App::new("rustup")
        .version("0.1.1")
        .about("The Rust toolchain installer")
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::DeriveDisplayOrder)
        .arg(Arg::with_name("verbose")
            .help("Enable verbose output")
            .short("v")
            .long("verbose"))
        .arg(Arg::with_name("no-self-update")
            .help("Don't perform self update when running the `rustup` command")
            .long("no-self-update")
            .takes_value(false)
            .hidden(true))
        .subcommand(SubCommand::with_name("default")
            .about("Set the default toolchain")
            .arg(Arg::with_name("toolchain")
                .required(true)))
        .subcommand(SubCommand::with_name("update")
            .about("Install or update a toolchain from a Rust distribution channel")
            .arg(Arg::with_name("toolchain")
                .required(false)))
        .subcommand(SubCommand::with_name("run")
            .about("Run a command with an environment configured for a given toolchain")
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("toolchain")
                .required(true))
            .arg(Arg::with_name("command")
                .required(true).multiple(true)))
        .subcommand(SubCommand::with_name("which")
            .about("Display which binary will be run for a given command")
            .arg(Arg::with_name("command")
                .required(true)))
        .subcommand(SubCommand::with_name("show")
            .about("Show the active toolchain"))
        .subcommand(SubCommand::with_name("target")
            .about("Modify a toolchain's supported targets")
            .setting(AppSettings::SubcommandRequired)
            .subcommand(SubCommand::with_name("list")
                .about("List installed and available targets"))
            .subcommand(SubCommand::with_name("add")
                .about("Add a target to a Rust toolchain")
                .arg(Arg::with_name("target")
                    .required(true)))
            .subcommand(SubCommand::with_name("remove")
                .about("Remove a target  from a Rust toolchain")
                .arg(Arg::with_name("target")
                    .required(true))
                .arg(Arg::with_name("toolchain"))))
        .subcommand(SubCommand::with_name("toolchain")
            .about("Modify or query the installed toolchains")
            .setting(AppSettings::SubcommandRequiredElseHelp)
            .subcommand(SubCommand::with_name("list")
                .about("List installed toolchains"))
            .subcommand(SubCommand::with_name("link")
                .about("Create a custom toolchain by symlinking to a directory")
                .arg(Arg::with_name("toolchain")
                    .required(true))
                .arg(Arg::with_name("path")
                    .required(true)))
            .subcommand(SubCommand::with_name("remove")
                .about("Uninstall a toolchain")
                .arg(Arg::with_name("toolchain")
                     .required(true))))
        .subcommand(SubCommand::with_name("override")
            .about("Modify directory toolchain overrides")
            .setting(AppSettings::SubcommandRequiredElseHelp)
            .subcommand(SubCommand::with_name("list")
                .about("List directory toolchain overrides"))
            .subcommand(SubCommand::with_name("add")
                .about("Set the override toolchain for a directory")
                .arg(Arg::with_name("toolchain")
                     .required(true)))
            .subcommand(SubCommand::with_name("remove")
                .about("Remove the override toolchain for a directory")))
        .subcommand(SubCommand::with_name("self")
            .about("Modify the rustup installation")
            .setting(AppSettings::SubcommandRequiredElseHelp)
            .subcommand(SubCommand::with_name("update")
                .about("Downloadand and install updates to rustup"))
            .subcommand(SubCommand::with_name("uninstall")
                .about("Uninstall rustup.")
                .arg(Arg::with_name("no-prompt")
                     .short("y")))
            .subcommand(SubCommand::with_name("upgrade-data")
                .about("Upgrade the internal data format.")))
}

fn maybe_upgrade_data(cfg: &Cfg, m: &ArgMatches) -> Result<bool> {
    match m.subcommand() {
        ("self", Some(c)) => {
            match c.subcommand() {
                ("upgrade-data", Some(_)) => {
                    try!(cfg.upgrade_data());
                    Ok(true)
                }
                _ => Ok(false),
            }
        }
        _ => Ok(false)
    }
}

fn update_all_channels(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    common::update_all_channels(cfg, !m.is_present("no-self-update"))
}

fn default_(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let ref toolchain = try!(cfg.get_toolchain(toolchain, false));

    let status = if !toolchain.is_custom() {
        Some(try!(toolchain.install_from_dist_if_not_installed()))
    } else if !toolchain.exists() {
        return Err(Error::ToolchainNotInstalled(toolchain.name().to_string()));
    } else {
        None
    };

    try!(toolchain.make_default());

    if let Some(status) = status {
        println!("");
        try!(common::show_channel_update(cfg, toolchain.name(), Ok(status)));
    }

    Ok(())
}

fn update(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let toolchain = if let Some(name) = m.value_of("toolchain") {
        try!(cfg.get_toolchain(name, false))
    } else {
        let ref cwd = try!(utils::current_dir());
        let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd));
        toolchain
    };

    let status = if !toolchain.is_custom() {
        Some(try!(toolchain.install_from_dist()))
    } else if !toolchain.exists() {
        return Err(Error::ToolchainNotInstalled(toolchain.name().to_string()));
    } else {
        None
    };

    if let Some(status) = status {
        println!("");
        try!(common::show_channel_update(cfg, toolchain.name(), Ok(status)));
    }

    Ok(())
}

fn run(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let args = m.values_of("command").unwrap();
    let args: Vec<_> = args.collect();
    let cmd = try!(cfg.create_command_for_toolchain(toolchain, args[0]));

    common::run_inner(cmd, &args)
}

fn which(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let binary = m.value_of("command").expect("");

    let binary_path = try!(cfg.which_binary(&try!(utils::current_dir()), binary))
                          .expect("binary not found");

    try!(utils::assert_is_file(&binary_path));

    println!("{}", binary_path.display());

    Ok(())
}

fn show(cfg: &Cfg) -> Result<()> {
    let ref cwd = try!(utils::current_dir());
    let override_ = try!(cfg.find_override(cwd));
    if let Some((toolchain, reason)) = override_ {
        println!("{} ({})", toolchain.name(), reason);
        return Ok(());
    }

    let toolchain = try!(cfg.find_default());
    if let Some(toolchain) = toolchain {
        println!("{} (default toolchain)", toolchain.name());
        return Ok(());
    }

    println!("no active toolchain");

    Ok(())
}

fn target_list(cfg: &Cfg) -> Result<()> {
    let ref cwd = try!(utils::current_dir());
    let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd));

    common::list_targets(&toolchain)
}

fn target_add(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let target = m.value_of("target").expect("");
    let ref cwd = try!(utils::current_dir());
    let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd));
    let new_component = Component {
        pkg: "rust-std".to_string(),
        target: target.to_string(),
    };

    toolchain.add_component(new_component)
}

fn target_remove(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let target = m.value_of("target").expect("");
    let ref cwd = try!(utils::current_dir());
    let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd));
    let new_component = Component {
        pkg: "rust-std".to_string(),
        target: target.to_string(),
    };

    toolchain.remove_component(new_component)
}

fn toolchain_link(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let ref path = m.value_of("path").expect("");
    let toolchain = try!(cfg.get_toolchain(toolchain, true));

    toolchain.install_from_dir(Path::new(path), true)
}

fn toolchain_remove(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let toolchain = try!(cfg.get_toolchain(toolchain, false));

    toolchain.remove()
}

fn override_add(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let ref toolchain = m.value_of("toolchain").expect("");
    let toolchain = try!(cfg.get_toolchain(toolchain, false));

    let status = if !toolchain.is_custom() {
        Some(try!(toolchain.install_from_dist_if_not_installed()))
    } else if !toolchain.exists() {
        return Err(Error::ToolchainNotInstalled(toolchain.name().to_string()));
    } else {
        None
    };

    try!(toolchain.make_override(&try!(utils::current_dir())));

    if let Some(status) = status {
        println!("");
        try!(common::show_channel_update(cfg, toolchain.name(), Ok(status)));
    }

    Ok(())
}

fn override_remove(cfg: &Cfg) -> Result<()> {
    let ref path = try!(utils::current_dir());

    let ref override_db = cfg.override_db;
    let notify_handler = cfg.notify_handler.as_ref();

    if try!(override_db.find(path, notify_handler)).is_none() {
        info!("no override toolchain for '{}'", path.display());
        return Ok(());
    }

    try!(override_db.remove(path, &cfg.temp_cfg, notify_handler));
    info!("override toolchain for '{}' removed", path.display());
    Ok(())
}

fn doc(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
    let doc_url = if m.is_present("book") {
        "book/index.html"
    } else if m.is_present("std") {
        "std/index.html"
    } else {
        "index.html"
    };

    cfg.open_docs_for_dir(&try!(utils::current_dir()), doc_url)
}

fn self_uninstall(m: &ArgMatches) -> Result<()> {
    let no_prompt = m.is_present("no-prompt");

    self_update::uninstall(no_prompt)
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use aptos_types::{account_address::AccountAddress, transaction::Transaction};
use async_trait::async_trait;
use futures::{
    channel::{mpsc, oneshot},
    stream::FusedStream,
    Stream,
};
use serde::{Deserialize, Serialize};
use std::{
    fmt,
    pin::Pin,
    task::{Context, Poll},
    time::Duration,
};
use thiserror::Error;
use tokio::time::timeout;

const MEMPOOL_NOTIFICATION_CHANNEL_SIZE: usize = 1;

#[derive(Clone, Debug, Deserialize, Error, PartialEq, Serialize)]
pub enum Error {
    #[error("Commit notification failed: {0}")]
    CommitNotificationError(String),
    #[error("Hit the timeout waiting for mempool to respond to the notification!")]
    TimeoutWaitingForMempool,
    #[error("Unexpected error encountered: {0}")]
    UnexpectedErrorEncountered(String),
}

/// The interface between state sync and mempool, allowing state sync to notify
/// mempool of events (e.g., newly committed transactions).
#[async_trait]
pub trait MempoolNotificationSender: Send + Clone + Sync + 'static {
    /// Notify mempool of the newly committed transactions at the specified block timestamp.
    async fn notify_new_commit(
        &self,
        committed_transactions: Vec<Transaction>,
        block_timestamp_usecs: u64,
        notification_timeout_ms: u64,
    ) -> Result<(), Error>;
}

/// This method returns a (MempoolNotifier, MempoolNotificationListener) pair that can be used
/// to allow state sync and mempool to communicate.
///
/// Note: state sync should take the notifier and mempool should take the listener.
pub fn new_mempool_notifier_listener_pair() -> (MempoolNotifier, MempoolNotificationListener) {
    let (notification_sender, notification_receiver) =
        mpsc::channel(MEMPOOL_NOTIFICATION_CHANNEL_SIZE);

    let mempool_notifier = MempoolNotifier::new(notification_sender);
    let mempool_listener = MempoolNotificationListener::new(notification_receiver);

    (mempool_notifier, mempool_listener)
}

/// The state sync component responsible for notifying mempool.
#[derive(Clone, Debug)]
pub struct MempoolNotifier {
    notification_sender: mpsc::Sender<MempoolCommitNotification>,
}

impl MempoolNotifier {
    fn new(notification_sender: mpsc::Sender<MempoolCommitNotification>) -> Self {
        Self {
            notification_sender,
        }
    }
}

#[async_trait]
impl MempoolNotificationSender for MempoolNotifier {
    async fn notify_new_commit(
        &self,
        transactions: Vec<Transaction>,
        block_timestamp_usecs: u64,
        notification_timeout_ms: u64,
    ) -> Result<(), Error> {
        // Get only user transactions from committed transactions
        let user_transactions: Vec<CommittedTransaction> = transactions
            .iter()
            .filter_map(|transaction| match transaction {
                Transaction::UserTransaction(signed_txn) => Some(CommittedTransaction {
                    sender: signed_txn.sender(),
                    sequence_number: signed_txn.sequence_number(),
                }),
                _ => None,
            })
            .collect();

        // It is possible that there is no user transaction present in current block, even in that
        // case, we notify the mempool, because otherwise, mempool might be stuck in an older version
        // of state view, which can be pruned. See https://github.com/aptos-labs/aptos-core/issues/1882
        // for more details.
        // Construct a oneshot channel to receive a mempool response
        let (callback, callback_receiver) = oneshot::channel();
        let commit_notification = MempoolCommitNotification {
            transactions: user_transactions,
            block_timestamp_usecs,
            callback,
        };

        // Send the notification to mempool
        if let Err(error) = self
            .notification_sender
            .clone()
            .try_send(commit_notification)
        {
            return Err(Error::CommitNotificationError(format!(
                "Failed to notify mempool of committed transactions! Error: {:?}",
                error
            )));
        }

        // Handle any responses or a timeout
        if let Ok(response) = timeout(
            Duration::from_millis(notification_timeout_ms),
            callback_receiver,
        )
        .await
        {
            match response {
                Ok(MempoolNotificationResponse::Success) => Ok(()),
                Err(error) => Err(Error::UnexpectedErrorEncountered(format!("{:?}", error))),
            }
        } else {
            Err(Error::TimeoutWaitingForMempool)
        }
    }
}

/// The mempool component responsible for responding to state sync notifications.
#[derive(Debug)]
pub struct MempoolNotificationListener {
    notification_receiver: mpsc::Receiver<MempoolCommitNotification>,
}

impl MempoolNotificationListener {
    fn new(notification_receiver: mpsc::Receiver<MempoolCommitNotification>) -> Self {
        MempoolNotificationListener {
            notification_receiver,
        }
    }

    /// Respond (succesfully) to the commit notification previously sent by state sync.
    pub fn ack_commit_notification(
        &self,
        mempool_commit_notification: MempoolCommitNotification,
    ) -> Result<(), Error> {
        mempool_commit_notification
            .callback
            .send(MempoolNotificationResponse::Success)
            .map_err(|error| Error::UnexpectedErrorEncountered(format!("{:?}", error)))
    }
}

impl Stream for MempoolNotificationListener {
    type Item = MempoolCommitNotification;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.get_mut().notification_receiver).poll_next(cx)
    }
}

impl FusedStream for MempoolNotificationListener {
    fn is_terminated(&self) -> bool {
        self.notification_receiver.is_terminated()
    }
}

/// A notification for newly committed transactions sent by state sync to mempool.
#[derive(Debug)]
pub struct MempoolCommitNotification {
    pub transactions: Vec<CommittedTransaction>,
    pub block_timestamp_usecs: u64, // The timestamp of the committed block.
    pub(crate) callback: oneshot::Sender<MempoolNotificationResponse>,
}

impl fmt::Display for MempoolCommitNotification {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "MempoolCommitNotification [block_timestamp_usecs: {}, txns: {:?}]",
            self.block_timestamp_usecs, self.transactions
        )
    }
}

/// A successfully executed and committed user transaction.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommittedTransaction {
    pub sender: AccountAddress,
    pub sequence_number: u64,
}

impl fmt::Display for CommittedTransaction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.sender, self.sequence_number,)
    }
}

/// A response from mempool for a notification.
///
/// Note: failure responses are not currently used.
#[derive(Debug)]
enum MempoolNotificationResponse {
    Success,
}

#[cfg(test)]
mod tests {
    use crate::{CommittedTransaction, Error, MempoolNotificationSender};
    use aptos_crypto::{ed25519::Ed25519PrivateKey, HashValue, PrivateKey, SigningKey, Uniform};
    use aptos_types::{
        account_address::AccountAddress,
        block_metadata::BlockMetadata,
        chain_id::ChainId,
        transaction::{
            ChangeSet, RawTransaction, Script, SignedTransaction, Transaction, TransactionPayload,
            WriteSetPayload,
        },
        write_set::WriteSetMut,
    };
    use claim::{assert_matches, assert_ok};
    use futures::{executor::block_on, FutureExt, StreamExt};
    use tokio::runtime::{Builder, Runtime};

    #[test]
    fn test_mempool_not_listening() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, mut mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Send a notification and expect a timeout (no listener)
        let notify_result =
            block_on(mempool_notifier.notify_new_commit(vec![create_user_transaction()], 0, 1000));
        assert_matches!(notify_result, Err(Error::TimeoutWaitingForMempool));

        // Drop the receiver and try again
        mempool_listener.notification_receiver.close();
        let notify_result =
            block_on(mempool_notifier.notify_new_commit(vec![create_user_transaction()], 0, 1000));
        assert_matches!(notify_result, Err(Error::CommitNotificationError(_)));
    }

    #[test]
    fn test_zero_timeout() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, _mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Send a notification and expect a timeout (zero timeout)
        let notify_result =
            block_on(mempool_notifier.notify_new_commit(vec![create_user_transaction()], 0, 0));
        assert_matches!(notify_result, Err(Error::TimeoutWaitingForMempool));
    }

    #[test]
    fn test_no_transaction_filtering() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, _mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Create several transactions that should be filtered out
        let mut transactions = vec![];
        for _ in 0..5 {
            transactions.push(create_block_metadata_transaction());
            transactions.push(create_genesis_transaction());
        }

        // Send a notification and verify no timeout because no notification was sent!
        let notify_result =
            block_on(mempool_notifier.notify_new_commit(transactions.clone(), 0, 1000));
        assert_matches!(notify_result, Err(Error::TimeoutWaitingForMempool));

        // Send another notification with a single user transaction now included.
        transactions.push(create_user_transaction());
        let notify_result = block_on(mempool_notifier.notify_new_commit(transactions, 0, 1000));
        assert_matches!(notify_result, Err(Error::TimeoutWaitingForMempool));
    }

    #[test]
    fn test_commit_notification_arrives() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, mut mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Send a notification
        let user_transaction = create_user_transaction();
        let transactions = vec![user_transaction.clone()];
        let block_timestamp_usecs = 101;
        let _ =
            block_on(mempool_notifier.notify_new_commit(transactions, block_timestamp_usecs, 1000));

        // Verify the notification arrives at the receiver
        match mempool_listener.select_next_some().now_or_never() {
            Some(mempool_commit_notification) => match user_transaction {
                Transaction::UserTransaction(signed_transaction) => {
                    assert_eq!(
                        mempool_commit_notification.transactions,
                        vec![CommittedTransaction {
                            sender: signed_transaction.sender(),
                            sequence_number: signed_transaction.sequence_number(),
                        }]
                    );
                    assert_eq!(
                        mempool_commit_notification.block_timestamp_usecs,
                        block_timestamp_usecs
                    );
                }
                result => panic!("Expected user transaction but got: {:?}", result),
            },
            result => panic!("Expected mempool commit notification but got: {:?}", result),
        };
    }

    #[test]
    fn test_mempool_success_response() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, mut mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Spawn a new thread to handle any messages on the receiver
        let _handler = std::thread::spawn(move || loop {
            if let Some(mempool_commit_notification) =
                mempool_listener.select_next_some().now_or_never()
            {
                let _result = mempool_listener.ack_commit_notification(mempool_commit_notification);
            }
        });

        // Send a notification and verify a successful response
        let notify_result = block_on(mempool_notifier.notify_new_commit(
            vec![create_user_transaction()],
            101,
            1000,
        ));
        assert_ok!(notify_result);
    }

    fn create_user_transaction() -> Transaction {
        let private_key = Ed25519PrivateKey::generate_for_testing();
        let public_key = private_key.public_key();

        let transaction_payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![]));
        let raw_transaction = RawTransaction::new(
            AccountAddress::random(),
            0,
            transaction_payload,
            0,
            0,
            0,
            ChainId::new(10),
        );
        let signed_transaction = SignedTransaction::new(
            raw_transaction.clone(),
            public_key,
            private_key.sign(&raw_transaction),
        );

        Transaction::UserTransaction(signed_transaction)
    }

    fn create_block_metadata_transaction() -> Transaction {
        Transaction::BlockMetadata(BlockMetadata::new(
            HashValue::new([0; HashValue::LENGTH]),
            0,
            300000001,
            vec![false],
            AccountAddress::random(),
            vec![],
            1,
        ))
    }

    fn create_genesis_transaction() -> Transaction {
        Transaction::GenesisTransaction(WriteSetPayload::Direct(ChangeSet::new(
            WriteSetMut::new(vec![])
                .freeze()
                .expect("freeze cannot fail"),
            vec![],
        )))
    }

    fn create_runtime() -> Runtime {
        Builder::new_multi_thread().enable_all().build().unwrap()
    }
}
<commit_msg>[mempool] Address minor review comments (#1980)<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use aptos_types::{account_address::AccountAddress, transaction::Transaction};
use async_trait::async_trait;
use futures::{
    channel::{mpsc, oneshot},
    stream::FusedStream,
    Stream,
};
use serde::{Deserialize, Serialize};
use std::{
    fmt,
    pin::Pin,
    task::{Context, Poll},
    time::Duration,
};
use thiserror::Error;
use tokio::time::timeout;

const MEMPOOL_NOTIFICATION_CHANNEL_SIZE: usize = 1;

#[derive(Clone, Debug, Deserialize, Error, PartialEq, Serialize)]
pub enum Error {
    #[error("Commit notification failed: {0}")]
    CommitNotificationError(String),
    #[error("Hit the timeout waiting for mempool to respond to the notification!")]
    TimeoutWaitingForMempool,
    #[error("Unexpected error encountered: {0}")]
    UnexpectedErrorEncountered(String),
}

/// The interface between state sync and mempool, allowing state sync to notify
/// mempool of events (e.g., newly committed transactions).
#[async_trait]
pub trait MempoolNotificationSender: Send + Clone + Sync + 'static {
    /// Notify mempool of the newly committed transactions at the specified block timestamp.
    async fn notify_new_commit(
        &self,
        committed_transactions: Vec<Transaction>,
        block_timestamp_usecs: u64,
        notification_timeout_ms: u64,
    ) -> Result<(), Error>;
}

/// This method returns a (MempoolNotifier, MempoolNotificationListener) pair that can be used
/// to allow state sync and mempool to communicate.
///
/// Note: state sync should take the notifier and mempool should take the listener.
pub fn new_mempool_notifier_listener_pair() -> (MempoolNotifier, MempoolNotificationListener) {
    let (notification_sender, notification_receiver) =
        mpsc::channel(MEMPOOL_NOTIFICATION_CHANNEL_SIZE);

    let mempool_notifier = MempoolNotifier::new(notification_sender);
    let mempool_listener = MempoolNotificationListener::new(notification_receiver);

    (mempool_notifier, mempool_listener)
}

/// The state sync component responsible for notifying mempool.
#[derive(Clone, Debug)]
pub struct MempoolNotifier {
    notification_sender: mpsc::Sender<MempoolCommitNotification>,
}

impl MempoolNotifier {
    fn new(notification_sender: mpsc::Sender<MempoolCommitNotification>) -> Self {
        Self {
            notification_sender,
        }
    }
}

#[async_trait]
impl MempoolNotificationSender for MempoolNotifier {
    async fn notify_new_commit(
        &self,
        transactions: Vec<Transaction>,
        block_timestamp_usecs: u64,
        notification_timeout_ms: u64,
    ) -> Result<(), Error> {
        // Get only user transactions from committed transactions
        let user_transactions: Vec<CommittedTransaction> = transactions
            .iter()
            .filter_map(|transaction| match transaction {
                Transaction::UserTransaction(signed_txn) => Some(CommittedTransaction {
                    sender: signed_txn.sender(),
                    sequence_number: signed_txn.sequence_number(),
                }),
                _ => None,
            })
            .collect();

        // Construct a oneshot channel to receive a mempool response
        let (callback, callback_receiver) = oneshot::channel();
        // Mempool needs to be notified about all transactions (user and non-user transactions).
        // See https://github.com/aptos-labs/aptos-core/issues/1882 for more details.
        let commit_notification = MempoolCommitNotification {
            transactions: user_transactions,
            block_timestamp_usecs,
            callback,
        };

        // Send the notification to mempool
        if let Err(error) = self
            .notification_sender
            .clone()
            .try_send(commit_notification)
        {
            return Err(Error::CommitNotificationError(format!(
                "Failed to notify mempool of committed transactions! Error: {:?}",
                error
            )));
        }

        // Handle any responses or a timeout
        if let Ok(response) = timeout(
            Duration::from_millis(notification_timeout_ms),
            callback_receiver,
        )
        .await
        {
            match response {
                Ok(MempoolNotificationResponse::Success) => Ok(()),
                Err(error) => Err(Error::UnexpectedErrorEncountered(format!("{:?}", error))),
            }
        } else {
            Err(Error::TimeoutWaitingForMempool)
        }
    }
}

/// The mempool component responsible for responding to state sync notifications.
#[derive(Debug)]
pub struct MempoolNotificationListener {
    notification_receiver: mpsc::Receiver<MempoolCommitNotification>,
}

impl MempoolNotificationListener {
    fn new(notification_receiver: mpsc::Receiver<MempoolCommitNotification>) -> Self {
        MempoolNotificationListener {
            notification_receiver,
        }
    }

    /// Respond (succesfully) to the commit notification previously sent by state sync.
    pub fn ack_commit_notification(
        &self,
        mempool_commit_notification: MempoolCommitNotification,
    ) -> Result<(), Error> {
        mempool_commit_notification
            .callback
            .send(MempoolNotificationResponse::Success)
            .map_err(|error| Error::UnexpectedErrorEncountered(format!("{:?}", error)))
    }
}

impl Stream for MempoolNotificationListener {
    type Item = MempoolCommitNotification;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.get_mut().notification_receiver).poll_next(cx)
    }
}

impl FusedStream for MempoolNotificationListener {
    fn is_terminated(&self) -> bool {
        self.notification_receiver.is_terminated()
    }
}

/// A notification for newly committed transactions sent by state sync to mempool.
#[derive(Debug)]
pub struct MempoolCommitNotification {
    pub transactions: Vec<CommittedTransaction>,
    pub block_timestamp_usecs: u64, // The timestamp of the committed block.
    pub(crate) callback: oneshot::Sender<MempoolNotificationResponse>,
}

impl fmt::Display for MempoolCommitNotification {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "MempoolCommitNotification [block_timestamp_usecs: {}, txns: {:?}]",
            self.block_timestamp_usecs, self.transactions
        )
    }
}

/// A successfully executed and committed user transaction.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommittedTransaction {
    pub sender: AccountAddress,
    pub sequence_number: u64,
}

impl fmt::Display for CommittedTransaction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.sender, self.sequence_number,)
    }
}

/// A response from mempool for a notification.
///
/// Note: failure responses are not currently used.
#[derive(Debug)]
enum MempoolNotificationResponse {
    Success,
}

#[cfg(test)]
mod tests {
    use crate::{CommittedTransaction, Error, MempoolNotificationSender};
    use aptos_crypto::{ed25519::Ed25519PrivateKey, HashValue, PrivateKey, SigningKey, Uniform};
    use aptos_types::{
        account_address::AccountAddress,
        block_metadata::BlockMetadata,
        chain_id::ChainId,
        transaction::{
            ChangeSet, RawTransaction, Script, SignedTransaction, Transaction, TransactionPayload,
            WriteSetPayload,
        },
        write_set::WriteSetMut,
    };
    use claim::{assert_matches, assert_ok};
    use futures::{executor::block_on, FutureExt, StreamExt};
    use tokio::runtime::{Builder, Runtime};

    #[test]
    fn test_mempool_not_listening() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, mut mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Send a notification and expect a timeout (no listener)
        let notify_result =
            block_on(mempool_notifier.notify_new_commit(vec![create_user_transaction()], 0, 1000));
        assert_matches!(notify_result, Err(Error::TimeoutWaitingForMempool));

        // Drop the receiver and try again
        mempool_listener.notification_receiver.close();
        let notify_result =
            block_on(mempool_notifier.notify_new_commit(vec![create_user_transaction()], 0, 1000));
        assert_matches!(notify_result, Err(Error::CommitNotificationError(_)));
    }

    #[test]
    fn test_zero_timeout() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, _mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Send a notification and expect a timeout (zero timeout)
        let notify_result =
            block_on(mempool_notifier.notify_new_commit(vec![create_user_transaction()], 0, 0));
        assert_matches!(notify_result, Err(Error::TimeoutWaitingForMempool));
    }

    #[test]
    fn test_no_transaction_filtering() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, _mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Create several non-user transactions
        let mut transactions = vec![];
        for _ in 0..5 {
            transactions.push(create_block_metadata_transaction());
            transactions.push(create_genesis_transaction());
        }

        // Send a notification and verify we get a timeout because mempool didn't respond
        let notify_result =
            block_on(mempool_notifier.notify_new_commit(transactions.clone(), 0, 1000));
        assert_matches!(notify_result, Err(Error::TimeoutWaitingForMempool));

        // Send another notification with a single user transaction now included.
        transactions.push(create_user_transaction());
        let notify_result = block_on(mempool_notifier.notify_new_commit(transactions, 0, 1000));
        assert_matches!(notify_result, Err(Error::TimeoutWaitingForMempool));
    }

    #[test]
    fn test_commit_notification_arrives() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, mut mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Send a notification
        let user_transaction = create_user_transaction();
        let transactions = vec![user_transaction.clone()];
        let block_timestamp_usecs = 101;
        let _ =
            block_on(mempool_notifier.notify_new_commit(transactions, block_timestamp_usecs, 1000));

        // Verify the notification arrives at the receiver
        match mempool_listener.select_next_some().now_or_never() {
            Some(mempool_commit_notification) => match user_transaction {
                Transaction::UserTransaction(signed_transaction) => {
                    assert_eq!(
                        mempool_commit_notification.transactions,
                        vec![CommittedTransaction {
                            sender: signed_transaction.sender(),
                            sequence_number: signed_transaction.sequence_number(),
                        }]
                    );
                    assert_eq!(
                        mempool_commit_notification.block_timestamp_usecs,
                        block_timestamp_usecs
                    );
                }
                result => panic!("Expected user transaction but got: {:?}", result),
            },
            result => panic!("Expected mempool commit notification but got: {:?}", result),
        };
    }

    #[test]
    fn test_mempool_success_response() {
        // Create runtime and mempool notifier
        let runtime = create_runtime();
        let _enter = runtime.enter();
        let (mempool_notifier, mut mempool_listener) = crate::new_mempool_notifier_listener_pair();

        // Spawn a new thread to handle any messages on the receiver
        let _handler = std::thread::spawn(move || loop {
            if let Some(mempool_commit_notification) =
                mempool_listener.select_next_some().now_or_never()
            {
                let _result = mempool_listener.ack_commit_notification(mempool_commit_notification);
            }
        });

        // Send a notification and verify a successful response
        let notify_result = block_on(mempool_notifier.notify_new_commit(
            vec![create_user_transaction()],
            101,
            1000,
        ));
        assert_ok!(notify_result);
    }

    fn create_user_transaction() -> Transaction {
        let private_key = Ed25519PrivateKey::generate_for_testing();
        let public_key = private_key.public_key();

        let transaction_payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![]));
        let raw_transaction = RawTransaction::new(
            AccountAddress::random(),
            0,
            transaction_payload,
            0,
            0,
            0,
            ChainId::new(10),
        );
        let signed_transaction = SignedTransaction::new(
            raw_transaction.clone(),
            public_key,
            private_key.sign(&raw_transaction),
        );

        Transaction::UserTransaction(signed_transaction)
    }

    fn create_block_metadata_transaction() -> Transaction {
        Transaction::BlockMetadata(BlockMetadata::new(
            HashValue::new([0; HashValue::LENGTH]),
            0,
            300000001,
            vec![false],
            AccountAddress::random(),
            vec![],
            1,
        ))
    }

    fn create_genesis_transaction() -> Transaction {
        Transaction::GenesisTransaction(WriteSetPayload::Direct(ChangeSet::new(
            WriteSetMut::new(vec![])
                .freeze()
                .expect("freeze cannot fail"),
            vec![],
        )))
    }

    fn create_runtime() -> Runtime {
        Builder::new_multi_thread().enable_all().build().unwrap()
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Exonum Team
//
// 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.

//! Transaction definitions for the configuration service.

use exonum::{blockchain::{ExecutionResult, Schema as CoreSchema, StoredConfiguration, Transaction},
             crypto::{CryptoHash, Hash, PublicKey},
             messages::Message,
             node::State,
             storage::{Fork, Snapshot}};

use errors::Error as ServiceError;
use schema::{MaybeVote, ProposeData, Schema};

transactions! {
    /// Configuration Service transactions.
    pub ConfigurationTransactions {
        const SERVICE_ID = super::SERVICE_ID;

        /// Propose a new configuration.
        ///
        /// # Notes
        ///
        /// See [`ErrorCode`] for the description of error codes emitted by the `execute()`
        /// method.
        ///
        /// [`ErrorCode`]: enum.ErrorCode.html
        struct Propose {
            /// Sender of the transaction.
            ///
            /// Should be one of validators as per the active configuration.
            from: &PublicKey,

            /// Configuration in JSON format.
            ///
            /// Should be convertible into `StoredConfiguration`.
            cfg: &str,
        }

        /// Vote for the new configuration.
        ///
        /// # Notes
        ///
        /// The stored version of the transaction has a special variant corresponding to absence
        /// of a vote. See [`MaybeVote`] for details.
        ///
        /// See [`ErrorCode`] for the description of error codes emitted by the `execute()`
        /// method.
        ///
        /// [`MaybeVote`]: struct.MaybeVote.html
        /// [`ErrorCode`]: enum.ErrorCode.html
        struct Vote {
            /// Sender of the transaction.
            ///
            /// Should be one of validators as per the active configuration.
            from: &PublicKey,

            /// Hash of the configuration that this vote is for.
            ///
            /// See [crate docs](index.html) for more details on how the hash is calculated.
            cfg_hash: &Hash,
        }

        /// VoteAgainst for the new configuration.
        ///
        /// # Notes
        ///
        /// The stored version of the transaction has a special variant corresponding to absence
        /// of a vote. See [`MaybeVote`] for details.
        ///
        /// See [`ErrorCode`] for the description of error codes emitted by the `execute()`
        /// method.
        ///
        /// [`MaybeVote`]: struct.MaybeVote.html
        /// [`ErrorCode`]: enum.ErrorCode.html
        struct VoteAgainst {
            /// Sender of the transaction.
            ///
            /// Should be one of validators as per the active configuration.
            from: &PublicKey,

            /// Hash of the configuration that this vote is for.
            ///
            /// See [crate docs](index.html) for more details on how the hash is calculated.
            cfg_hash: &Hash,
        }
    }
}

/// Checks if a specified key belongs to one of the current validators.
///
/// # Return value
///
/// The index of the validator authoring the transaction, or `None` if no validator matches
/// the supplied public key.
fn validator_index(snapshot: &Snapshot, key: &PublicKey) -> Option<usize> {
    let actual_config = CoreSchema::new(snapshot).actual_configuration();
    let keys = actual_config.validator_keys;
    keys.iter().position(|k| k.service_key == *key)
}

/// Checks if there is enough votes for a particular configuration hash.
fn enough_votes_to_commit(snapshot: &Snapshot, cfg_hash: &Hash) -> bool {
    let actual_config = CoreSchema::new(snapshot).actual_configuration();

    let schema = Schema::new(snapshot);
    let votes = schema.votes_by_config_hash(cfg_hash);
    let votes_count = votes.iter().filter(|vote| vote.is_consent()).count();
    let majority_count = match actual_config.majority_count {
        Some(majority_count) => majority_count as usize,
        _ => State::byzantine_majority_count(actual_config.validator_keys.len()),
    };

    votes_count >= majority_count
}

impl Propose {
    /// Performs context-dependent checks on the proposal.
    ///
    /// # Return value
    ///
    /// Configuration parsed from the transaction together with its hash.
    fn precheck(&self, snapshot: &Snapshot) -> Result<(StoredConfiguration, Hash), ServiceError> {
        use self::ServiceError::*;
        use exonum::storage::StorageValue;

        let following_config = CoreSchema::new(snapshot).following_configuration();
        if let Some(following) = following_config {
            Err(AlreadyScheduled(following))?;
        }
        if validator_index(snapshot, self.from()).is_none() {
            Err(UnknownSender)?;
        }

        let config_candidate =
            StoredConfiguration::try_deserialize(self.cfg().as_bytes()).map_err(InvalidConfig)?;
        self.check_config_candidate(&config_candidate, snapshot)?;

        let cfg = StoredConfiguration::from_bytes(self.cfg().as_bytes().into());
        let cfg_hash = CryptoHash::hash(&cfg);
        if let Some(old_propose) = Schema::new(snapshot).propose(&cfg_hash) {
            Err(AlreadyProposed(old_propose))?;
        }

        Ok((cfg, cfg_hash))
    }

    /// Checks the consistency of a candidate next configuration.
    fn check_config_candidate(
        &self,
        candidate: &StoredConfiguration,
        snapshot: &Snapshot,
    ) -> Result<(), ServiceError> {
        use self::ServiceError::*;

        let actual_config = CoreSchema::new(snapshot).actual_configuration();
        if candidate.previous_cfg_hash != actual_config.hash() {
            return Err(InvalidConfigRef(actual_config));
        }

        let current_height = CoreSchema::new(snapshot).height().next();
        if candidate.actual_from <= current_height {
            return Err(ActivationInPast(current_height));
        }

        if let Some(proposed_majority_count) = candidate.majority_count {
            let proposed_majority_count = proposed_majority_count as usize;
            let validators_num = candidate.validator_keys.len();
            let min_votes_count = State::byzantine_majority_count(validators_num);

            if proposed_majority_count < min_votes_count || proposed_majority_count > validators_num
            {
                return Err(InvalidMajorityCount {
                    min: min_votes_count,
                    max: validators_num,
                    proposed: proposed_majority_count,
                });
            }
        }

        Ok(())
    }

    /// Saves this proposal to the service schema.
    fn save(&self, fork: &mut Fork, cfg: &StoredConfiguration, cfg_hash: Hash) {
        let prev_cfg = CoreSchema::new(fork.as_ref())
            .configs()
            .get(&cfg.previous_cfg_hash)
            .unwrap();

        // Start writing to storage.
        // NB. DO NOT write to the service schema anywhere else during `Propose::execute`, it may
        // break invariants.
        let mut schema = Schema::new(fork);

        let propose_data = {
            let mut votes_table = schema.votes_by_config_hash_mut(&cfg_hash);
            debug_assert!(votes_table.is_empty());

            let num_validators = prev_cfg.validator_keys.len();
            for _ in 0..num_validators {
                votes_table.push(MaybeVote::none());
            }

            ProposeData::new(
                self.clone(),
                &votes_table.merkle_root(),
                num_validators as u64,
            )
        };

        {
            let mut propose_data_table = schema.propose_data_by_config_hash_mut();
            debug_assert!(propose_data_table.get(&cfg_hash).is_none());
            propose_data_table.put(&cfg_hash, propose_data);
        }

        schema.config_hash_by_ordinal_mut().push(cfg_hash);
    }
}

impl Transaction for Propose {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, fork: &mut Fork) -> ExecutionResult {
        let (cfg, cfg_hash) = self.precheck(fork.as_ref()).map_err(|err| {
            error!("Discarding propose {:?}: {}", self, err);
            err
        })?;

        self.save(fork, &cfg, cfg_hash);
        trace!("Put propose {:?} to config_proposes table", self);
        Ok(())
    }
}

enum VotingDecisionRef<'a> {
    Vote(&'a Vote),
    VoteAgainst(&'a VoteAgainst),
}

impl<'a> VotingDecisionRef<'a> {
    fn sender(&self) -> &PublicKey {
        match *self {
            VotingDecisionRef::Vote(vote) => vote.from(),
            VotingDecisionRef::VoteAgainst(vote) => vote.from(),
        }
    }

    fn cfg_hash(&self) -> &Hash {
        match *self {
            VotingDecisionRef::Vote(vote) => vote.cfg_hash(),
            VotingDecisionRef::VoteAgainst(vote) => vote.cfg_hash(),
        }
    }

    fn to_maybe_vote(&self) -> MaybeVote {
        match *self {
            VotingDecisionRef::Vote(vote) => MaybeVote::from(vote.clone()),
            VotingDecisionRef::VoteAgainst(vote) => MaybeVote::from(vote.clone()),
        }
    }

    /// Checks context-dependent conditions for a `Vote`/`VoteAgainst` transaction.
    ///
    /// # Return value
    ///
    /// Returns a configuration this transaction is for on success, or an error (if any).
    fn precheck(&self, snapshot: &Snapshot) -> Result<StoredConfiguration, ServiceError> {
        use self::ServiceError::*;

        let following_config = CoreSchema::new(snapshot).following_configuration();
        if let Some(following) = following_config {
            Err(AlreadyScheduled(following))?;
        }

        let schema = Schema::new(snapshot);
        if let Some(validator_id) = validator_index(snapshot, self.sender()) {
            let vote = schema
                .votes_by_config_hash(self.cfg_hash())
                .get(validator_id as u64)
                .unwrap();
            if vote.is_some() {
                Err(AlreadyVoted)?;
            }
        } else {
            Err(UnknownSender)?;
        }

        let propose = schema
            .propose(self.cfg_hash())
            .ok_or_else(|| UnknownConfigRef(*self.cfg_hash()))?;

        let parsed = StoredConfiguration::try_deserialize(propose.cfg().as_bytes()).unwrap();
        propose.check_config_candidate(&parsed, snapshot)?;
        Ok(parsed)
    }

    fn save(&self, fork: &mut Fork) {
        use exonum::storage::StorageValue;

        let cfg_hash = self.cfg_hash();
        let propose_data: ProposeData = Schema::new(fork.as_ref())
            .propose_data_by_config_hash()
            .get(self.cfg_hash())
            .unwrap();

        let propose = propose_data.tx_propose();
        let prev_cfg_hash =
            StoredConfiguration::from_bytes(propose.cfg().as_bytes().into()).previous_cfg_hash;
        let prev_cfg = CoreSchema::new(fork.as_ref())
            .configs()
            .get(&prev_cfg_hash)
            .unwrap();
        let validator_id = prev_cfg
            .validator_keys
            .iter()
            .position(|pk| pk.service_key == *self.sender())
            .unwrap();

        // Start writing to storage.
        // NB. DO NOT write to the service schema anywhere else during `Vote::execute`, it may
        // break invariants.
        let mut schema = Schema::new(fork);

        let propose_data = {
            let mut votes = schema.votes_by_config_hash_mut(cfg_hash);
            votes.set(validator_id as u64, self.to_maybe_vote());
            ProposeData::new(
                propose_data.tx_propose(),
                &votes.merkle_root(),
                propose_data.num_validators(),
            )
        };

        schema
            .propose_data_by_config_hash_mut()
            .put(cfg_hash, propose_data);
    }
}

impl<'a> From<&'a Vote> for VotingDecisionRef<'a> {
    fn from(vote: &Vote) -> VotingDecisionRef {
        VotingDecisionRef::Vote(vote)
    }
}

impl<'a> From<&'a VoteAgainst> for VotingDecisionRef<'a> {
    fn from(vote_against: &VoteAgainst) -> VotingDecisionRef {
        VotingDecisionRef::VoteAgainst(vote_against)
    }
}

impl Transaction for Vote {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, fork: &mut Fork) -> ExecutionResult {
        let vote = VotingDecisionRef::from(self);
        let parsed_config = vote.precheck(fork.as_ref()).map_err(|err| {
            error!("Discarding vote {:?}: {}", self, err);
            err
        })?;

        vote.save(fork);
        trace!(
            "Put Vote:{:?} to corresponding cfg votes_by_config_hash table",
            self
        );

        if enough_votes_to_commit(fork.as_ref(), self.cfg_hash()) {
            CoreSchema::new(fork).commit_configuration(parsed_config);
        }
        Ok(())
    }
}

impl Transaction for VoteAgainst {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, fork: &mut Fork) -> ExecutionResult {
        let vote_against = VotingDecisionRef::from(self);
        vote_against.precheck(fork.as_ref()).map_err(|err| {
            error!("Discarding vote against {:?}: {}", self, err);
            err
        })?;

        vote_against.save(fork);
        trace!(
            "Put VoteAgainst:{:?} to corresponding cfg votes_by_config_hash table",
            self
        );

        Ok(())
    }
}
<commit_msg>Fixed panic at unwrap in configuration service [ECR-1642] (#733)<commit_after>// Copyright 2018 The Exonum Team
//
// 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.

//! Transaction definitions for the configuration service.

use exonum::{blockchain::{ExecutionResult, Schema as CoreSchema, StoredConfiguration, Transaction},
             crypto::{CryptoHash, Hash, PublicKey},
             messages::Message,
             node::State,
             storage::{Fork, Snapshot}};

use errors::Error as ServiceError;
use schema::{MaybeVote, ProposeData, Schema};

transactions! {
    /// Configuration Service transactions.
    pub ConfigurationTransactions {
        const SERVICE_ID = super::SERVICE_ID;

        /// Propose a new configuration.
        ///
        /// # Notes
        ///
        /// See [`ErrorCode`] for the description of error codes emitted by the `execute()`
        /// method.
        ///
        /// [`ErrorCode`]: enum.ErrorCode.html
        struct Propose {
            /// Sender of the transaction.
            ///
            /// Should be one of validators as per the active configuration.
            from: &PublicKey,

            /// Configuration in JSON format.
            ///
            /// Should be convertible into `StoredConfiguration`.
            cfg: &str,
        }

        /// Vote for the new configuration.
        ///
        /// # Notes
        ///
        /// The stored version of the transaction has a special variant corresponding to absence
        /// of a vote. See [`MaybeVote`] for details.
        ///
        /// See [`ErrorCode`] for the description of error codes emitted by the `execute()`
        /// method.
        ///
        /// [`MaybeVote`]: struct.MaybeVote.html
        /// [`ErrorCode`]: enum.ErrorCode.html
        struct Vote {
            /// Sender of the transaction.
            ///
            /// Should be one of validators as per the active configuration.
            from: &PublicKey,

            /// Hash of the configuration that this vote is for.
            ///
            /// See [crate docs](index.html) for more details on how the hash is calculated.
            cfg_hash: &Hash,
        }

        /// VoteAgainst for the new configuration.
        ///
        /// # Notes
        ///
        /// The stored version of the transaction has a special variant corresponding to absence
        /// of a vote. See [`MaybeVote`] for details.
        ///
        /// See [`ErrorCode`] for the description of error codes emitted by the `execute()`
        /// method.
        ///
        /// [`MaybeVote`]: struct.MaybeVote.html
        /// [`ErrorCode`]: enum.ErrorCode.html
        struct VoteAgainst {
            /// Sender of the transaction.
            ///
            /// Should be one of validators as per the active configuration.
            from: &PublicKey,

            /// Hash of the configuration that this vote is for.
            ///
            /// See [crate docs](index.html) for more details on how the hash is calculated.
            cfg_hash: &Hash,
        }
    }
}

/// Checks if a specified key belongs to one of the current validators.
///
/// # Return value
///
/// The index of the validator authoring the transaction, or `None` if no validator matches
/// the supplied public key.
fn validator_index(snapshot: &Snapshot, key: &PublicKey) -> Option<usize> {
    let actual_config = CoreSchema::new(snapshot).actual_configuration();
    let keys = actual_config.validator_keys;
    keys.iter().position(|k| k.service_key == *key)
}

/// Checks if there is enough votes for a particular configuration hash.
fn enough_votes_to_commit(snapshot: &Snapshot, cfg_hash: &Hash) -> bool {
    let actual_config = CoreSchema::new(snapshot).actual_configuration();

    let schema = Schema::new(snapshot);
    let votes = schema.votes_by_config_hash(cfg_hash);
    let votes_count = votes.iter().filter(|vote| vote.is_consent()).count();
    let majority_count = match actual_config.majority_count {
        Some(majority_count) => majority_count as usize,
        _ => State::byzantine_majority_count(actual_config.validator_keys.len()),
    };

    votes_count >= majority_count
}

impl Propose {
    /// Performs context-dependent checks on the proposal.
    ///
    /// # Return value
    ///
    /// Configuration parsed from the transaction together with its hash.
    fn precheck(&self, snapshot: &Snapshot) -> Result<(StoredConfiguration, Hash), ServiceError> {
        use self::ServiceError::*;
        use exonum::storage::StorageValue;

        let following_config = CoreSchema::new(snapshot).following_configuration();
        if let Some(following) = following_config {
            Err(AlreadyScheduled(following))?;
        }
        if validator_index(snapshot, self.from()).is_none() {
            Err(UnknownSender)?;
        }

        let config_candidate =
            StoredConfiguration::try_deserialize(self.cfg().as_bytes()).map_err(InvalidConfig)?;
        self.check_config_candidate(&config_candidate, snapshot)?;

        let cfg = StoredConfiguration::from_bytes(self.cfg().as_bytes().into());
        let cfg_hash = CryptoHash::hash(&cfg);
        if let Some(old_propose) = Schema::new(snapshot).propose(&cfg_hash) {
            Err(AlreadyProposed(old_propose))?;
        }

        Ok((cfg, cfg_hash))
    }

    /// Checks the consistency of a candidate next configuration.
    fn check_config_candidate(
        &self,
        candidate: &StoredConfiguration,
        snapshot: &Snapshot,
    ) -> Result<(), ServiceError> {
        use self::ServiceError::*;

        let actual_config = CoreSchema::new(snapshot).actual_configuration();
        if candidate.previous_cfg_hash != actual_config.hash() {
            return Err(InvalidConfigRef(actual_config));
        }

        let current_height = CoreSchema::new(snapshot).height().next();
        if candidate.actual_from <= current_height {
            return Err(ActivationInPast(current_height));
        }

        if let Some(proposed_majority_count) = candidate.majority_count {
            let proposed_majority_count = proposed_majority_count as usize;
            let validators_num = candidate.validator_keys.len();
            let min_votes_count = State::byzantine_majority_count(validators_num);

            if proposed_majority_count < min_votes_count || proposed_majority_count > validators_num
            {
                return Err(InvalidMajorityCount {
                    min: min_votes_count,
                    max: validators_num,
                    proposed: proposed_majority_count,
                });
            }
        }

        Ok(())
    }

    /// Saves this proposal to the service schema.
    fn save(&self, fork: &mut Fork, cfg: &StoredConfiguration, cfg_hash: Hash) {
        let prev_cfg = CoreSchema::new(fork.as_ref())
            .configs()
            .get(&cfg.previous_cfg_hash)
            .unwrap();

        // Start writing to storage.
        // NB. DO NOT write to the service schema anywhere else during `Propose::execute`, it may
        // break invariants.
        let mut schema = Schema::new(fork);

        let propose_data = {
            let mut votes_table = schema.votes_by_config_hash_mut(&cfg_hash);
            debug_assert!(votes_table.is_empty());

            let num_validators = prev_cfg.validator_keys.len();
            for _ in 0..num_validators {
                votes_table.push(MaybeVote::none());
            }

            ProposeData::new(
                self.clone(),
                &votes_table.merkle_root(),
                num_validators as u64,
            )
        };

        {
            let mut propose_data_table = schema.propose_data_by_config_hash_mut();
            debug_assert!(propose_data_table.get(&cfg_hash).is_none());
            propose_data_table.put(&cfg_hash, propose_data);
        }

        schema.config_hash_by_ordinal_mut().push(cfg_hash);
    }
}

impl Transaction for Propose {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, fork: &mut Fork) -> ExecutionResult {
        let (cfg, cfg_hash) = self.precheck(fork.as_ref()).map_err(|err| {
            error!("Discarding propose {:?}: {}", self, err);
            err
        })?;

        self.save(fork, &cfg, cfg_hash);
        trace!("Put propose {:?} to config_proposes table", self);
        Ok(())
    }
}

enum VotingDecisionRef<'a> {
    Vote(&'a Vote),
    VoteAgainst(&'a VoteAgainst),
}

impl<'a> VotingDecisionRef<'a> {
    fn sender(&self) -> &PublicKey {
        match *self {
            VotingDecisionRef::Vote(vote) => vote.from(),
            VotingDecisionRef::VoteAgainst(vote) => vote.from(),
        }
    }

    fn cfg_hash(&self) -> &Hash {
        match *self {
            VotingDecisionRef::Vote(vote) => vote.cfg_hash(),
            VotingDecisionRef::VoteAgainst(vote) => vote.cfg_hash(),
        }
    }

    fn to_maybe_vote(&self) -> MaybeVote {
        match *self {
            VotingDecisionRef::Vote(vote) => MaybeVote::from(vote.clone()),
            VotingDecisionRef::VoteAgainst(vote) => MaybeVote::from(vote.clone()),
        }
    }

    /// Checks context-dependent conditions for a `Vote`/`VoteAgainst` transaction.
    ///
    /// # Return value
    ///
    /// Returns a configuration this transaction is for on success, or an error (if any).
    fn precheck(&self, snapshot: &Snapshot) -> Result<StoredConfiguration, ServiceError> {
        use self::ServiceError::*;

        let following_config = CoreSchema::new(snapshot).following_configuration();
        if let Some(following) = following_config {
            Err(AlreadyScheduled(following))?;
        }

        let schema = Schema::new(snapshot);
        let propose = schema
            .propose(self.cfg_hash())
            .ok_or_else(|| UnknownConfigRef(*self.cfg_hash()))?;

        if let Some(validator_id) = validator_index(snapshot, self.sender()) {
            let vote = schema
                .votes_by_config_hash(self.cfg_hash())
                .get(validator_id as u64)
                .expect("Can't get vote for precheck");

            if vote.is_some() {
                return Err(AlreadyVoted);
            }
        } else {
            return Err(UnknownSender);
        }

        let parsed = StoredConfiguration::try_deserialize(propose.cfg().as_bytes()).unwrap();
        propose.check_config_candidate(&parsed, snapshot)?;
        Ok(parsed)
    }

    fn save(&self, fork: &mut Fork) {
        use exonum::storage::StorageValue;

        let cfg_hash = self.cfg_hash();
        let propose_data: ProposeData = Schema::new(fork.as_ref())
            .propose_data_by_config_hash()
            .get(self.cfg_hash())
            .unwrap();

        let propose = propose_data.tx_propose();
        let prev_cfg_hash =
            StoredConfiguration::from_bytes(propose.cfg().as_bytes().into()).previous_cfg_hash;
        let prev_cfg = CoreSchema::new(fork.as_ref())
            .configs()
            .get(&prev_cfg_hash)
            .unwrap();
        let validator_id = prev_cfg
            .validator_keys
            .iter()
            .position(|pk| pk.service_key == *self.sender())
            .unwrap();

        // Start writing to storage.
        // NB. DO NOT write to the service schema anywhere else during `Vote::execute`, it may
        // break invariants.
        let mut schema = Schema::new(fork);

        let propose_data = {
            let mut votes = schema.votes_by_config_hash_mut(cfg_hash);
            votes.set(validator_id as u64, self.to_maybe_vote());
            ProposeData::new(
                propose_data.tx_propose(),
                &votes.merkle_root(),
                propose_data.num_validators(),
            )
        };

        schema
            .propose_data_by_config_hash_mut()
            .put(cfg_hash, propose_data);
    }
}

impl<'a> From<&'a Vote> for VotingDecisionRef<'a> {
    fn from(vote: &Vote) -> VotingDecisionRef {
        VotingDecisionRef::Vote(vote)
    }
}

impl<'a> From<&'a VoteAgainst> for VotingDecisionRef<'a> {
    fn from(vote_against: &VoteAgainst) -> VotingDecisionRef {
        VotingDecisionRef::VoteAgainst(vote_against)
    }
}

impl Transaction for Vote {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, fork: &mut Fork) -> ExecutionResult {
        let vote = VotingDecisionRef::from(self);
        let parsed_config = vote.precheck(fork.as_ref()).map_err(|err| {
            error!("Discarding vote {:?}: {}", self, err);
            err
        })?;

        vote.save(fork);
        trace!(
            "Put Vote:{:?} to corresponding cfg votes_by_config_hash table",
            self
        );

        if enough_votes_to_commit(fork.as_ref(), self.cfg_hash()) {
            CoreSchema::new(fork).commit_configuration(parsed_config);
        }
        Ok(())
    }
}

impl Transaction for VoteAgainst {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, fork: &mut Fork) -> ExecutionResult {
        let vote_against = VotingDecisionRef::from(self);
        vote_against.precheck(fork.as_ref()).map_err(|err| {
            error!("Discarding vote against {:?}: {}", self, err);
            err
        })?;

        vote_against.save(fork);
        trace!(
            "Put VoteAgainst:{:?} to corresponding cfg votes_by_config_hash table",
            self
        );

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use exonum_testkit::{TestKit, TestKitBuilder};

    use super::{Hash, VotingDecisionRef};
    use errors::Error as ServiceError;
    use tests::{new_tx_config_vote, new_tx_config_vote_against};
    use Service as ConfigurationService;

    #[test]
    fn test_vote_without_propose() {
        let testkit: TestKit = TestKitBuilder::validator()
            .with_validators(4)
            .with_service(ConfigurationService {})
            .create();

        let hash = Hash::default();

        let illegal_vote = new_tx_config_vote(&testkit.network().validators()[3], hash);
        let vote = VotingDecisionRef::from(&illegal_vote);
        let vote_result = vote.precheck(testkit.snapshot().as_ref());

        let illegal_vote_against =
            new_tx_config_vote_against(&testkit.network().validators()[3], hash);
        let vote_against = VotingDecisionRef::from(&illegal_vote_against);
        let vote_against_result = vote_against.precheck(testkit.snapshot().as_ref());

        assert_matches!(vote_result, Err(ServiceError::UnknownConfigRef(_)));
        assert_matches!(vote_against_result, Err(ServiceError::UnknownConfigRef(_)));
    }
}
<|endoftext|>