<commit_before>// Copyright (c) 2016-2020 Fabian Schuiki

//! The high-level intermediate representation for SystemVerilog.
//!
//! After parsing the AST is lowered into this representation, eliminating a lot
//! of syntactic sugar and resolving any syntactic ambiguities.

use crate::crate_prelude::*;
use std::{collections::BTreeSet, sync::Arc};

pub(crate) mod lowering;
mod nodes;
mod visit;

pub(crate) use self::lowering::hir_of;
pub use self::lowering::Hint;
pub use self::nodes::*;
pub use self::visit::*;

make_arenas!(
    /// An arena to allocate HIR nodes into.
    pub struct Arena<'hir> {
        modules: Module<'hir>,
        interfaces: Interface<'hir>,
        ports: Port,
        types: Type,
        exprs: Expr<'hir>,
        inst_target: InstTarget<'hir>,
        insts: Inst<'hir>,
        type_params: TypeParam,
        value_params: ValueParam,
        var_decls: VarDecl,
        procs: Proc,
        stmts: Stmt,
        event_exprs: EventExpr,
        gens: Gen,
        genvar_decls: GenvarDecl,
        typedefs: Typedef,
        assigns: Assign,
        packages: Package,
        enum_variants: EnumVariant,
        subroutines: Subroutine,
    }
);

/// Determine the nodes accessed by another node.
#[moore_derive::query]
pub(crate) fn accessed_nodes<'a>(
    cx: &impl Context<'a>,
    node_id: NodeId,
    env: ParamEnv,
) -> Result<Arc<AccessTable>> {
    let mut k = AccessTableCollector {
        cx,
        env,
        table: AccessTable {
            node_id,
            read: Default::default(),
            written: Default::default(),
        },
    };
    k.visit_node_with_id(node_id, false);
    Ok(Arc::new(k.table))
}

/// A table of accessed nodes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccessTable {
    /// The node for which the analysis was performed.
    pub node_id: NodeId,
    /// All nodes being read.
    pub read: BTreeSet<AccessedNode>,
    /// All nodes being written.
    pub written: BTreeSet<AccessedNode>,
}

/// An accessed node. The `AccessTable` carries this enum as entries.
///
/// The `AccessTable` collects accessed nodes across a design. Most of the nodes
/// are pretty simple, but some -- like an interface signal -- require special
/// care.
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum AccessedNode {
    /// A regular value.
    Regular(NodeId),
    /// An interface signal.
    Intf(NodeId, NodeId),
}

impl AccessedNode {
    /// Get the ID of the accessed node, dropping any context information.
    pub fn id(&self) -> NodeId {
        match *self {
            Self::Regular(id) | Self::Intf(_, id) => id,
        }
    }
}

impl From<NodeId> for AccessedNode {
    fn from(other: NodeId) -> Self {
        Self::Regular(other)
    }
}

/// A visitor for the HIR that populates an access table.
struct AccessTableCollector<'a, C> {
    cx: &'a C,
    env: ParamEnv,
    table: AccessTable,
}

impl<'a, 'gcx: 'a, C> Visitor<'gcx> for AccessTableCollector<'a, C>
where
    C: Context<'gcx>,
{
    type Context = C;
    fn context(&self) -> &C {
        self.cx
    }

    fn visit_expr(&mut self, expr: &'gcx Expr, lvalue: bool) {
        match expr.kind {
            ExprKind::PositionalPattern(ref nodes) => {
                for &node in nodes {
                    self.visit_node_with_id(node, false);
                }
                return;
            }
            ExprKind::RepeatPattern(node, ref nodes) => {
                self.visit_node_with_id(node, false);
                for &node in nodes {
                    self.visit_node_with_id(node, false);
                }
                return;
            }
            ExprKind::NamedPattern(ref pats) => {
                for &(_, node) in pats {
                    self.visit_node_with_id(node, false);
                }
                return;
            }
            ExprKind::Ident(name) => match self.cx.resolve_upwards_or_error(name, expr.id) {
                Ok(binding) => {
                    if self.is_binding_interesting(binding) {
                        if lvalue {
                            self.table.written.insert(binding.into());
                        } else {
                            self.table.read.insert(binding.into());
                        }
                    }
                }
                Err(()) => (),
            },
            ExprKind::LocalIntfSignal { inst, name } => {
                let node = self
                    .cx
                    .type_of(inst.id(), self.env)
                    .map(|ty| ty.get_interface().unwrap())
                    .and_then(|intf| {
                        self.cx
                            .resolve_hierarchical_or_error(name, intf.ast)
                            .map(|def| AccessedNode::Intf(inst.id(), def.node.id()))
                    });
                match node {
                    Ok(node) => {
                        if lvalue {
                            self.table.written.insert(node);
                        } else {
                            self.table.read.insert(node);
                        }
                    }
                    Err(()) => (),
                }
            }
            _ => (),
        }
        walk_expr(self, expr, lvalue)
    }
}

impl<'a, 'gcx: 'a, C> AccessTableCollector<'a, C>
where
    C: Context<'gcx>,
{
    fn is_binding_interesting(&self, binding: NodeId) -> bool {
        if self.cx.is_parent_of(self.table.node_id, binding) {
            return false;
        }
        match self.cx.hir_of(binding) {
            Ok(HirNode::ValueParam(..)) => return false,
            Ok(HirNode::TypeParam(..)) => return false,
            Ok(HirNode::GenvarDecl(..)) => return false,
            Ok(HirNode::EnumVariant(..)) => return false,
            Ok(HirNode::Package(..)) => return false,
            Ok(HirNode::Module(..)) => return false,
            Err(_) => return false,
            _ => (),
        }
        true
    }
}
<commit_msg>svlog: hir: Use MIR for access tables<commit_after>// Copyright (c) 2016-2020 Fabian Schuiki

//! The high-level intermediate representation for SystemVerilog.
//!
//! After parsing the AST is lowered into this representation, eliminating a lot
//! of syntactic sugar and resolving any syntactic ambiguities.

use crate::crate_prelude::*;
use crate::mir::WalkVisitor as _;
use std::{collections::BTreeSet, sync::Arc};

pub(crate) mod lowering;
mod nodes;
mod visit;

pub(crate) use self::lowering::hir_of;
pub use self::lowering::Hint;
pub use self::nodes::*;
pub use self::visit::*;

make_arenas!(
    /// An arena to allocate HIR nodes into.
    pub struct Arena<'hir> {
        modules: Module<'hir>,
        interfaces: Interface<'hir>,
        ports: Port,
        types: Type,
        exprs: Expr<'hir>,
        inst_target: InstTarget<'hir>,
        insts: Inst<'hir>,
        type_params: TypeParam,
        value_params: ValueParam,
        var_decls: VarDecl,
        procs: Proc,
        stmts: Stmt,
        event_exprs: EventExpr,
        gens: Gen,
        genvar_decls: GenvarDecl,
        typedefs: Typedef,
        assigns: Assign,
        packages: Package,
        enum_variants: EnumVariant,
        subroutines: Subroutine,
    }
);

/// Determine the nodes accessed by another node.
#[moore_derive::query]
pub(crate) fn accessed_nodes<'a>(
    cx: &impl Context<'a>,
    node_id: NodeId,
    env: ParamEnv,
) -> Result<Arc<AccessTable>> {
    let mut k = AccessTableCollector {
        cx,
        env,
        table: AccessTable {
            node_id,
            read: Default::default(),
            written: Default::default(),
        },
    };
    k.visit_node_with_id(node_id, false);
    Ok(Arc::new(k.table))
}

/// A table of accessed nodes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccessTable {
    /// The node for which the analysis was performed.
    pub node_id: NodeId,
    /// All nodes being read.
    pub read: BTreeSet<AccessedNode>,
    /// All nodes being written.
    pub written: BTreeSet<AccessedNode>,
}

/// An accessed node. The `AccessTable` carries this enum as entries.
///
/// The `AccessTable` collects accessed nodes across a design. Most of the nodes
/// are pretty simple, but some -- like an interface signal -- require special
/// care.
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum AccessedNode {
    /// A regular value.
    Regular(NodeId),
    /// An interface signal.
    Intf(NodeId, NodeId),
}

impl AccessedNode {
    /// Get the ID of the accessed node, dropping any context information.
    pub fn id(&self) -> NodeId {
        match *self {
            Self::Regular(id) | Self::Intf(_, id) => id,
        }
    }
}

impl From<NodeId> for AccessedNode {
    fn from(other: NodeId) -> Self {
        Self::Regular(other)
    }
}

/// A visitor for the HIR that populates an access table.
struct AccessTableCollector<'a, C> {
    cx: &'a C,
    env: ParamEnv,
    table: AccessTable,
}

impl<'a, 'gcx: 'a, C> Visitor<'gcx> for AccessTableCollector<'a, C>
where
    C: Context<'gcx>,
{
    type Context = C;
    fn context(&self) -> &C {
        self.cx
    }

    fn visit_expr(&mut self, expr: &'gcx Expr, lvalue: bool) {
        if lvalue {
            self.cx.mir_lvalue(expr.id, self.env).walk(self);
        } else {
            self.cx.mir_rvalue(expr.id, self.env).walk(self);
        }
    }
}

impl<'a, 'gcx: 'a, C> mir::Visitor<'gcx> for AccessTableCollector<'a, C>
where
    C: Context<'gcx>,
{
    fn pre_visit_lvalue(&mut self, mir: &mir::Lvalue) -> bool {
        match mir.kind {
            mir::LvalueKind::Var(id) | mir::LvalueKind::Port(id)
                if self.is_binding_interesting(id) =>
            {
                self.table.written.insert(AccessedNode::Regular(id));
                false
            }
            mir::LvalueKind::IntfSignal(intf, sig) if self.is_binding_interesting(intf) => {
                self.table.written.insert(AccessedNode::Intf(intf, sig));
                false
            }
            _ => true,
        }
    }

    fn pre_visit_rvalue(&mut self, mir: &mir::Rvalue) -> bool {
        match mir.kind {
            mir::RvalueKind::Var(id) | mir::RvalueKind::Port(id)
                if self.is_binding_interesting(id) =>
            {
                self.table.read.insert(AccessedNode::Regular(id));
                false
            }
            mir::RvalueKind::IntfSignal(intf, sig) if self.is_binding_interesting(intf) => {
                self.table.read.insert(AccessedNode::Intf(intf, sig));
                false
            }
            _ => true,
        }
    }
}

impl<'a, 'gcx: 'a, C> AccessTableCollector<'a, C>
where
    C: Context<'gcx>,
{
    fn is_binding_interesting(&self, binding: NodeId) -> bool {
        !self.cx.is_parent_of(self.table.node_id, binding)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use habitat_butterfly::member::Health;
use habitat_butterfly::message::swim::Election_Status;

use common;

#[test]
fn three_members_run_election() {
    let mut net = common::net::SwimNet::new(3);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_service(2, "witcher");

    net.add_election(0, "witcher", 0);
    net.add_election(1, "witcher", 0);
    net.add_election(2, "witcher", 0);

    assert_wait_for_election_status!(net, [0..3], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..3, 0..3], "witcher.prod");
}

#[test]
fn three_members_run_election_from_one_starting_rumor() {
    let mut net = common::net::SwimNet::new(3);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_service(2, "witcher");
    net.add_election(0, "witcher", 0);
    assert_wait_for_election_status!(net, [0..3], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..3, 0..3], "witcher.prod");
}

#[test]
fn two_members_fail_to_find_quorum() {
    let mut net = common::net::SwimNet::new(2);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_election(0, "witcher", 0);
    assert_wait_for_equal_election!(net, [0..2, 0..2], "witcher.prod");
    assert_wait_for_election_status!(net, [0..2], "witcher.prod", Election_Status::NoQuorum);
}

#[test]
fn two_members_find_quorum_when_a_third_comes() {
    let mut net = common::net::SwimNet::new(2);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_election(0, "witcher", 0);
    assert_wait_for_equal_election!(net, [0..2, 0..2], "witcher.prod");
    assert_wait_for_election_status!(net, [0..2], "witcher.prod", Election_Status::NoQuorum);

    net.members.push(common::start_server("2", None));
    net.add_service(2, "witcher");
    net.connect(2, 0);
    assert_wait_for_election_status!(net, [0..2], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..3, 0..3], "witcher.prod");
}

#[test]
fn five_members_elect_a_new_leader_when_the_old_one_dies() {
    let mut net = common::net::SwimNet::new(5);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_service(2, "witcher");
    net.add_service(3, "witcher");
    net.add_service(4, "witcher");
    net.add_election(0, "witcher", 0);
    assert_wait_for_election_status!(net, [0..5], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..5, 0..5], "witcher.prod");

    let mut leader_id = String::from("");
    net[0].election_store.with_rumor("witcher.prod", "election", |e| {
        leader_id = String::from(e.unwrap().get_member_id());
    });

    let mut paused = 0;
    for (index, server) in net.iter_mut().enumerate() {
        if server.member_id() == &leader_id[..] {
            paused = index;
        }
    }
    net[paused].pause();
    let paused_id = net[paused].member_id();
    assert_wait_for_health_of!(net, paused, Health::Confirmed);
    if paused == 0 {
        net[1].restart_elections();
    } else {
        net[0].restart_elections();
    }

    for i in 0..5 {
        if !i == paused {
            assert_wait_for_election_status!(net, i, "witcher.prod", Election_Status::Running);
        }
    }

    for i in 0..5 {
        if !i == paused {
            assert_wait_for_election_status!(net, i, "witcher.prod", Election_Status::Finished);
        }
    }

    if paused == 0 {
        net[1].election_store.with_rumor("witcher.prod", "election", |e| {
            assert!(e.unwrap().get_term() == 1);
            assert!(e.unwrap().get_member_id() != paused_id);
        });
    } else {
        net[0].election_store.with_rumor("witcher.prod", "election", |e| {
            assert!(e.unwrap().get_term() == 1);
            assert!(e.unwrap().get_member_id() != paused_id);
        });
    }
}

#[test]
fn five_members_elect_a_new_leader_when_they_are_quorum_partitioned() {
    let mut net = common::net::SwimNet::new(5);
    net[0].member.write().expect("Member lock is poisoned").set_persistent(true);
    net[4].member.write().expect("Member lock is poisoned").set_persistent(true);
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_service(2, "witcher");
    net.add_service(3, "witcher");
    net.add_service(4, "witcher");
    net.add_election(0, "witcher", 1);
    net.connect(0, 1);
    net.connect(1, 2);
    net.connect(2, 3);
    net.connect(3, 4);
    assert_wait_for_health_of!(net, [0..5, 0..5], Health::Alive);
    assert_wait_for_election_status!(net, [0..5], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..5, 0..5], "witcher.prod");

    let mut leader_id = String::from("");
    net[0].election_store.with_rumor("witcher.prod", "election", |e| {
        leader_id = String::from(e.unwrap().get_member_id());
    });

    assert_eq!(leader_id, net[0].member_id());

    let mut leader_index = 0;
    for (index, server) in net.iter_mut().enumerate() {
        if server.member_id() == &leader_id[..] {
            leader_index = index;
        }
    }
    println!("Leader index: {}", leader_index);

    let mut new_leader_id = String::from("");
    net.partition(0..2, 2..5);
    assert_wait_for_health_of!(net, [0..2, 2..5], Health::Confirmed);
    net[0].restart_elections();
    net[4].restart_elections();
    assert_wait_for_election_status!(net, 0, "witcher.prod", Election_Status::NoQuorum);
    assert_wait_for_election_status!(net, 1, "witcher.prod", Election_Status::NoQuorum);
    // Because of the timing here, its possible that the other two finish the election before we
    // get around to testing them, causing a false positive. All we really care about is they all
    // finish.
    assert_wait_for_election_status!(net, 2, "witcher.prod", Election_Status::Running);
    assert_wait_for_election_status!(net, 2, "witcher.prod", Election_Status::Finished);
    assert_wait_for_election_status!(net, 3, "witcher.prod", Election_Status::Finished);
    assert_wait_for_election_status!(net, 4, "witcher.prod", Election_Status::Finished);
    net[0].election_store.with_rumor("witcher.prod", "election", |e| {
        println!("OLD: {:#?}", e);
        new_leader_id = String::from(e.unwrap().get_member_id());
    });
    net[2].election_store.with_rumor("witcher.prod", "election", |e| {
        println!("NEW: {:#?}", e);
        new_leader_id = String::from(e.unwrap().get_member_id());
    });
    assert!(leader_id != new_leader_id);
    println!("Leader {} New {}", leader_id, new_leader_id);
    net.unpartition(0..2, 2..5);
    assert_wait_for_health_of!(net, [0..5, 0..5], Health::Alive);
    assert_wait_for_election_status!(net, 0, "witcher.prod", Election_Status::Finished);
    assert_wait_for_election_status!(net, 1, "witcher.prod", Election_Status::Finished);

    net[4].election_store.with_rumor("witcher.prod", "election", |e| {
        println!("MAJORITY: {:#?}", e);
    });

    net[0].election_store.with_rumor("witcher.prod", "election", |e| {
        println!("MINORITY: {:#?}", e);
        assert_eq!(new_leader_id, String::from(e.unwrap().get_member_id()));
    });
}
<commit_msg>Fix election test<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use habitat_butterfly::member::Health;
use habitat_butterfly::message::swim::Election_Status;

use common;

#[test]
fn three_members_run_election() {
    let mut net = common::net::SwimNet::new(3);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_service(2, "witcher");

    net.add_election(0, "witcher", 0);
    net.add_election(1, "witcher", 0);
    net.add_election(2, "witcher", 0);

    assert_wait_for_election_status!(net, [0..3], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..3, 0..3], "witcher.prod");
}

#[test]
fn three_members_run_election_from_one_starting_rumor() {
    let mut net = common::net::SwimNet::new(3);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_service(2, "witcher");
    net.add_election(0, "witcher", 0);
    assert_wait_for_election_status!(net, [0..3], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..3, 0..3], "witcher.prod");
}

#[test]
fn two_members_fail_to_find_quorum() {
    let mut net = common::net::SwimNet::new(2);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_election(0, "witcher", 0);
    assert_wait_for_equal_election!(net, [0..2, 0..2], "witcher.prod");
    assert_wait_for_election_status!(net, [0..2], "witcher.prod", Election_Status::NoQuorum);
}

#[test]
fn two_members_find_quorum_when_a_third_comes() {
    let mut net = common::net::SwimNet::new(2);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_election(0, "witcher", 0);
    assert_wait_for_equal_election!(net, [0..2, 0..2], "witcher.prod");
    assert_wait_for_election_status!(net, [0..2], "witcher.prod", Election_Status::NoQuorum);

    net.members.push(common::start_server("2", None));
    net.add_service(2, "witcher");
    net.connect(2, 0);
    assert_wait_for_election_status!(net, [0..2], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..3, 0..3], "witcher.prod");
}

#[test]
fn five_members_elect_a_new_leader_when_the_old_one_dies() {
    let mut net = common::net::SwimNet::new(5);
    net.mesh();
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_service(2, "witcher");
    net.add_service(3, "witcher");
    net.add_service(4, "witcher");
    net.add_election(0, "witcher", 0);
    assert_wait_for_election_status!(net, [0..5], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..5, 0..5], "witcher.prod");

    let mut leader_id = String::from("");
    net[0].election_store.with_rumor("witcher.prod", "election", |e| {
        leader_id = String::from(e.unwrap().get_member_id());
    });

    let mut paused = 0;
    for (index, server) in net.iter_mut().enumerate() {
        if server.member_id() == &leader_id[..] {
            paused = index;
        }
    }
    net[paused].pause();
    let paused_id = net[paused].member_id();
    assert_wait_for_health_of!(net, paused, Health::Confirmed);
    if paused == 0 {
        net[1].restart_elections();
    } else {
        net[0].restart_elections();
    }

    for i in 0..5 {
        if !i == paused {
            assert_wait_for_election_status!(net, i, "witcher.prod", Election_Status::Running);
        }
    }

    for i in 0..5 {
        if !i == paused {
            assert_wait_for_election_status!(net, i, "witcher.prod", Election_Status::Finished);
        }
    }

    if paused == 0 {
        net[1].election_store.with_rumor("witcher.prod", "election", |e| {
            assert!(e.unwrap().get_term() == 1);
            assert!(e.unwrap().get_member_id() != paused_id);
        });
    } else {
        net[0].election_store.with_rumor("witcher.prod", "election", |e| {
            assert!(e.unwrap().get_term() == 1);
            assert!(e.unwrap().get_member_id() != paused_id);
        });
    }
}

#[test]
fn five_members_elect_a_new_leader_when_they_are_quorum_partitioned() {
    let mut net = common::net::SwimNet::new(5);
    net[0].member.write().expect("Member lock is poisoned").set_persistent(true);
    net[4].member.write().expect("Member lock is poisoned").set_persistent(true);
    net.add_service(0, "witcher");
    net.add_service(1, "witcher");
    net.add_service(2, "witcher");
    net.add_service(3, "witcher");
    net.add_service(4, "witcher");
    net.add_election(0, "witcher", 1);
    net.connect(0, 1);
    net.connect(1, 2);
    net.connect(2, 3);
    net.connect(3, 4);
    assert_wait_for_health_of!(net, [0..5, 0..5], Health::Alive);
    assert_wait_for_election_status!(net, [0..5], "witcher.prod", Election_Status::Finished);
    assert_wait_for_equal_election!(net, [0..5, 0..5], "witcher.prod");

    let mut leader_id = String::from("");
    net[0].election_store.with_rumor("witcher.prod", "election", |e| {
        leader_id = String::from(e.unwrap().get_member_id());
    });

    assert_eq!(leader_id, net[0].member_id());

    let mut leader_index = 0;
    for (index, server) in net.iter_mut().enumerate() {
        if server.member_id() == &leader_id[..] {
            leader_index = index;
        }
    }
    println!("Leader index: {}", leader_index);

    let mut new_leader_id = String::from("");
    net.partition(0..2, 2..5);
    assert_wait_for_health_of!(net, [0..2, 2..5], Health::Confirmed);
    net[0].restart_elections();
    net[4].restart_elections();
    assert_wait_for_election_status!(net, 0, "witcher.prod", Election_Status::NoQuorum);
    assert_wait_for_election_status!(net, 1, "witcher.prod", Election_Status::NoQuorum);
    assert_wait_for_election_status!(net, 2, "witcher.prod", Election_Status::Finished);
    assert_wait_for_election_status!(net, 3, "witcher.prod", Election_Status::Finished);
    assert_wait_for_election_status!(net, 4, "witcher.prod", Election_Status::Finished);
    net[0].election_store.with_rumor("witcher.prod", "election", |e| {
        println!("OLD: {:#?}", e);
        new_leader_id = String::from(e.unwrap().get_member_id());
    });
    net[2].election_store.with_rumor("witcher.prod", "election", |e| {
        println!("NEW: {:#?}", e);
        new_leader_id = String::from(e.unwrap().get_member_id());
    });
    assert!(leader_id != new_leader_id);
    println!("Leader {} New {}", leader_id, new_leader_id);
    net.unpartition(0..2, 2..5);
    assert_wait_for_health_of!(net, [0..5, 0..5], Health::Alive);
    assert_wait_for_election_status!(net, 0, "witcher.prod", Election_Status::Finished);
    assert_wait_for_election_status!(net, 1, "witcher.prod", Election_Status::Finished);

    net[4].election_store.with_rumor("witcher.prod", "election", |e| {
        println!("MAJORITY: {:#?}", e);
    });

    net[0].election_store.with_rumor("witcher.prod", "election", |e| {
        println!("MINORITY: {:#?}", e);
        assert_eq!(new_leader_id, String::from(e.unwrap().get_member_id()));
    });
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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.

extern crate clap;
extern crate env_logger;
extern crate habitat_core as hcore;
extern crate habitat_common as common;
extern crate habitat_pkg_export_docker as export_docker;
extern crate handlebars;
extern crate rand;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate log;

extern crate failure;
#[macro_use]
extern crate failure_derive;

mod topology;
mod error;
mod manifest;
mod cli;

use hcore::PROGRAM_NAME;
use common::ui::UI;

use export_docker::Result;

use manifest::Manifest;

// Synced with the version of the Habitat operator.
pub const VERSION: &'static str = "0.1.0";

fn main() {
    env_logger::init().unwrap();
    let mut ui = UI::default_with_env();
    if let Err(e) = start(&mut ui) {
        let _ = ui.fatal(e);
        std::process::exit(1)
    }
}

fn start(ui: &mut UI) -> Result<()> {
    let m = cli().get_matches();
    debug!("clap cli args: {:?}", m);

    if !m.is_present("NO_DOCKER_IMAGE") {
        export_docker::export_for_cli_matches(ui, &m)?;
    }
    let mut manifest = Manifest::new_from_cli_matches(ui, &m)?;
    manifest.generate()
}

fn cli<'a, 'b>() -> clap::App<'a, 'b> {
    let name: &str = &*PROGRAM_NAME;
    let about = "Creates a Docker image and Kubernetes manifest for a Habitat package. Habitat \
                 operator must be deployed within the Kubernetes cluster before the generated \
                 manifest can be applied to this cluster.";

    cli::Cli::new(name, about).add_all_args().app
}
<commit_msg>pkg-export-k8s: Make start() more generic<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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.

extern crate clap;
extern crate env_logger;
extern crate habitat_core as hcore;
extern crate habitat_common as common;
extern crate habitat_pkg_export_docker as export_docker;
extern crate handlebars;
extern crate rand;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate log;

extern crate failure;
#[macro_use]
extern crate failure_derive;

mod topology;
mod error;
mod manifest;
mod cli;

use hcore::PROGRAM_NAME;
use common::ui::UI;

use export_docker::Result;

use manifest::Manifest;

// Synced with the version of the Habitat operator.
pub const VERSION: &'static str = "0.1.0";

fn main() {
    env_logger::init().unwrap();
    let mut ui = UI::default_with_env();
    let m = cli().get_matches();
    debug!("clap cli args: {:?}", m);

    if let Err(e) = start(&mut ui, &m) {
        let _ = ui.fatal(e);
        std::process::exit(1)
    }
}

fn start(ui: &mut UI, matches: &clap::ArgMatches) -> Result<()> {
    if !matches.is_present("NO_DOCKER_IMAGE") {
        export_docker::export_for_cli_matches(ui, &matches)?;
    }
    let mut manifest = Manifest::new_from_cli_matches(ui, &matches)?;
    manifest.generate()
}

fn cli<'a, 'b>() -> clap::App<'a, 'b> {
    let name: &str = &*PROGRAM_NAME;
    let about = "Creates a Docker image and Kubernetes manifest for a Habitat package. Habitat \
                 operator must be deployed within the Kubernetes cluster before the generated \
                 manifest can be applied to this cluster.";

    cli::Cli::new(name, about).add_all_args().app
}
<|endoftext|>


<commit_before>//! # Serde
//!
//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data
//! structures efficiently and generically.
//!
//! The Serde ecosystem consists of data structures that know how to serialize
//! and deserialize themselves along with data formats that know how to
//! serialize and deserialize other things. Serde provides the layer by which
//! these two groups interact with each other, allowing any supported data
//! structure to be serialized and deserialized using any supported data format.
//!
//! See the Serde website [https://serde.rs/] for additional documentation and
//! usage examples.
//!
//! [https://serde.rs/]: https://serde.rs/
//!
//! ## Design
//!
//! Where many other languages rely on runtime reflection for serializing data,
//! Serde is instead built on Rust's powerful trait system. A data structure
//! that knows how to serialize and deserialize itself is one that implements
//! Serde's `Serialize` and `Deserialize` traits (or uses Serde's derive
//! attribute to automatically generate implementations at compile time). This
//! avoids any overhead of reflection or runtime type information. In fact in
//! many situations the interaction between data structure and data format can
//! be completely optimized away by the Rust compiler, leaving Serde
//! serialization to perform the same speed as a handwritten serializer for the
//! specific selection of data structure and data format.
//!
//! ## Data formats
//!
//! The following is a partial list of data formats that have been implemented
//! for Serde by the community.
//!
//! - [JSON], the ubiquitous JavaScript Object Notation used by many HTTP APIs.
//! - [Bincode], a compact binary format
//!   used for IPC within the Servo rendering engine.
//! - [CBOR], a Concise Binary Object Representation designed for small message
//!   size without the need for version negotiation.
//! - [YAML], a popular human-friendly configuration language that ain't markup
//!   language.
//! - [MessagePack], an efficient binary format that resembles a compact JSON.
//! - [TOML], a minimal configuration format used by [Cargo].
//! - [Pickle], a format common in the Python world.
//! - [RON], a Rusty Object Notation.
//! - [BSON], the data storage and network transfer format used by MongoDB.
//! - [Avro], a binary format used within Apache Hadoop, with support for schema
//!   definition.
//! - [JSON5], A superset of JSON including some productions from ES5.
//! - [URL], the x-www-form-urlencoded format.
//! - [Envy], a way to deserialize environment variables into Rust structs.
//!   *(deserialization only)*
//! - [Envy Store], a way to deserialize [AWS Parameter Store] parameters into
//!   Rust structs. *(deserialization only)*
//!
//! [JSON]: https://github.com/serde-rs/json
//! [Bincode]: https://github.com/TyOverby/bincode
//! [CBOR]: https://github.com/pyfisch/cbor
//! [YAML]: https://github.com/dtolnay/serde-yaml
//! [MessagePack]: https://github.com/3Hren/msgpack-rust
//! [TOML]: https://github.com/alexcrichton/toml-rs
//! [Pickle]: https://github.com/birkenfeld/serde-pickle
//! [RON]: https://github.com/ron-rs/ron
//! [BSON]: https://github.com/zonyitoo/bson-rs
//! [Avro]: https://github.com/flavray/avro-rs
//! [JSON5]: https://github.com/callum-oakley/json5-rs
//! [URL]: https://github.com/nox/serde_urlencoded
//! [Envy]: https://github.com/softprops/envy
//! [Envy Store]: https://github.com/softprops/envy-store
//! [Cargo]: http://doc.crates.io/manifest.html
//! [AWS Parameter Store]: https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html

////////////////////////////////////////////////////////////////////////////////

// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.98")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Unstable functionality only if the user asks for it. For tracking and
// discussion of these features please refer to this issue:
//
//    https://github.com/serde-rs/serde/issues/812
#![cfg_attr(feature = "unstable", feature(specialization, never_type))]
#![allow(unknown_lints, bare_trait_objects)]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Ignored clippy and clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        // not available in our oldest supported compiler
        checked_conversions,
        empty_enum,
        redundant_field_names,
        redundant_static_lifetimes,
        // integer and float ser/de requires these sorts of casts
        cast_possible_truncation,
        cast_possible_wrap,
        cast_sign_loss,
        // things are often more readable this way
        cast_lossless,
        module_name_repetitions,
        single_match_else,
        type_complexity,
        use_self,
        zero_prefixed_literal,
        // not practical
        needless_pass_by_value,
        similar_names,
        // preference
        doc_markdown,
    )
)]
// Rustc lints.
#![deny(missing_docs, unused_imports)]

////////////////////////////////////////////////////////////////////////////////

#[cfg(feature = "alloc")]
extern crate alloc;

/// A facade around all the types we need from the `std`, `core`, and `alloc`
/// crates. This avoids elaborate import wrangling having to happen in every
/// module.
mod lib {
    mod core {
        #[cfg(not(feature = "std"))]
        pub use core::*;
        #[cfg(feature = "std")]
        pub use std::*;
    }

    pub use self::core::{cmp, iter, mem, num, slice, str};
    pub use self::core::{f32, f64};
    pub use self::core::{i16, i32, i64, i8, isize};
    pub use self::core::{u16, u32, u64, u8, usize};

    pub use self::core::cell::{Cell, RefCell};
    pub use self::core::clone::{self, Clone};
    pub use self::core::convert::{self, From, Into};
    pub use self::core::default::{self, Default};
    pub use self::core::fmt::{self, Debug, Display};
    pub use self::core::marker::{self, PhantomData};
    pub use self::core::ops::Range;
    pub use self::core::option::{self, Option};
    pub use self::core::result::{self, Result};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::borrow::{Cow, ToOwned};
    #[cfg(feature = "std")]
    pub use std::borrow::{Cow, ToOwned};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::string::{String, ToString};
    #[cfg(feature = "std")]
    pub use std::string::{String, ToString};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::vec::Vec;
    #[cfg(feature = "std")]
    pub use std::vec::Vec;

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::boxed::Box;
    #[cfg(feature = "std")]
    pub use std::boxed::Box;

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::rc::{Rc, Weak as RcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::rc::{Rc, Weak as RcWeak};

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::sync::{Arc, Weak as ArcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::sync::{Arc, Weak as ArcWeak};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
    #[cfg(feature = "std")]
    pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};

    #[cfg(feature = "std")]
    pub use std::{error, net};

    #[cfg(feature = "std")]
    pub use std::collections::{HashMap, HashSet};
    #[cfg(feature = "std")]
    pub use std::ffi::{CStr, CString, OsStr, OsString};
    #[cfg(feature = "std")]
    pub use std::hash::{BuildHasher, Hash};
    #[cfg(feature = "std")]
    pub use std::io::Write;
    #[cfg(feature = "std")]
    pub use std::num::Wrapping;
    #[cfg(feature = "std")]
    pub use std::path::{Path, PathBuf};
    #[cfg(feature = "std")]
    pub use std::sync::{Mutex, RwLock};
    #[cfg(feature = "std")]
    pub use std::time::{SystemTime, UNIX_EPOCH};

    #[cfg(all(feature = "std", collections_bound))]
    pub use std::collections::Bound;

    #[cfg(core_reverse)]
    pub use self::core::cmp::Reverse;

    #[cfg(ops_bound)]
    pub use self::core::ops::Bound;

    #[cfg(range_inclusive)]
    pub use self::core::ops::RangeInclusive;

    #[cfg(all(feature = "std", std_atomic))]
    pub use std::sync::atomic::{
        AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU8,
        AtomicUsize, Ordering,
    };
    #[cfg(all(feature = "std", std_atomic64))]
    pub use std::sync::atomic::{AtomicI64, AtomicU64};

    #[cfg(any(core_duration, feature = "std"))]
    pub use self::core::time::Duration;
}

////////////////////////////////////////////////////////////////////////////////

#[macro_use]
mod macros;

#[macro_use]
mod integer128;

pub mod de;
pub mod ser;

#[doc(inline)]
pub use de::{Deserialize, Deserializer};
#[doc(inline)]
pub use ser::{Serialize, Serializer};

// Generated code uses these to support no_std. Not public API.
#[doc(hidden)]
pub mod export;

// Helpers used by generated code and doc tests. Not public API.
#[doc(hidden)]
pub mod private;

// Re-export #[derive(Serialize, Deserialize)].
//
// The reason re-exporting is not enabled by default is that disabling it would
// be annoying for crates that provide handwritten impls or data formats. They
// would need to disable default features and then explicitly re-enable std.
#[cfg(feature = "serde_derive")]
#[allow(unused_imports)]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde_derive")]
#[doc(hidden)]
pub use serde_derive::*;
<commit_msg>Suppress warnings about try! macro<commit_after>//! # Serde
//!
//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data
//! structures efficiently and generically.
//!
//! The Serde ecosystem consists of data structures that know how to serialize
//! and deserialize themselves along with data formats that know how to
//! serialize and deserialize other things. Serde provides the layer by which
//! these two groups interact with each other, allowing any supported data
//! structure to be serialized and deserialized using any supported data format.
//!
//! See the Serde website [https://serde.rs/] for additional documentation and
//! usage examples.
//!
//! [https://serde.rs/]: https://serde.rs/
//!
//! ## Design
//!
//! Where many other languages rely on runtime reflection for serializing data,
//! Serde is instead built on Rust's powerful trait system. A data structure
//! that knows how to serialize and deserialize itself is one that implements
//! Serde's `Serialize` and `Deserialize` traits (or uses Serde's derive
//! attribute to automatically generate implementations at compile time). This
//! avoids any overhead of reflection or runtime type information. In fact in
//! many situations the interaction between data structure and data format can
//! be completely optimized away by the Rust compiler, leaving Serde
//! serialization to perform the same speed as a handwritten serializer for the
//! specific selection of data structure and data format.
//!
//! ## Data formats
//!
//! The following is a partial list of data formats that have been implemented
//! for Serde by the community.
//!
//! - [JSON], the ubiquitous JavaScript Object Notation used by many HTTP APIs.
//! - [Bincode], a compact binary format
//!   used for IPC within the Servo rendering engine.
//! - [CBOR], a Concise Binary Object Representation designed for small message
//!   size without the need for version negotiation.
//! - [YAML], a popular human-friendly configuration language that ain't markup
//!   language.
//! - [MessagePack], an efficient binary format that resembles a compact JSON.
//! - [TOML], a minimal configuration format used by [Cargo].
//! - [Pickle], a format common in the Python world.
//! - [RON], a Rusty Object Notation.
//! - [BSON], the data storage and network transfer format used by MongoDB.
//! - [Avro], a binary format used within Apache Hadoop, with support for schema
//!   definition.
//! - [JSON5], A superset of JSON including some productions from ES5.
//! - [URL], the x-www-form-urlencoded format.
//! - [Envy], a way to deserialize environment variables into Rust structs.
//!   *(deserialization only)*
//! - [Envy Store], a way to deserialize [AWS Parameter Store] parameters into
//!   Rust structs. *(deserialization only)*
//!
//! [JSON]: https://github.com/serde-rs/json
//! [Bincode]: https://github.com/TyOverby/bincode
//! [CBOR]: https://github.com/pyfisch/cbor
//! [YAML]: https://github.com/dtolnay/serde-yaml
//! [MessagePack]: https://github.com/3Hren/msgpack-rust
//! [TOML]: https://github.com/alexcrichton/toml-rs
//! [Pickle]: https://github.com/birkenfeld/serde-pickle
//! [RON]: https://github.com/ron-rs/ron
//! [BSON]: https://github.com/zonyitoo/bson-rs
//! [Avro]: https://github.com/flavray/avro-rs
//! [JSON5]: https://github.com/callum-oakley/json5-rs
//! [URL]: https://github.com/nox/serde_urlencoded
//! [Envy]: https://github.com/softprops/envy
//! [Envy Store]: https://github.com/softprops/envy-store
//! [Cargo]: http://doc.crates.io/manifest.html
//! [AWS Parameter Store]: https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html

////////////////////////////////////////////////////////////////////////////////

// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.98")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Unstable functionality only if the user asks for it. For tracking and
// discussion of these features please refer to this issue:
//
//    https://github.com/serde-rs/serde/issues/812
#![cfg_attr(feature = "unstable", feature(specialization, never_type))]
#![allow(unknown_lints, bare_trait_objects, deprecated)]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Ignored clippy and clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        // not available in our oldest supported compiler
        checked_conversions,
        empty_enum,
        redundant_field_names,
        redundant_static_lifetimes,
        // integer and float ser/de requires these sorts of casts
        cast_possible_truncation,
        cast_possible_wrap,
        cast_sign_loss,
        // things are often more readable this way
        cast_lossless,
        module_name_repetitions,
        single_match_else,
        type_complexity,
        use_self,
        zero_prefixed_literal,
        // not practical
        needless_pass_by_value,
        similar_names,
        // preference
        doc_markdown,
    )
)]
// Rustc lints.
#![deny(missing_docs, unused_imports)]

////////////////////////////////////////////////////////////////////////////////

#[cfg(feature = "alloc")]
extern crate alloc;

/// A facade around all the types we need from the `std`, `core`, and `alloc`
/// crates. This avoids elaborate import wrangling having to happen in every
/// module.
mod lib {
    mod core {
        #[cfg(not(feature = "std"))]
        pub use core::*;
        #[cfg(feature = "std")]
        pub use std::*;
    }

    pub use self::core::{cmp, iter, mem, num, slice, str};
    pub use self::core::{f32, f64};
    pub use self::core::{i16, i32, i64, i8, isize};
    pub use self::core::{u16, u32, u64, u8, usize};

    pub use self::core::cell::{Cell, RefCell};
    pub use self::core::clone::{self, Clone};
    pub use self::core::convert::{self, From, Into};
    pub use self::core::default::{self, Default};
    pub use self::core::fmt::{self, Debug, Display};
    pub use self::core::marker::{self, PhantomData};
    pub use self::core::ops::Range;
    pub use self::core::option::{self, Option};
    pub use self::core::result::{self, Result};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::borrow::{Cow, ToOwned};
    #[cfg(feature = "std")]
    pub use std::borrow::{Cow, ToOwned};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::string::{String, ToString};
    #[cfg(feature = "std")]
    pub use std::string::{String, ToString};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::vec::Vec;
    #[cfg(feature = "std")]
    pub use std::vec::Vec;

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::boxed::Box;
    #[cfg(feature = "std")]
    pub use std::boxed::Box;

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::rc::{Rc, Weak as RcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::rc::{Rc, Weak as RcWeak};

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::sync::{Arc, Weak as ArcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::sync::{Arc, Weak as ArcWeak};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
    #[cfg(feature = "std")]
    pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};

    #[cfg(feature = "std")]
    pub use std::{error, net};

    #[cfg(feature = "std")]
    pub use std::collections::{HashMap, HashSet};
    #[cfg(feature = "std")]
    pub use std::ffi::{CStr, CString, OsStr, OsString};
    #[cfg(feature = "std")]
    pub use std::hash::{BuildHasher, Hash};
    #[cfg(feature = "std")]
    pub use std::io::Write;
    #[cfg(feature = "std")]
    pub use std::num::Wrapping;
    #[cfg(feature = "std")]
    pub use std::path::{Path, PathBuf};
    #[cfg(feature = "std")]
    pub use std::sync::{Mutex, RwLock};
    #[cfg(feature = "std")]
    pub use std::time::{SystemTime, UNIX_EPOCH};

    #[cfg(all(feature = "std", collections_bound))]
    pub use std::collections::Bound;

    #[cfg(core_reverse)]
    pub use self::core::cmp::Reverse;

    #[cfg(ops_bound)]
    pub use self::core::ops::Bound;

    #[cfg(range_inclusive)]
    pub use self::core::ops::RangeInclusive;

    #[cfg(all(feature = "std", std_atomic))]
    pub use std::sync::atomic::{
        AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU8,
        AtomicUsize, Ordering,
    };
    #[cfg(all(feature = "std", std_atomic64))]
    pub use std::sync::atomic::{AtomicI64, AtomicU64};

    #[cfg(any(core_duration, feature = "std"))]
    pub use self::core::time::Duration;
}

////////////////////////////////////////////////////////////////////////////////

#[macro_use]
mod macros;

#[macro_use]
mod integer128;

pub mod de;
pub mod ser;

#[doc(inline)]
pub use de::{Deserialize, Deserializer};
#[doc(inline)]
pub use ser::{Serialize, Serializer};

// Generated code uses these to support no_std. Not public API.
#[doc(hidden)]
pub mod export;

// Helpers used by generated code and doc tests. Not public API.
#[doc(hidden)]
pub mod private;

// Re-export #[derive(Serialize, Deserialize)].
//
// The reason re-exporting is not enabled by default is that disabling it would
// be annoying for crates that provide handwritten impls or data formats. They
// would need to disable default features and then explicitly re-enable std.
#[cfg(feature = "serde_derive")]
#[allow(unused_imports)]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde_derive")]
#[doc(hidden)]
pub use serde_derive::*;
<|endoftext|>


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

//! The module containing building blocks for creating blockchains powered by the
//! Exonum framework.
//!
//! Services are the main extension point for the Exonum framework. To create your own service on
//! top of Exonum blockchain you need to define following things:
//!
//! - Define your own information schema.
//! - Create one or more types of messages using a macro `message!` and implement
//! `Transaction` trait for them.
//! - Create data structure that implements `Service` trait.
//! - Optionally you can write api handlers.
//!
//! You may follow the [`minibank`][1] tutorial to get experience of programming your services.
//!
//! [1]: https://github.com/exonum/exonum-doc/blob/master/src/get-started/create-service.md

use std::sync::Arc;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::mem;
use std::fmt;
use std::panic;
use std::net::SocketAddr;

use vec_map::VecMap;
use byteorder::{ByteOrder, LittleEndian};
use mount::Mount;

use crypto::{self, Hash, PublicKey, SecretKey};
use messages::{CONSENSUS as CORE_SERVICE, Precommit, RawMessage, Connect};
use storage::{Database, Error, Fork, Patch, Snapshot};
use helpers::{Height, ValidatorId};
use node::ApiSender;

pub use self::block::{Block, BlockProof, SCHEMA_MAJOR_VERSION};
pub use self::schema::{gen_prefix, Schema, TxLocation};
pub use self::genesis::GenesisConfig;
pub use self::config::{ConsensusConfig, StoredConfiguration, TimeoutAdjusterConfig, ValidatorKeys};
pub use self::service::{ApiContext, Service, ServiceContext, SharedNodeState, Transaction};

mod block;
mod schema;
mod genesis;
mod service;
#[cfg(test)]
mod tests;

pub mod config;

/// Exonum blockchain instance with the concrete services set and data storage.
/// Only blockchains with the identical set of services and genesis block can be combined
/// into the single network.
pub struct Blockchain {
    db: Arc<Database>,
    service_map: Arc<VecMap<Box<Service>>>,
    service_keypair: (PublicKey, SecretKey),
    api_sender: ApiSender,
}

impl Blockchain {
    /// Constructs a blockchain for the given `storage` and list of `services`.
    pub fn new(
        storage: Box<Database>,
        services: Vec<Box<Service>>,
        service_public_key: PublicKey,
        service_secret_key: SecretKey,
        api_sender: ApiSender,
    ) -> Blockchain {
        let mut service_map = VecMap::new();
        for service in services {
            let id = service.service_id() as usize;
            if service_map.contains_key(id) {
                panic!(
                    "Services have already contain service with id={}, please change it.",
                    id
                );
            }
            service_map.insert(id, service);
        }

        Blockchain {
            db: storage.into(),
            service_map: Arc::new(service_map),
            service_keypair: (service_public_key, service_secret_key),
            api_sender,
        }
    }

    /// Recreates the blockchain to reuse with a sandbox.
    #[doc(hidden)]
    pub fn clone_with_api_sender(&self, api_sender: ApiSender) -> Blockchain {
        Blockchain {
            api_sender,
            ..self.clone()
        }
    }

    /// Returns service `VecMap` for all our services.
    pub fn service_map(&self) -> &Arc<VecMap<Box<Service>>> {
        &self.service_map
    }

    /// Creates a readonly snapshot of the current storage state.
    pub fn snapshot(&self) -> Box<Snapshot> {
        self.db.snapshot()
    }

    /// Creates snapshot of the current storage state that can be later committed into storage
    /// via `merge` method.
    pub fn fork(&self) -> Fork {
        self.db.fork()
    }

    /// Tries to create a `Transaction` object from the given raw message.
    /// Raw message can be converted into `Transaction` object only
    /// if following conditions are met.
    ///
    /// - Blockchain has service with the `service_id` of given raw message.
    /// - Service can deserialize given raw message.
    pub fn tx_from_raw(&self, raw: RawMessage) -> Option<Box<Transaction>> {
        let id = raw.service_id() as usize;
        self.service_map.get(id).and_then(|service| {
            service.tx_from_raw(raw).ok()
        })
    }

    /// Commits changes from the patch to the blockchain storage.
    /// See [`Fork`](../storage/struct.Fork.html) for details.
    pub fn merge(&mut self, patch: Patch) -> Result<(), Error> {
        self.db.merge(patch)
    }

    /// Returns the hash of latest committed block.
    ///
    /// # Panics
    ///
    /// - If the genesis block was not committed.
    pub fn last_hash(&self) -> Hash {
        Schema::new(&self.snapshot())
            .block_hashes_by_height()
            .last()
            .unwrap_or_else(Hash::default)
    }

    /// Returns the latest committed block.
    ///
    /// # Panics
    ///
    /// - If the genesis block was not committed.
    pub fn last_block(&self) -> Block {
        Schema::new(&self.snapshot()).last_block()
    }

    /// Creates and commits the genesis block for the given genesis configuration.
    pub fn create_genesis_block(&mut self, cfg: GenesisConfig) -> Result<(), Error> {
        let mut config_propose = StoredConfiguration {
            previous_cfg_hash: Hash::zero(),
            actual_from: Height::zero(),
            validator_keys: cfg.validator_keys,
            consensus: cfg.consensus,
            services: BTreeMap::new(),
        };

        let patch = {
            let mut fork = self.fork();
            // Update service tables
            for (_, service) in self.service_map.iter() {
                let cfg = service.initialize(&mut fork);
                let name = service.service_name();
                if config_propose.services.contains_key(name) {
                    panic!(
                        "Services already contain service with '{}' name, please change it",
                        name
                    );
                }
                config_propose.services.insert(name.into(), cfg);
            }
            // Commit actual configuration
            {
                let mut schema = Schema::new(&mut fork);
                if schema.block_hash_by_height(Height::zero()).is_some() {
                    // TODO create genesis block for MemoryDB and compare in hash with zero block
                    return Ok(());
                }
                schema.commit_configuration(config_propose);
            };
            self.merge(fork.into_patch())?;
            self.create_patch(ValidatorId::zero(), Height::zero(), &[], &BTreeMap::new())
                .1
        };
        self.merge(patch)?;
        Ok(())
    }

    /// Helper function to map tuple (`u16`, `u16`) of service table coordinates
    /// to 32 byte value for use as `MerklePatriciaTable` key (it currently
    /// supports only fixed size keys). `hash` function is used to distribute
    /// keys uniformly (compared to padding).
    /// # Arguments
    ///
    /// * `service_id` - `service_id` as returned by instance of type of
    /// `Service` trait
    /// * `table_idx` - index of service table in `Vec`, returned by
    /// `state_hash` method of instance of type of `Service` trait
    // also, it was the first idea around, to use `hash`
    pub fn service_table_unique_key(service_id: u16, table_idx: usize) -> Hash {
        debug_assert!(table_idx <= u16::max_value() as usize);
        let size = mem::size_of::<u16>();
        let mut vec = vec![0; 2 * size];
        LittleEndian::write_u16(&mut vec[0..size], service_id);
        LittleEndian::write_u16(&mut vec[size..2 * size], table_idx as u16);
        crypto::hash(&vec)
    }

    /// Executes the given transactions from pool.
    /// Then it collects the resulting changes from the current storage state and returns them
    /// with the hash of resulting block.
    pub fn create_patch(
        &self,
        proposer_id: ValidatorId,
        height: Height,
        tx_hashes: &[Hash],
        pool: &BTreeMap<Hash, Box<Transaction>>,
    ) -> (Hash, Patch) {
        // Create fork
        let mut fork = self.fork();

        let block_hash = {
            // Get last hash
            let last_hash = self.last_hash();
            // Save & execute transactions
            for (index, hash) in tx_hashes.iter().enumerate() {
                let tx = pool.get(hash).expect(
                    "BUG: Cannot find transaction in pool.",
                );

                fork.checkpoint();

                let r = panic::catch_unwind(panic::AssertUnwindSafe(|| { tx.execute(&mut fork); }));

                match r {
                    Ok(..) => fork.commit(),
                    Err(err) => {
                        if err.is::<Error>() {
                            // Continue panic unwind if the reason is StorageError
                            panic::resume_unwind(err);
                        }
                        fork.rollback();
                        error!("{:?} transaction execution failed: {:?}", tx, err);
                    }
                }

                let mut schema = Schema::new(&mut fork);
                schema.transactions_mut().put(hash, tx.raw().clone());
                schema.block_txs_mut(height).push(*hash);
                let location = TxLocation::new(height, index as u64);
                schema.tx_location_by_tx_hash_mut().put(hash, location);
            }

            // Get tx & state hash
            let (tx_hash, state_hash) = {
                let state_hashes = {
                    let schema = Schema::new(&fork);

                    let vec_core_state = schema.core_state_hash();
                    let mut state_hashes = Vec::new();

                    for (idx, core_table_hash) in vec_core_state.into_iter().enumerate() {
                        let key = Blockchain::service_table_unique_key(CORE_SERVICE, idx);
                        state_hashes.push((key, core_table_hash));
                    }

                    for service in self.service_map.values() {
                        let service_id = service.service_id();
                        let vec_service_state = service.state_hash(&fork);
                        for (idx, service_table_hash) in vec_service_state.into_iter().enumerate() {
                            let key = Blockchain::service_table_unique_key(service_id, idx);
                            state_hashes.push((key, service_table_hash));
                        }
                    }

                    state_hashes
                };

                let mut schema = Schema::new(&mut fork);

                let state_hash = {
                    let mut sum_table = schema.state_hash_aggregator_mut();
                    for (key, hash) in state_hashes {
                        sum_table.put(&key, hash)
                    }
                    sum_table.root_hash()
                };

                let tx_hash = schema.block_txs(height).root_hash();

                (tx_hash, state_hash)
            };

            // Create block
            let block = Block::new(
                SCHEMA_MAJOR_VERSION,
                proposer_id,
                height,
                tx_hashes.len() as u32,
                &last_hash,
                &tx_hash,
                &state_hash,
            );
            trace!("execute block = {:?}", block);
            // Eval block hash
            let block_hash = block.hash();
            // Update height
            let mut schema = Schema::new(&mut fork);
            schema.block_hashes_by_height_mut().push(block_hash);
            // Save block
            schema.blocks_mut().put(&block_hash, block);

            block_hash
        };

        (block_hash, fork.into_patch())
    }

    /// Commits to the storage block that proposes by node `State`.
    /// After that invokes `handle_commit` for each service in order of their identifiers
    /// and returns the list of transactions which which were created by the `handle_commit` event.
    #[cfg_attr(feature = "flame_profile", flame)]
    pub fn commit<'a, I>(
        &mut self,
        patch: &Patch,
        block_hash: Hash,
        precommits: I,
    ) -> Result<(), Error>
    where
        I: Iterator<Item = &'a Precommit>,
    {
        let patch = {
            let mut fork = {
                let mut fork = self.db.fork();
                fork.merge(patch.clone()); // FIXME: avoid cloning here
                fork
            };

            {
                let mut schema = Schema::new(&mut fork);
                for precommit in precommits {
                    schema.precommits_mut(&block_hash).push(precommit.clone());
                }
            }
            fork.into_patch()
        };
        self.merge(patch)?;
        // Initializes the context after merge.
        let context = ServiceContext::new(
            self.service_keypair.0,
            self.service_keypair.1.clone(),
            self.api_sender.clone(),
            self.fork(),
        );
        // Invokes `handle_commit` for each service in order of their identifiers
        for service in self.service_map.values() {
            service.handle_commit(&context);
        }
        Ok(())
    }

    /// Returns `Mount` object that aggregates public api handlers.
    pub fn mount_public_api(&self) -> Mount {
        let context = self.api_context();
        let mut mount = Mount::new();
        for service in self.service_map.values() {
            if let Some(handler) = service.public_api_handler(&context) {
                mount.mount(service.service_name(), handler);
            }
        }
        mount
    }

    /// Returns `Mount` object that aggregates private api handlers.
    pub fn mount_private_api(&self) -> Mount {
        let context = self.api_context();
        let mut mount = Mount::new();
        for service in self.service_map.values() {
            if let Some(handler) = service.private_api_handler(&context) {
                mount.mount(service.service_name(), handler);
            }
        }
        mount
    }

    fn api_context(&self) -> ApiContext {
        ApiContext::from_parts(
            self,
            self.api_sender.clone(),
            &self.service_keypair.0,
            &self.service_keypair.1,
        )
    }

    /// Saves peer to the peers cache
    pub fn save_peer(&mut self, pubkey: &PublicKey, peer: Connect) {
        let mut fork = self.fork();

        {
            let mut schema = Schema::new(&mut fork);
            schema.peers_cache_mut().put(pubkey, peer);
        }

        self.merge(fork.into_patch()).expect(
            "Unable to save peer to the peers cache",
        );
    }

    /// Removes peer from the peers cache
    pub fn remove_peer_with_addr(&mut self, addr: &SocketAddr) {
        let mut fork = self.fork();

        {
            let mut schema = Schema::new(&mut fork);
            let mut peers = schema.peers_cache_mut();
            let peer = peers.iter().find(|&(_, ref v)| v.addr() == *addr);
            if let Some(pubkey) = peer.map(|(k, _)| k) {
                peers.remove(&pubkey);
            }
        }

        self.merge(fork.into_patch()).expect(
            "Unable to remove peer from the peers cache",
        );
    }

    /// Recover cached peers if any.
    pub fn get_saved_peers(&self) -> HashMap<PublicKey, Connect> {
        let schema = Schema::new(self.snapshot());
        let peers_cache = schema.peers_cache();
        let it = peers_cache.iter().map(|(k, v)| (k, v.clone()));
        it.collect()
    }
}

impl fmt::Debug for Blockchain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Blockchain(..)")
    }
}

impl Clone for Blockchain {
    fn clone(&self) -> Blockchain {
        Blockchain {
            db: Arc::clone(&self.db),
            service_map: Arc::clone(&self.service_map),
            api_sender: self.api_sender.clone(),
            service_keypair: self.service_keypair.clone(),
        }
    }
}
<commit_msg>Edit `blockchain` module docs for clarity<commit_after>// Copyright 2017 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.

//! The module containing building blocks for creating blockchains powered by the
//! Exonum framework.
//!
//! Services are the main extension point for the Exonum framework. To create your service on
//! top of Exonum blockchain you need to do the following:
//!
//! - Define your own information schema.
//! - Create one or more transaction types using the [`message!`] macro and implement
//!   the [`Transaction`] trait for them.
//! - Create a data structure implementing the [`Service`] trait.
//! - Optionally you can write API handlers for the service.
//!
//! You may consult [the service creation tutorial][doc:create-service] for a more detailed
//! manual how to create services.
//!
//! [`message!`]: ../macro.message.html
//! [`Transaction`]: ./trait.Transaction.html
//! [`Service`]: ./trait.Service.html
//! [doc:create-service]: https://exonum.com/doc/get-started/create-service

use std::sync::Arc;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::mem;
use std::fmt;
use std::panic;
use std::net::SocketAddr;

use vec_map::VecMap;
use byteorder::{ByteOrder, LittleEndian};
use mount::Mount;

use crypto::{self, Hash, PublicKey, SecretKey};
use messages::{CONSENSUS as CORE_SERVICE, Precommit, RawMessage, Connect};
use storage::{Database, Error, Fork, Patch, Snapshot};
use helpers::{Height, ValidatorId};
use node::ApiSender;

pub use self::block::{Block, BlockProof, SCHEMA_MAJOR_VERSION};
pub use self::schema::{gen_prefix, Schema, TxLocation};
pub use self::genesis::GenesisConfig;
pub use self::config::{ConsensusConfig, StoredConfiguration, TimeoutAdjusterConfig, ValidatorKeys};
pub use self::service::{ApiContext, Service, ServiceContext, SharedNodeState, Transaction};

mod block;
mod schema;
mod genesis;
mod service;
#[cfg(test)]
mod tests;

pub mod config;

/// Exonum blockchain instance with the concrete services set and data storage.
/// Only blockchains with the identical set of services and genesis block can be combined
/// into the single network.
pub struct Blockchain {
    db: Arc<Database>,
    service_map: Arc<VecMap<Box<Service>>>,
    service_keypair: (PublicKey, SecretKey),
    api_sender: ApiSender,
}

impl Blockchain {
    /// Constructs a blockchain for the given `storage` and list of `services`.
    pub fn new(
        storage: Box<Database>,
        services: Vec<Box<Service>>,
        service_public_key: PublicKey,
        service_secret_key: SecretKey,
        api_sender: ApiSender,
    ) -> Blockchain {
        let mut service_map = VecMap::new();
        for service in services {
            let id = service.service_id() as usize;
            if service_map.contains_key(id) {
                panic!(
                    "Services have already contain service with id={}, please change it.",
                    id
                );
            }
            service_map.insert(id, service);
        }

        Blockchain {
            db: storage.into(),
            service_map: Arc::new(service_map),
            service_keypair: (service_public_key, service_secret_key),
            api_sender,
        }
    }

    /// Recreates the blockchain to reuse with a sandbox.
    #[doc(hidden)]
    pub fn clone_with_api_sender(&self, api_sender: ApiSender) -> Blockchain {
        Blockchain {
            api_sender,
            ..self.clone()
        }
    }

    /// Returns service `VecMap` for all our services.
    pub fn service_map(&self) -> &Arc<VecMap<Box<Service>>> {
        &self.service_map
    }

    /// Creates a readonly snapshot of the current storage state.
    pub fn snapshot(&self) -> Box<Snapshot> {
        self.db.snapshot()
    }

    /// Creates snapshot of the current storage state that can be later committed into storage
    /// via `merge` method.
    pub fn fork(&self) -> Fork {
        self.db.fork()
    }

    /// Tries to create a `Transaction` object from the given raw message.
    /// Raw message can be converted into `Transaction` object only
    /// if following conditions are met.
    ///
    /// - Blockchain has service with the `service_id` of given raw message.
    /// - Service can deserialize given raw message.
    pub fn tx_from_raw(&self, raw: RawMessage) -> Option<Box<Transaction>> {
        let id = raw.service_id() as usize;
        self.service_map.get(id).and_then(|service| {
            service.tx_from_raw(raw).ok()
        })
    }

    /// Commits changes from the patch to the blockchain storage.
    /// See [`Fork`](../storage/struct.Fork.html) for details.
    pub fn merge(&mut self, patch: Patch) -> Result<(), Error> {
        self.db.merge(patch)
    }

    /// Returns the hash of latest committed block.
    ///
    /// # Panics
    ///
    /// - If the genesis block was not committed.
    pub fn last_hash(&self) -> Hash {
        Schema::new(&self.snapshot())
            .block_hashes_by_height()
            .last()
            .unwrap_or_else(Hash::default)
    }

    /// Returns the latest committed block.
    ///
    /// # Panics
    ///
    /// - If the genesis block was not committed.
    pub fn last_block(&self) -> Block {
        Schema::new(&self.snapshot()).last_block()
    }

    /// Creates and commits the genesis block for the given genesis configuration.
    pub fn create_genesis_block(&mut self, cfg: GenesisConfig) -> Result<(), Error> {
        let mut config_propose = StoredConfiguration {
            previous_cfg_hash: Hash::zero(),
            actual_from: Height::zero(),
            validator_keys: cfg.validator_keys,
            consensus: cfg.consensus,
            services: BTreeMap::new(),
        };

        let patch = {
            let mut fork = self.fork();
            // Update service tables
            for (_, service) in self.service_map.iter() {
                let cfg = service.initialize(&mut fork);
                let name = service.service_name();
                if config_propose.services.contains_key(name) {
                    panic!(
                        "Services already contain service with '{}' name, please change it",
                        name
                    );
                }
                config_propose.services.insert(name.into(), cfg);
            }
            // Commit actual configuration
            {
                let mut schema = Schema::new(&mut fork);
                if schema.block_hash_by_height(Height::zero()).is_some() {
                    // TODO create genesis block for MemoryDB and compare in hash with zero block
                    return Ok(());
                }
                schema.commit_configuration(config_propose);
            };
            self.merge(fork.into_patch())?;
            self.create_patch(ValidatorId::zero(), Height::zero(), &[], &BTreeMap::new())
                .1
        };
        self.merge(patch)?;
        Ok(())
    }

    /// Helper function to map tuple (`u16`, `u16`) of service table coordinates
    /// to 32 byte value for use as `MerklePatriciaTable` key (it currently
    /// supports only fixed size keys). `hash` function is used to distribute
    /// keys uniformly (compared to padding).
    /// # Arguments
    ///
    /// * `service_id` - `service_id` as returned by instance of type of
    /// `Service` trait
    /// * `table_idx` - index of service table in `Vec`, returned by
    /// `state_hash` method of instance of type of `Service` trait
    // also, it was the first idea around, to use `hash`
    pub fn service_table_unique_key(service_id: u16, table_idx: usize) -> Hash {
        debug_assert!(table_idx <= u16::max_value() as usize);
        let size = mem::size_of::<u16>();
        let mut vec = vec![0; 2 * size];
        LittleEndian::write_u16(&mut vec[0..size], service_id);
        LittleEndian::write_u16(&mut vec[size..2 * size], table_idx as u16);
        crypto::hash(&vec)
    }

    /// Executes the given transactions from pool.
    /// Then it collects the resulting changes from the current storage state and returns them
    /// with the hash of resulting block.
    pub fn create_patch(
        &self,
        proposer_id: ValidatorId,
        height: Height,
        tx_hashes: &[Hash],
        pool: &BTreeMap<Hash, Box<Transaction>>,
    ) -> (Hash, Patch) {
        // Create fork
        let mut fork = self.fork();

        let block_hash = {
            // Get last hash
            let last_hash = self.last_hash();
            // Save & execute transactions
            for (index, hash) in tx_hashes.iter().enumerate() {
                let tx = pool.get(hash).expect(
                    "BUG: Cannot find transaction in pool.",
                );

                fork.checkpoint();

                let r = panic::catch_unwind(panic::AssertUnwindSafe(|| { tx.execute(&mut fork); }));

                match r {
                    Ok(..) => fork.commit(),
                    Err(err) => {
                        if err.is::<Error>() {
                            // Continue panic unwind if the reason is StorageError
                            panic::resume_unwind(err);
                        }
                        fork.rollback();
                        error!("{:?} transaction execution failed: {:?}", tx, err);
                    }
                }

                let mut schema = Schema::new(&mut fork);
                schema.transactions_mut().put(hash, tx.raw().clone());
                schema.block_txs_mut(height).push(*hash);
                let location = TxLocation::new(height, index as u64);
                schema.tx_location_by_tx_hash_mut().put(hash, location);
            }

            // Get tx & state hash
            let (tx_hash, state_hash) = {
                let state_hashes = {
                    let schema = Schema::new(&fork);

                    let vec_core_state = schema.core_state_hash();
                    let mut state_hashes = Vec::new();

                    for (idx, core_table_hash) in vec_core_state.into_iter().enumerate() {
                        let key = Blockchain::service_table_unique_key(CORE_SERVICE, idx);
                        state_hashes.push((key, core_table_hash));
                    }

                    for service in self.service_map.values() {
                        let service_id = service.service_id();
                        let vec_service_state = service.state_hash(&fork);
                        for (idx, service_table_hash) in vec_service_state.into_iter().enumerate() {
                            let key = Blockchain::service_table_unique_key(service_id, idx);
                            state_hashes.push((key, service_table_hash));
                        }
                    }

                    state_hashes
                };

                let mut schema = Schema::new(&mut fork);

                let state_hash = {
                    let mut sum_table = schema.state_hash_aggregator_mut();
                    for (key, hash) in state_hashes {
                        sum_table.put(&key, hash)
                    }
                    sum_table.root_hash()
                };

                let tx_hash = schema.block_txs(height).root_hash();

                (tx_hash, state_hash)
            };

            // Create block
            let block = Block::new(
                SCHEMA_MAJOR_VERSION,
                proposer_id,
                height,
                tx_hashes.len() as u32,
                &last_hash,
                &tx_hash,
                &state_hash,
            );
            trace!("execute block = {:?}", block);
            // Eval block hash
            let block_hash = block.hash();
            // Update height
            let mut schema = Schema::new(&mut fork);
            schema.block_hashes_by_height_mut().push(block_hash);
            // Save block
            schema.blocks_mut().put(&block_hash, block);

            block_hash
        };

        (block_hash, fork.into_patch())
    }

    /// Commits to the storage block that proposes by node `State`.
    /// After that invokes `handle_commit` for each service in order of their identifiers
    /// and returns the list of transactions which which were created by the `handle_commit` event.
    #[cfg_attr(feature = "flame_profile", flame)]
    pub fn commit<'a, I>(
        &mut self,
        patch: &Patch,
        block_hash: Hash,
        precommits: I,
    ) -> Result<(), Error>
    where
        I: Iterator<Item = &'a Precommit>,
    {
        let patch = {
            let mut fork = {
                let mut fork = self.db.fork();
                fork.merge(patch.clone()); // FIXME: avoid cloning here
                fork
            };

            {
                let mut schema = Schema::new(&mut fork);
                for precommit in precommits {
                    schema.precommits_mut(&block_hash).push(precommit.clone());
                }
            }
            fork.into_patch()
        };
        self.merge(patch)?;
        // Initializes the context after merge.
        let context = ServiceContext::new(
            self.service_keypair.0,
            self.service_keypair.1.clone(),
            self.api_sender.clone(),
            self.fork(),
        );
        // Invokes `handle_commit` for each service in order of their identifiers
        for service in self.service_map.values() {
            service.handle_commit(&context);
        }
        Ok(())
    }

    /// Returns `Mount` object that aggregates public api handlers.
    pub fn mount_public_api(&self) -> Mount {
        let context = self.api_context();
        let mut mount = Mount::new();
        for service in self.service_map.values() {
            if let Some(handler) = service.public_api_handler(&context) {
                mount.mount(service.service_name(), handler);
            }
        }
        mount
    }

    /// Returns `Mount` object that aggregates private api handlers.
    pub fn mount_private_api(&self) -> Mount {
        let context = self.api_context();
        let mut mount = Mount::new();
        for service in self.service_map.values() {
            if let Some(handler) = service.private_api_handler(&context) {
                mount.mount(service.service_name(), handler);
            }
        }
        mount
    }

    fn api_context(&self) -> ApiContext {
        ApiContext::from_parts(
            self,
            self.api_sender.clone(),
            &self.service_keypair.0,
            &self.service_keypair.1,
        )
    }

    /// Saves peer to the peers cache
    pub fn save_peer(&mut self, pubkey: &PublicKey, peer: Connect) {
        let mut fork = self.fork();

        {
            let mut schema = Schema::new(&mut fork);
            schema.peers_cache_mut().put(pubkey, peer);
        }

        self.merge(fork.into_patch()).expect(
            "Unable to save peer to the peers cache",
        );
    }

    /// Removes peer from the peers cache
    pub fn remove_peer_with_addr(&mut self, addr: &SocketAddr) {
        let mut fork = self.fork();

        {
            let mut schema = Schema::new(&mut fork);
            let mut peers = schema.peers_cache_mut();
            let peer = peers.iter().find(|&(_, ref v)| v.addr() == *addr);
            if let Some(pubkey) = peer.map(|(k, _)| k) {
                peers.remove(&pubkey);
            }
        }

        self.merge(fork.into_patch()).expect(
            "Unable to remove peer from the peers cache",
        );
    }

    /// Recover cached peers if any.
    pub fn get_saved_peers(&self) -> HashMap<PublicKey, Connect> {
        let schema = Schema::new(self.snapshot());
        let peers_cache = schema.peers_cache();
        let it = peers_cache.iter().map(|(k, v)| (k, v.clone()));
        it.collect()
    }
}

impl fmt::Debug for Blockchain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Blockchain(..)")
    }
}

impl Clone for Blockchain {
    fn clone(&self) -> Blockchain {
        Blockchain {
            db: Arc::clone(&self.db),
            service_map: Arc::clone(&self.service_map),
            api_sender: self.api_sender.clone(),
            service_keypair: self.service_keypair.clone(),
        }
    }
}
<|endoftext|>