<commit_before>use kay::{ActorSystem, World, Actor};
use util::random::{seed, Rng};

use simulation::{TimeOfDay, TimeOfDayRange, Instant, Duration, Ticks, SimulationID, Simulatable,
SimulatableID};
use economy::resources::Resource;
use economy::resources::Resource::*;
use economy::market::{Deal, EvaluationRequester, EvaluationRequesterID, EvaluatedSearchResult};
use land_use::buildings::BuildingID;
use transport::pathfinding::trip::{TripResult, TripListenerID};
use transport::pathfinding::RoughLocationID;

pub mod names;
use self::names::{family_name, member_name};

use economy::households::{Household, HouseholdID, HouseholdCore, MemberIdx, Offer, OfferID, OfferIdx};

#[derive(Compact, Clone)]
pub struct Family {
    id: FamilyID,
    home: BuildingID,
    core: HouseholdCore,
}

impl Family {
    pub fn move_into(
        id: FamilyID,
        n_members: u32,
        home: BuildingID,
        simulation: SimulationID,
        world: &mut World,
    ) -> Family {
        simulation.wake_up_in(Ticks(0), id.into(), world);

        let mut core = HouseholdCore::new(
            id.into(),
            world,
            n_members as usize,
            home.into(),
            vec![Offer::new(
                MemberIdx(0),
                TimeOfDayRange::new(16, 0, 11, 0),
                Deal::new(Some((Wakefulness, 3.0)), Duration::from_hours(1)),
                1,
                true,
            )].into(),
        );

        core.used_offers.insert(
            Wakefulness,
            OfferID {
                household: id.into(),
                idx: OfferIdx(0),
            },
        );

        Family { id, home, core }
    }
}

use simulation::{Sleeper, SleeperID};

impl Sleeper for Family {
    fn wake(&mut self, current_instant: Instant, world: &mut World) {
        self.update_core(current_instant, world);
    }
}

use economy::households::ResultAspect;

impl EvaluationRequester for Family {
    fn expect_n_results(&mut self, resource: Resource, n: u32, world: &mut World) {
        self.update_results(resource, &ResultAspect::SetTarget(n), world);
    }

    fn on_result(&mut self, result: &EvaluatedSearchResult, world: &mut World) {
        let &EvaluatedSearchResult {
            resource,
            ref evaluated_deals,
            ..
        } = result;
        self.update_results(
            resource,
            &ResultAspect::AddDeals(evaluated_deals.clone()),
            world,
        );
    }
}

use transport::pathfinding::trip::{TripListener, TripID};

impl TripListener for Family {
    fn trip_created(&mut self, trip: TripID, world: &mut World) {
        self.on_trip_created(trip, world);
    }

    fn trip_result(
        &mut self,
        trip: TripID,
        result: TripResult,
        rough_source: RoughLocationID,
        rough_destination: RoughLocationID,
        world: &mut World,
    ) {
        self.on_trip_result(trip, result, rough_source, rough_destination, world);
    }
}

impl Household for Family {
    fn core(&self) -> &HouseholdCore {
        &self.core
    }

    fn core_mut(&mut self) -> &mut HouseholdCore {
        &mut self.core
    }

    fn site(&self) -> RoughLocationID {
        self.home.into()
    }

    fn is_shared(resource: Resource) -> bool {
        match resource {
            Wakefulness | Satiety => false,
            Money | Groceries => true,
            _ => unimplemented!(),
        }
    }

    fn supplier_shared(resource: Resource) -> bool {
        match resource {
            Money => false,
            Wakefulness | Satiety | Groceries => true,
            _ => unimplemented!(),
        }
    }

    fn importance(resource: Resource, time: TimeOfDay) -> f32 {
        let hour = time.hours_minutes().0;

        let bihourly_importance = match resource {
            Wakefulness => Some([7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 7, 7]),
            Satiety => Some([0, 0, 5, 5, 1, 5, 5, 1, 5, 5, 1, 1]),
            Money => Some([0, 0, 3, 3, 5, 5, 5, 3, 3, 1, 1, 1]),
            Groceries => Some([0, 0, 4, 4, 1, 4, 4, 4, 4, 4, 0, 0]),
            _ => None,
        };

        bihourly_importance
            .map(|lookup| lookup[hour / 2] as f32)
            .unwrap_or(0.0)
    }

    fn interesting_resources() -> &'static [Resource] {
        &[
            Wakefulness,
            Satiety, //Entertainment,
            Money,
            Groceries,
            /* Furniture, */
            /*Clothes,
             *Devices,
             *Services, */
        ]
    }

    fn decay(&mut self, dt: Duration, _: &mut World) {
        for (i, member_resources) in self.core.member_resources.iter_mut().enumerate() {
            {
                let individuality = seed((self.id, i)).gen_range(0.8, 1.2);
                let wakefulness = member_resources.mut_entry_or(Wakefulness, 0.0);
                *wakefulness -= 1.0 * individuality * dt.as_hours();
            }
            {
                let individuality = seed((self.id, i, 1u8)).gen_range(0.8, 1.2);
                let satiety = member_resources.mut_entry_or(Satiety, 0.0);
                if *satiety < 0.0 {
                    let groceries = self.core.resources.mut_entry_or(Groceries, 0.0);
                    *groceries -= 1.0;
                    *satiety += 1.0;
                }
                *satiety -= 3.0 * individuality * dt.as_days();
            }
            // {
            //     let individuality = seed((self.id, i)).gen_range(0.8, 1.2);
            //     let entertainment = member_resources.mut_entry_or(Entertainment, 0.0);
            //     *entertainment -= 0.2 * individuality * dt.as_hours();
            // }
        }
        // {
        //     let individuality = seed(self.id).gen_range(0.8, 1.2);
        //     let furniture = self.core.resources.mut_entry_or(Furniture, 0.0);
        //     *furniture -= 0.005 * individuality * dt.as_hours();
        // }
        // {
        //     let individuality = seed(self.id).gen_range(0.8, 1.2);
        //     let devices = self.core.resources.mut_entry_or(Devices, 0.0);
        //     *devices -= 0.005 * individuality * dt.as_hours();
        // }
        // {
        //     let individuality = seed(self.id).gen_range(0.8, 1.2);
        //     let services = self.core.resources.mut_entry_or(Services, 0.0);
        //     *services -= 0.01 * individuality * dt.as_hours();
        // }
    }

    fn on_destroy(&mut self, world: &mut World) {
        self.home.remove_household(self.id_as(), world);
    }

    fn household_name(&self) -> String {
        format!("The {} Family", family_name(self.id))
    }

    fn member_name(&self, member: MemberIdx) -> String {
        member_name(self.id, member)
    }
}

impl Simulatable for Family {
    fn tick(&mut self, _dt: f32, current_instant: Instant, world: &mut World) {
        self.on_tick(current_instant, world);
    }
}

use transport::pathfinding::{RoughLocation, RoughLocationResolve};

impl RoughLocation for Family {
    fn resolve(&self) -> RoughLocationResolve {
        RoughLocationResolve::SameAs(self.site())
    }
}

pub fn setup(system: &mut ActorSystem) {
    system.register::<Family>();
    auto_setup(system);
}

mod kay_auto;
pub use self::kay_auto::*;
<commit_msg>rustfmt<commit_after>use kay::{ActorSystem, World, Actor};
use util::random::{seed, Rng};

use simulation::{TimeOfDay, TimeOfDayRange, Instant, Duration, Ticks, SimulationID, Simulatable,
SimulatableID};
use economy::resources::Resource;
use economy::resources::Resource::*;
use economy::market::{Deal, EvaluationRequester, EvaluationRequesterID, EvaluatedSearchResult};
use land_use::buildings::BuildingID;
use transport::pathfinding::trip::{TripResult, TripListenerID};
use transport::pathfinding::RoughLocationID;

pub mod names;
use self::names::{family_name, member_name};

use economy::households::{Household, HouseholdID, HouseholdCore,
MemberIdx, Offer, OfferID, OfferIdx};

#[derive(Compact, Clone)]
pub struct Family {
    id: FamilyID,
    home: BuildingID,
    core: HouseholdCore,
}

impl Family {
    pub fn move_into(
        id: FamilyID,
        n_members: u32,
        home: BuildingID,
        simulation: SimulationID,
        world: &mut World,
    ) -> Family {
        simulation.wake_up_in(Ticks(0), id.into(), world);

        let mut core = HouseholdCore::new(
            id.into(),
            world,
            n_members as usize,
            home.into(),
            vec![Offer::new(
                MemberIdx(0),
                TimeOfDayRange::new(16, 0, 11, 0),
                Deal::new(Some((Wakefulness, 3.0)), Duration::from_hours(1)),
                1,
                true,
            )].into(),
        );

        core.used_offers.insert(
            Wakefulness,
            OfferID {
                household: id.into(),
                idx: OfferIdx(0),
            },
        );

        Family { id, home, core }
    }
}

use simulation::{Sleeper, SleeperID};

impl Sleeper for Family {
    fn wake(&mut self, current_instant: Instant, world: &mut World) {
        self.update_core(current_instant, world);
    }
}

use economy::households::ResultAspect;

impl EvaluationRequester for Family {
    fn expect_n_results(&mut self, resource: Resource, n: u32, world: &mut World) {
        self.update_results(resource, &ResultAspect::SetTarget(n), world);
    }

    fn on_result(&mut self, result: &EvaluatedSearchResult, world: &mut World) {
        let &EvaluatedSearchResult {
            resource,
            ref evaluated_deals,
            ..
        } = result;
        self.update_results(
            resource,
            &ResultAspect::AddDeals(evaluated_deals.clone()),
            world,
        );
    }
}

use transport::pathfinding::trip::{TripListener, TripID};

impl TripListener for Family {
    fn trip_created(&mut self, trip: TripID, world: &mut World) {
        self.on_trip_created(trip, world);
    }

    fn trip_result(
        &mut self,
        trip: TripID,
        result: TripResult,
        rough_source: RoughLocationID,
        rough_destination: RoughLocationID,
        world: &mut World,
    ) {
        self.on_trip_result(trip, result, rough_source, rough_destination, world);
    }
}

impl Household for Family {
    fn core(&self) -> &HouseholdCore {
        &self.core
    }

    fn core_mut(&mut self) -> &mut HouseholdCore {
        &mut self.core
    }

    fn site(&self) -> RoughLocationID {
        self.home.into()
    }

    fn is_shared(resource: Resource) -> bool {
        match resource {
            Wakefulness | Satiety => false,
            Money | Groceries => true,
            _ => unimplemented!(),
        }
    }

    fn supplier_shared(resource: Resource) -> bool {
        match resource {
            Money => false,
            Wakefulness | Satiety | Groceries => true,
            _ => unimplemented!(),
        }
    }

    fn importance(resource: Resource, time: TimeOfDay) -> f32 {
        let hour = time.hours_minutes().0;

        let bihourly_importance = match resource {
            Wakefulness => Some([7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 7, 7]),
            Satiety => Some([0, 0, 5, 5, 1, 5, 5, 1, 5, 5, 1, 1]),
            Money => Some([0, 0, 3, 3, 5, 5, 5, 3, 3, 1, 1, 1]),
            Groceries => Some([0, 0, 4, 4, 1, 4, 4, 4, 4, 4, 0, 0]),
            _ => None,
        };

        bihourly_importance
            .map(|lookup| lookup[hour / 2] as f32)
            .unwrap_or(0.0)
    }

    fn interesting_resources() -> &'static [Resource] {
        &[
            Wakefulness,
            Satiety, //Entertainment,
            Money,
            Groceries,
            /* Furniture, */
            /*Clothes,
             *Devices,
             *Services, */
        ]
    }

    fn decay(&mut self, dt: Duration, _: &mut World) {
        for (i, member_resources) in self.core.member_resources.iter_mut().enumerate() {
            {
                let individuality = seed((self.id, i)).gen_range(0.8, 1.2);
                let wakefulness = member_resources.mut_entry_or(Wakefulness, 0.0);
                *wakefulness -= 1.0 * individuality * dt.as_hours();
            }
            {
                let individuality = seed((self.id, i, 1u8)).gen_range(0.8, 1.2);
                let satiety = member_resources.mut_entry_or(Satiety, 0.0);
                if *satiety < 0.0 {
                    let groceries = self.core.resources.mut_entry_or(Groceries, 0.0);
                    *groceries -= 1.0;
                    *satiety += 1.0;
                }
                *satiety -= 3.0 * individuality * dt.as_days();
            }
            // {
            //     let individuality = seed((self.id, i)).gen_range(0.8, 1.2);
            //     let entertainment = member_resources.mut_entry_or(Entertainment, 0.0);
            //     *entertainment -= 0.2 * individuality * dt.as_hours();
            // }
        }
        // {
        //     let individuality = seed(self.id).gen_range(0.8, 1.2);
        //     let furniture = self.core.resources.mut_entry_or(Furniture, 0.0);
        //     *furniture -= 0.005 * individuality * dt.as_hours();
        // }
        // {
        //     let individuality = seed(self.id).gen_range(0.8, 1.2);
        //     let devices = self.core.resources.mut_entry_or(Devices, 0.0);
        //     *devices -= 0.005 * individuality * dt.as_hours();
        // }
        // {
        //     let individuality = seed(self.id).gen_range(0.8, 1.2);
        //     let services = self.core.resources.mut_entry_or(Services, 0.0);
        //     *services -= 0.01 * individuality * dt.as_hours();
        // }
    }

    fn on_destroy(&mut self, world: &mut World) {
        self.home.remove_household(self.id_as(), world);
    }

    fn household_name(&self) -> String {
        format!("The {} Family", family_name(self.id))
    }

    fn member_name(&self, member: MemberIdx) -> String {
        member_name(self.id, member)
    }
}

impl Simulatable for Family {
    fn tick(&mut self, _dt: f32, current_instant: Instant, world: &mut World) {
        self.on_tick(current_instant, world);
    }
}

use transport::pathfinding::{RoughLocation, RoughLocationResolve};

impl RoughLocation for Family {
    fn resolve(&self) -> RoughLocationResolve {
        RoughLocationResolve::SameAs(self.site())
    }
}

pub fn setup(system: &mut ActorSystem) {
    system.register::<Family>();
    auto_setup(system);
}

mod kay_auto;
pub use self::kay_auto::*;
<|endoftext|>


<commit_before>#![feature(linkage, naked_functions, asm, const_fn, proc_macro, used)]

// Execute using: RUSTFLAGS="-C relocation-model=dynamic-no-pic -C code-model=kernel" RUST_BACKTRACE=1 cargo test --verbose --test kvm -- --nocapture
extern crate x86;
extern crate core;

#[macro_use]
extern crate klogger;

extern crate test_macros;
use test_macros::kvmattrs;

// Needed for code generated by `kvmattrs`
extern crate test;
use self::test::KvmTestMetaData;

#[test]
#[kvmattrs(identity_map, ram(0x30000000, 0x31000000))]
fn use_the_port() {
    log!("1");
    unsafe {
        asm!("inb $0, %al" :: "i"(0x01) :: "volatile");
    }
}

#[test]
#[kvmattrs(identity_map, ram(0x30000000, 0x31000000))]
fn io_example2() {
    assert!(1==1);
}
<commit_msg>Update run command #20.<commit_after>#![feature(linkage, naked_functions, asm, const_fn, proc_macro, used)]
//RUSTFLAGS="-C relocation-model=dynamic-no-pic -C code-model=kernel" RUST_BACKTRACE=1 cargo test --verbose --test kvm -- --nocapture

extern crate x86;
extern crate core;

#[macro_use]
extern crate klogger;

extern crate test_macros;
use test_macros::kvmattrs;

// Needed for code generated by `kvmattrs`
extern crate test;
use self::test::KvmTestMetaData;

#[test]
#[kvmattrs(identity_map, ram(0x30000000, 0x31000000))]
fn use_the_port() {
    log!("1");
    unsafe {
        asm!("inb $0, %al" :: "i"(0x01) :: "volatile");
    }
}

#[test]
#[kvmattrs(identity_map, ram(0x30000000, 0x31000000))]
fn io_example2() {
    assert!(1==1);
}
<|endoftext|>


<commit_before>// Copyright 2015 The Delix Project Authors. See the AUTHORS file at the top level directory.
//
// 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 delix;

#[allow(dead_code)] mod helper;

use std::thread;

use delix::node::State;

#[test]
fn single_echo_from_local() {
    helper::set_up();

    let node = helper::build_node("127.0.0.1:3001", &[]);
    node.register("echo", Box::new(|request| Ok(request.to_vec())))
            .unwrap();

    thread::sleep_ms(100);
    helper::assert_node(&node, State::Discovering, 0);

    let response = node.request("echo", b"test message").unwrap();
    assert_eq!(b"test message".to_vec(), response);
}

#[test]
fn single_echo_from_remote() {
    helper::set_up();

    let node_one = helper::build_node("127.0.0.1:3011", &[]);
    node_one.register("echo", Box::new(|request| Ok(request.to_vec())))
            .unwrap();

    let node_two = helper::build_node("127.0.0.1:3012", &["127.0.0.1:3011"]);

    thread::sleep_ms(1000);
    helper::assert_node(&node_one, State::Joined, 1);
    helper::assert_node(&node_two, State::Joined, 1);

    let response = node_two.request("echo", b"test message").unwrap();
    assert_eq!(b"test message".to_vec(), response);
}

#[test]
fn multiple_echos_from_remote() {
    helper::set_up();

    let node_one = helper::build_node("127.0.0.1:3021", &[]);
    node_one.register("echo", Box::new(|request| Ok(request.to_vec())))
            .unwrap();

    let node_two = helper::build_node("127.0.0.1:3022", &["127.0.0.1:3021"]);

    thread::sleep_ms(1000);
    helper::assert_node(&node_one, State::Joined, 1);
    helper::assert_node(&node_two, State::Joined, 1);

    assert_eq!(b"test message one".to_vec(), node_two.request("echo", b"test message one").unwrap());
    assert_eq!(b"test message two".to_vec(), node_two.request("echo", b"test message two").unwrap());
}

#[test]
fn balanced_echos_from_two_remotes() {
    helper::set_up();

    let node_one = helper::build_node("127.0.0.1:3031", &[]);
    node_one.register("echo", Box::new(|_| {
        thread::sleep_ms(10);
        Ok(b"echo one".to_vec())
    })).unwrap();

    let node_two = helper::build_node("127.0.0.1:3032", &["127.0.0.1:3031"]);
    node_two.register("echo", Box::new(|_| {
        thread::sleep_ms(100);
        Ok(b"echo two".to_vec())
    })).unwrap();

    let node_three = helper::build_node("127.0.0.1:3033", &["127.0.0.1:3031"]);

    thread::sleep_ms(1000);
    helper::assert_node(&node_one, State::Joined, 2);
    helper::assert_node(&node_two, State::Joined, 2);
    helper::assert_node(&node_three, State::Joined, 2);

    // in the first round the balancer has no statistic, so every serivice gets a request in order.
    assert_eq!(b"echo one".to_vec(), node_three.request("echo", b"").unwrap());
    assert_eq!(b"echo two".to_vec(), node_three.request("echo", b"").unwrap());

    // in the second round the balancer can access some respond time statistic, so this round
    // contains two requests to node one and one to node two.
    assert_eq!(b"echo one".to_vec(), node_three.request("echo", b"").unwrap());
    assert_eq!(b"echo one".to_vec(), node_three.request("echo", b"").unwrap());
    assert_eq!(b"echo two".to_vec(), node_three.request("echo", b"").unwrap());

    // if a service deregisters in the middle of a round, the changes should be processed
    assert_eq!(b"echo one".to_vec(), node_three.request("echo", b"").unwrap());
    node_two.deregister("echo").unwrap();
    assert_eq!(b"echo one".to_vec(), node_three.request("echo", b"").unwrap());
    assert_eq!(b"echo one".to_vec(), node_three.request("echo", b"").unwrap());
}
<commit_msg>refactored request tests<commit_after>// Copyright 2015 The Delix Project Authors. See the AUTHORS file at the top level directory.
//
// 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 delix;

#[allow(dead_code)] mod helper;

use std::thread;

use delix::node::State;

#[test]
fn single_echo_from_local() {
    helper::set_up();

    let node = helper::build_node("127.0.0.1:3001", &[]);
    node.register("echo", Box::new(|request| Ok(request.to_vec())))
            .unwrap();

    thread::sleep_ms(100);
    helper::assert_node(&node, State::Discovering, 0);

    let response = node.request("echo", b"test message").unwrap();
    assert_eq!(b"test message".to_vec(), response);
}

#[test]
fn single_echo_from_remote() {
    helper::set_up();

    let node_one = helper::build_node("127.0.0.1:3011", &[]);
    node_one.register("echo", Box::new(|request| Ok(request.to_vec())))
            .unwrap();

    let node_two = helper::build_node("127.0.0.1:3012", &["127.0.0.1:3011"]);

    thread::sleep_ms(1000);
    helper::assert_node(&node_one, State::Joined, 1);
    helper::assert_node(&node_two, State::Joined, 1);

    let response = node_two.request("echo", b"test message").unwrap();
    assert_eq!(b"test message".to_vec(), response);
}

#[test]
fn multiple_echos_from_remote() {
    helper::set_up();

    let node_one = helper::build_node("127.0.0.1:3021", &[]);
    node_one.register("echo", Box::new(|request| Ok(request.to_vec())))
            .unwrap();

    let node_two = helper::build_node("127.0.0.1:3022", &["127.0.0.1:3021"]);

    thread::sleep_ms(1000);
    helper::assert_node(&node_one, State::Joined, 1);
    helper::assert_node(&node_two, State::Joined, 1);

    assert_eq!(b"test message one".to_vec(), node_two.request("echo", b"test message one").unwrap());
    assert_eq!(b"test message two".to_vec(), node_two.request("echo", b"test message two").unwrap());
}

#[test]
fn balanced_echos_from_two_remotes() {
    helper::set_up();

    let node_one = helper::build_node("127.0.0.1:3031", &[]);
    node_one.register("echo", Box::new(|_| {
        thread::sleep_ms(10);
        Ok(b"echo one".to_vec())
    })).unwrap();

    let node_two = helper::build_node("127.0.0.1:3032", &["127.0.0.1:3031"]);
    node_two.register("echo", Box::new(|_| {
        thread::sleep_ms(100);
        Ok(b"echo two".to_vec())
    })).unwrap();

    let node_three = helper::build_node("127.0.0.1:3033", &["127.0.0.1:3031"]);

    thread::sleep_ms(1000);
    helper::assert_node(&node_one, State::Joined, 2);
    helper::assert_node(&node_two, State::Joined, 2);
    helper::assert_node(&node_three, State::Joined, 2);

    // in the first round the balancer has no statistic, so every serivice gets a request in order.
    assert_eq!("echo one", String::from_utf8_lossy(&node_three.request("echo", b"").unwrap()));
    assert_eq!("echo two", String::from_utf8_lossy(&node_three.request("echo", b"").unwrap()));

    // in the second round the balancer can access some respond time statistic, so this round
    // contains two requests to node one and one to node two.
    assert_eq!("echo one", String::from_utf8_lossy(&node_three.request("echo", b"").unwrap()));
    assert_eq!("echo one", String::from_utf8_lossy(&node_three.request("echo", b"").unwrap()));
    assert_eq!("echo two", String::from_utf8_lossy(&node_three.request("echo", b"").unwrap()));

    // if a service deregisters in the middle of a round, the changes should be processed
    assert_eq!("echo one", String::from_utf8_lossy(&node_three.request("echo", b"").unwrap()));
    node_two.deregister("echo").unwrap();
    assert_eq!("echo one", String::from_utf8_lossy(&node_three.request("echo", b"").unwrap()));
    assert_eq!("echo one", String::from_utf8_lossy(&node_three.request("echo", b"").unwrap()));
}
<|endoftext|>


<commit_before>#[macro_use]
mod spec;
mod block;
mod schema;
pub mod config;
mod genesis;
mod service;

use std::sync::Arc;
use std::collections::HashMap;

use time::Timespec;
use vec_map::VecMap;

use ::crypto::Hash;
use ::messages::{RawMessage, Precommit};
use ::node::State;

use ::storage::{MerkleTable, MemoryDB, Patch, Database, Fork, Error, Map, List, Storage,
                View as StorageView};

pub use self::block::Block;
pub use self::schema::{ConfigurationData, Schema};
pub use self::genesis::GenesisConfig;
pub use self::config::{StoredConfiguration, ConsensusConfig};
pub use self::service::{Service, Transaction, NodeState};

#[derive(Clone)]
pub struct Blockchain {
    db: Storage,
    service_map: Arc<VecMap<Box<Service>>>,
}

impl Blockchain {
    pub fn new(db: Storage, services: Vec<Box<Service>>) -> 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: db,
            service_map: Arc::new(service_map),
        }
    }

    pub fn view(&self) -> StorageView {
        self.db.fork()
    }

    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())
    }

    pub fn merge(&self, patch: &Patch) -> Result<(), Error> {
        self.db.merge(patch)
    }

    pub fn last_hash(&self) -> Result<Hash, Error> {
        Ok(Schema::new(&self.view())
            .heights()
            .last()?
            .unwrap_or_else(Hash::default))
    }

    pub fn last_block(&self) -> Result<Block, Error> {
        Ok(Schema::new(&self.view()).last_block()?.unwrap())
    }

    pub fn create_genesis_block(&self, cfg: GenesisConfig) -> Result<(), Error> {
        let mut config_propose = StoredConfiguration {
            actual_from: 0,
            validators: cfg.validators,
            consensus: cfg.consensus,
            services: HashMap::new(),
        };
        let time = Timespec {
            sec: cfg.time as i64,
            nsec: 0,
        };

        let patch = {
            let view = self.view();
            // Update service tables
            for (id, service) in self.service_map.iter() {
                let cfg = service.handle_genesis_block(&view)?;
                config_propose.services.insert(id as u16, cfg);
            }
            // Commit actual configuration
            {
                let schema = Schema::new(&view);
                if let Some(block_hash) = schema.heights().get(0)? {
                    // TODO create genesis block for MemoryDB and compare in hash with zero block
                    // panic!("Genesis block is already created");
                    let _ = block_hash;
                    return Ok(());
                }
                schema.commit_actual_configuration(0, config_propose.serialize().as_ref())?;
            };
            self.merge(&view.changes())?;
            self.create_patch(0, 0, time, &[], &HashMap::new())?.1
        };
        self.merge(&patch)?;
        Ok(())
    }

    pub fn create_patch(&self,
                        height: u64,
                        round: u32,
                        time: Timespec,
                        tx_hashes: &[Hash],
                        pool: &HashMap<Hash, Box<Transaction>>)
                        -> Result<(Hash, Patch), Error> {
        // Create fork
        let fork = self.view();
        // Create databa schema
        let schema = Schema::new(&fork);
        // Get last hash
        let last_hash = self.last_hash()?;
        // Save & execute transactions
        for hash in tx_hashes {
            let tx = &pool[hash];
            tx.execute(&fork)?;
            schema.transactions()
                .put(hash, tx.raw().clone())
                .unwrap();
            schema.block_txs(height)
                .append(*hash)
                .unwrap();
        }
        // Get tx hash
        let tx_hash = schema.block_txs(height).root_hash()?;
        // Get state hash
        let state_hash = {
            let db = MemoryDB::new();
            let hashes: MerkleTable<MemoryDB, u64, Hash> = MerkleTable::new(db);

            // Add core state hashes
            hashes.append(schema.state_hash()?)?;
            // Add state hashes from extensions
            for service in self.service_map.values() {
                if let Some(hash) = service.state_hash(&fork) {
                    hashes.append(hash?)?;
                }
            }
            hashes.root_hash()?
        };

        // Create block
        let block = Block::new(height, round, time, &last_hash, &tx_hash, &state_hash);
        // Eval block hash
        let block_hash = block.hash();
        // Update height
        // TODO: check that height == propose.height
        schema.heights().append(block_hash).is_ok();
        // Save block
        schema.blocks().put(&block_hash, block).is_ok();
        Ok((block_hash, fork.changes()))
    }

    fn execute_config_change(view: &Self::View, config_message: &ConfigMessage) {
        match *config_message {
            ConfigMessage::ConfigPropose(ref config_propose_tx) => {
                if !Self::handle_config_propose(view, config_propose_tx).is_ok() {
                    error!("ConfigPropose failed !");
                }
            }
            ConfigMessage::ConfigVote(ref config_vote_tx) => {
                if !Self::handle_config_vote(view, config_vote_tx).is_ok() {
                    error!("ConfigVote failed !");
                }
            }
        }
    }

    fn get_height(view: &Self::View) -> u64 {
        if let Ok(Some(last_block)) = view.last_block() {
            return last_block.height() + 1;
        }
        0
    }

    fn get_actual_configuration(view: &Self::View) -> Result<StoredConfiguration, Error> {
        let h = Self::get_height(view);
        let heights = view.configs_heights();
        let height_values = heights.values().unwrap();

        // TODO improve perfomance
        let idx = height_values.into_iter()
            .rposition(|r| u64::from(r) <= h)
            .unwrap();

        let height = heights.get(idx as u64)?.unwrap();
        Self::get_configuration_at_height(view, height.into()).map(|x| x.unwrap())
    }

    fn commit_actual_configuration(view: &Self::View,
                                   actual_from: u64,
                                   config_data: &[u8])
                                   -> Result<(), Error> {
        let height_bytecode = actual_from.into();
        view.configs().put(&height_bytecode, config_data.to_vec())?;
        view.configs_heights().append(height_bytecode)?;
        Ok(())
    }

    // FIXME Replace by result?
    fn get_configuration_at_height(view: &Self::View,
                                   height: u64)
                                   -> Result<Option<StoredConfiguration>, Error> {
        let configs = view.configs();
        if let Some(config) = configs.get(&height.into())? {
            match StoredConfiguration::deserialize(&config) {
                Ok(configuration) => {
                    return Ok(Some(configuration));
                }
                Err(_) => {
                    error!("Can't parse found configuration at height: {}", height);
                }
            }
        }
        Ok(None)
    }

    fn handle_config_propose(view: &Self::View,
                             config_propose: &ConfigPropose)
                             -> Result<Hash, Error> {
        let config = Self::get_actual_configuration(view).unwrap();
        if !config.validators.contains(config_propose.from()) {
            error!("ConfigPropose from unknown validator: {:?}",
                   config_propose.from());
            return Err(Error::new("ConfigPropose from unknown validator"));
        }

        let hash = <ConfigPropose as Message>::hash(config_propose);
        if view.config_proposes().get(&hash).unwrap().is_some() {
            error!("Received config_propose has already been handled, msg={:?}",
                   config_propose);
            return Err(Error::new("Received config_propose has already been handled"));
        }

        trace!("Handle ConfigPropose");
        view.config_proposes().put(&hash, config_propose.clone()).unwrap();
        Ok(hash)
    }

    fn handle_config_vote(view: &Self::View, config_vote: &ConfigVote) -> Result<(), Error> {
        let config = Self::get_actual_configuration(view).unwrap();

        if !config.validators.contains(config_vote.from()) {
            error!("ConfigVote from unknown validator: {:?}",
                   config_vote.from());
            return Err(Error::new("ConfigVote from unknown validator"));
        }

        if view.config_proposes().get(config_vote.hash_propose()).unwrap().is_none() {
            error!("Received ConfigVote for unknown transaciton, msg={:?}",
                   config_vote);
            return Err(Error::new("Received ConfigVote for unknown transaciton"));
        }

        if let Some(vote) = view.config_votes().get(config_vote.from()).unwrap() {
            if vote.seed() != config_vote.seed() - 1 {
                error!("Received config_vote with wrong seed, msg={:?}",
                       config_vote);
                return Err(Error::new("Received config_vote with wrong seed"));
            }
        }

        let msg = config_vote.clone();
        let _ = view.config_votes().put(msg.from(), config_vote.clone());

        let mut votes_count = 0;
        for pub_key in config.validators.clone() {
            if let Some(vote) = view.config_votes().get(&pub_key).unwrap() {
                if config_vote.hash_propose() == vote.hash_propose() && !vote.revoke() {
                    votes_count += 1;
                }
            }
        }

        if votes_count > 2 / 3 * config.validators.len() {
            if let Some(config_propose) =
                view.config_proposes()
                    .get(config_vote.hash_propose())
                    .unwrap() {
                Self::commit_actual_configuration(view,
                                                  config_propose.actual_from_height(),
                                                  config_propose.config())
                    .unwrap();
            }
        }
        Ok(())
    }

    pub fn commit<'a, I>(&self,
                         state: &mut State,
                         block_hash: Hash,
                         precommits: I)
                         -> Result<Vec<Box<Transaction>>, Error>
        where I: Iterator<Item = &'a Precommit>
    {
        let (patch, txs) = {
            let view = {
                let patch = state.block(&block_hash).unwrap().patch();
                let view = self.db.fork();
                view.merge(patch);
                view
            };

            let schema = Schema::new(&view);
            for precommit in precommits {
                schema.precommits(&block_hash).append(precommit.clone())?;
            }

            let mut node_state = NodeState::new(state, &view);
            for service in self.service_map.values() {
                service.handle_commit(&mut node_state)?;
            }
            (view.changes(), node_state.transactions())
        };
        self.merge(&patch)?;
        Ok(txs)
    }
}<commit_msg>Fixes after rebase<commit_after>#[macro_use]
mod spec;
mod block;
mod schema;
pub mod config;
mod genesis;
mod service;

use std::sync::Arc;
use std::collections::HashMap;

use time::Timespec;
use vec_map::VecMap;

use ::crypto::Hash;
use ::messages::{RawMessage, Precommit};
use ::node::State;

use ::storage::{MerkleTable, MemoryDB, Patch, Database, Fork, Error, Map, List, Storage,
                View as StorageView};

pub use self::block::Block;
pub use self::schema::{ConfigurationData, Schema};
pub use self::genesis::GenesisConfig;
pub use self::config::{StoredConfiguration, ConsensusConfig};
pub use self::service::{Service, Transaction, NodeState};

#[derive(Clone)]
pub struct Blockchain {
    db: Storage,
    service_map: Arc<VecMap<Box<Service>>>,
}

impl Blockchain {
    pub fn new(db: Storage, services: Vec<Box<Service>>) -> 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: db,
            service_map: Arc::new(service_map),
        }
    }

    pub fn view(&self) -> StorageView {
        self.db.fork()
    }

    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())
    }

    pub fn merge(&self, patch: &Patch) -> Result<(), Error> {
        self.db.merge(patch)
    }

    pub fn last_hash(&self) -> Result<Hash, Error> {
        Ok(Schema::new(&self.view())
            .heights()
            .last()?
            .unwrap_or_else(Hash::default))
    }

    pub fn last_block(&self) -> Result<Block, Error> {
        Ok(Schema::new(&self.view()).last_block()?.unwrap())
    }

    pub fn create_genesis_block(&self, cfg: GenesisConfig) -> Result<(), Error> {
        let mut config_propose = StoredConfiguration {
            actual_from: 0,
            validators: cfg.validators,
            consensus: cfg.consensus,
            services: HashMap::new(),
        };
        let time = Timespec {
            sec: cfg.time as i64,
            nsec: 0,
        };

        let patch = {
            let view = self.view();
            // Update service tables
            for (id, service) in self.service_map.iter() {
                let cfg = service.handle_genesis_block(&view)?;
                config_propose.services.insert(id as u16, cfg);
            }
            // Commit actual configuration
            {
                let schema = Schema::new(&view);
                if let Some(block_hash) = schema.heights().get(0)? {
                    // TODO create genesis block for MemoryDB and compare in hash with zero block
                    // panic!("Genesis block is already created");
                    let _ = block_hash;
                    return Ok(());
                }
                schema.commit_actual_configuration(0, config_propose.serialize().as_ref())?;
            };
            self.merge(&view.changes())?;
            self.create_patch(0, 0, time, &[], &HashMap::new())?.1
        };
        self.merge(&patch)?;
        Ok(())
    }

    pub fn create_patch(&self,
                        height: u64,
                        round: u32,
                        time: Timespec,
                        tx_hashes: &[Hash],
                        pool: &HashMap<Hash, Box<Transaction>>)
                        -> Result<(Hash, Patch), Error> {
        // Create fork
        let fork = self.view();
        // Create databa schema
        let schema = Schema::new(&fork);
        // Get last hash
        let last_hash = self.last_hash()?;
        // Save & execute transactions
        for hash in tx_hashes {
            let tx = &pool[hash];
            tx.execute(&fork)?;
            schema.transactions()
                .put(hash, tx.raw().clone())
                .unwrap();
            schema.block_txs(height)
                .append(*hash)
                .unwrap();
        }
        // Get tx hash
        let tx_hash = schema.block_txs(height).root_hash()?;
        // Get state hash
        let state_hash = {
            let db = MemoryDB::new();
            let hashes: MerkleTable<MemoryDB, u64, Hash> = MerkleTable::new(db);

            // Add core state hashes
            hashes.append(schema.state_hash()?)?;
            // Add state hashes from extensions
            for service in self.service_map.values() {
                if let Some(hash) = service.state_hash(&fork) {
                    hashes.append(hash?)?;
                }
            }
            hashes.root_hash()?
        };

        // Create block
        let block = Block::new(height, round, time, &last_hash, &tx_hash, &state_hash);
        // Eval block hash
        let block_hash = block.hash();
        // Update height
        // TODO: check that height == propose.height
        schema.heights().append(block_hash).is_ok();
        // Save block
        schema.blocks().put(&block_hash, block).is_ok();
        Ok((block_hash, fork.changes()))
    }

    pub fn commit<'a, I>(&self,
                         state: &mut State,
                         block_hash: Hash,
                         precommits: I)
                         -> Result<Vec<Box<Transaction>>, Error>
        where I: Iterator<Item = &'a Precommit>
    {
        let (patch, txs) = {
            let view = {
                let patch = state.block(&block_hash).unwrap().patch();
                let view = self.db.fork();
                view.merge(patch);
                view
            };

            let schema = Schema::new(&view);
            for precommit in precommits {
                schema.precommits(&block_hash).append(precommit.clone())?;
            }

            let mut node_state = NodeState::new(state, &view);
            for service in self.service_map.values() {
                service.handle_commit(&mut node_state)?;
            }
            (view.changes(), node_state.transactions())
        };
        self.merge(&patch)?;
        Ok(txs)
    }
}<|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 vec_map::VecMap;
use byteorder::{ByteOrder, LittleEndian};
use mount::Mount;

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 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: Box<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,
            service_map: Arc::new(service_map),
            service_keypair: (service_public_key, service_secret_key),
            api_sender,
        }
    }

    /// Returnts 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().unwrap()
    }

    /// 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 pubkey = peers.iter().find(|&(_, ref v)| v.addr() == *addr).map(|(k, _)| k);
            if let Some(pubkey) = pubkey {
                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: self.db.clone(),
            service_map: Arc::clone(&self.service_map),
            api_sender: self.api_sender.clone(),
            service_keypair: self.service_keypair.clone(),
        }
    }
}
<commit_msg>Fix rustfmt issue<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 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 vec_map::VecMap;
use byteorder::{ByteOrder, LittleEndian};
use mount::Mount;

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 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: Box<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,
            service_map: Arc::new(service_map),
            service_keypair: (service_public_key, service_secret_key),
            api_sender,
        }
    }

    /// Returnts 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().unwrap()
    }

    /// 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: self.db.clone(),
            service_map: Arc::clone(&self.service_map),
            api_sender: self.api_sender.clone(),
            service_keypair: self.service_keypair.clone(),
        }
    }
}
<|endoftext|>


<commit_before>//! All keys in leveldb are compared by their binary value unless
//! defined otherwise.
//!
//! Comparators allow to override this comparison.
//! The ordering of keys introduced by the compartor influences iteration order.
//! Databases written with one Comparator cannot be opened with another.
use cbits::leveldb::*;
use libc::{size_t,c_void};
use libc;
use std::mem;
use std::slice;
use std::cmp::Ordering;
use database::db_key::Key;
use database::db_key::from_u8;
use core::marker::PhantomData;

/// A comparator has two important functions:
///
/// * the name function returns a fixed name to detect errors when
///   opening databases with a different name
/// * The comparison implementation
pub trait Comparator {
     type K: Key;

     /// Return the name of the Comparator
     fn name(&self) -> *const u8;
     /// compare two keys. This must implement a total ordering.
     fn compare(&self, a: &Self::K, b: &Self::K) -> Ordering;
     /// whether the comparator is the `DefaultComparator`
     fn null() -> bool {
         false
     }
}

/// OrdComparator is a comparator comparing Keys that implement `Ord`
#[derive(Copy)]
pub struct OrdComparator<K> {
    marker: PhantomData<K>,
}
/// DefaultComparator is the a stand in for "no comparator set"
#[derive(Copy)]
pub struct DefaultComparator;

extern "C" fn name<K: Key, T: Comparator>(state: *mut libc::c_void) -> *const u8 {
     let x: &T = unsafe { &*(state as *mut T) };
     x.name()
}

extern "C" fn compare<K: Key, T: Comparator>(state: *mut libc::c_void,
                                     a: *const u8, a_len: size_t,
                                     b: *const u8, b_len: size_t) -> i32 {
     unsafe {
          let a_slice = slice::from_raw_parts::<u8>(a, a_len as usize);
          let b_slice = slice::from_raw_parts::<u8>(b, b_len as usize);
          let x: &T = &*(state as *mut T);
          let a_key = from_u8::<<T as Comparator>::K>(a_slice);
          let b_key = from_u8::<<T as Comparator>::K>(b_slice);
          match x.compare(&a_key, &b_key) {
              Ordering::Less => -1,
              Ordering::Equal => 0,
              Ordering::Greater => 1
          }
     }
}

extern "C" fn destructor<T>(state: *mut libc::c_void) {
     let _x: Box<T> = unsafe {mem::transmute(state)};
     // let the Box fall out of scope and run the T's destructor
}

#[allow(missing_docs)]
pub fn create_comparator<K: Key, T: Comparator<K = K>>(x: Box<T>) -> *mut leveldb_comparator_t {
     unsafe {
          leveldb_comparator_create(mem::transmute(x),
                                    destructor::<T>,
                                    compare::<K, T>,
                                    name::<K, T>)
     }
}

impl<K: Key + Ord> Comparator for OrdComparator<K> {
  type K = K;

  fn name(&self) -> *const u8 {
    "ord_comparator".as_ptr()
  }

  fn compare(&self, a: &K, b: &K) -> Ordering {
    a.cmp(b)
  }
}

impl Comparator for DefaultComparator {
  type K = i32;

  fn name(&self) -> *const u8 {
    "default_comparator".as_ptr()
  }

  fn compare(&self, _a: &i32, _b: &i32) -> Ordering {
    Ordering::Equal
  }

  fn null() -> bool {
    true
  }
}
<commit_msg>Let OrdComparator encode the type in the name<commit_after>//! All keys in leveldb are compared by their binary value unless
//! defined otherwise.
//!
//! Comparators allow to override this comparison.
//! The ordering of keys introduced by the compartor influences iteration order.
//! Databases written with one Comparator cannot be opened with another.
use cbits::leveldb::*;
use libc::{size_t,c_void};
use libc;
use std::mem;
use std::slice;
use std::cmp::Ordering;
use database::db_key::Key;
use database::db_key::from_u8;
use core::marker::PhantomData;

/// A comparator has two important functions:
///
/// * the name function returns a fixed name to detect errors when
///   opening databases with a different name
/// * The comparison implementation
pub trait Comparator {
     type K: Key;

     /// Return the name of the Comparator
     fn name(&self) -> *const u8;
     /// compare two keys. This must implement a total ordering.
     fn compare(&self, a: &Self::K, b: &Self::K) -> Ordering;
     /// whether the comparator is the `DefaultComparator`
     fn null() -> bool {
         false
     }
}

/// OrdComparator is a comparator comparing Keys that implement `Ord`
#[derive(Copy)]
pub struct OrdComparator<K> {
    marker: PhantomData<K>,
}
/// DefaultComparator is the a stand in for "no comparator set"
#[derive(Copy)]
pub struct DefaultComparator;

extern "C" fn name<K: Key, T: Comparator>(state: *mut libc::c_void) -> *const u8 {
     let x: &T = unsafe { &*(state as *mut T) };
     x.name()
}

extern "C" fn compare<K: Key, T: Comparator>(state: *mut libc::c_void,
                                     a: *const u8, a_len: size_t,
                                     b: *const u8, b_len: size_t) -> i32 {
     unsafe {
          let a_slice = slice::from_raw_parts::<u8>(a, a_len as usize);
          let b_slice = slice::from_raw_parts::<u8>(b, b_len as usize);
          let x: &T = &*(state as *mut T);
          let a_key = from_u8::<<T as Comparator>::K>(a_slice);
          let b_key = from_u8::<<T as Comparator>::K>(b_slice);
          match x.compare(&a_key, &b_key) {
              Ordering::Less => -1,
              Ordering::Equal => 0,
              Ordering::Greater => 1
          }
     }
}

extern "C" fn destructor<T>(state: *mut libc::c_void) {
     let _x: Box<T> = unsafe {mem::transmute(state)};
     // let the Box fall out of scope and run the T's destructor
}

#[allow(missing_docs)]
pub fn create_comparator<K: Key, T: Comparator<K = K>>(x: Box<T>) -> *mut leveldb_comparator_t {
     unsafe {
          leveldb_comparator_create(mem::transmute(x),
                                    destructor::<T>,
                                    compare::<K, T>,
                                    name::<K, T>)
     }
}

impl<K: Key + Ord> Comparator for OrdComparator<K> {
  type K = K;

  fn name(&self) -> *const u8 {
    use std::intrinsics::get_tydesc;

    let tydesc = unsafe { *get_tydesc::<K>() };
    tydesc.name.as_ptr()
  }

  fn compare(&self, a: &K, b: &K) -> Ordering {
    a.cmp(b)
  }
}

impl Comparator for DefaultComparator {
  type K = i32;

  fn name(&self) -> *const u8 {
    "default_comparator".as_ptr()
  }

  fn compare(&self, _a: &i32, _b: &i32) -> Ordering {
    Ordering::Equal
  }

  fn null() -> bool {
    true
  }
}
<|endoftext|>


<commit_before>
use std::path::Path;
use toml::Value;
use error::Result;
use failure::err_msg;

/// Metadata for custom builds
///
/// You can customize docs.rs builds by defining `[package.metadata.docs.rs]` table in your
/// crates' `Cargo.toml`.
///
/// An example metadata:
///
/// ```text
/// [package]
/// name = "test"
///
/// [package.metadata.docs.rs]
/// features = [ "feature1", "feature2" ]
/// all-features = true
/// no-default-features = true
/// default-target = "x86_64-unknown-linux-gnu"
/// extra-targets = [ "x86_64-apple-darwin", "x86_64-pc-windows-msvc" ]
/// rustc-args = [ "--example-rustc-arg" ]
/// rustdoc-args = [ "--example-rustdoc-arg" ]
/// ```
///
/// You can define one or more fields in your `Cargo.toml`.
pub struct Metadata {
    /// List of features docs.rs will build.
    ///
    /// By default, docs.rs will only build default features.
    pub features: Option<Vec<String>>,

    /// Set `all-features` to true if you want docs.rs to build all features for your crate
    pub all_features: bool,

    /// Docs.rs will always build default features.
    ///
    /// Set `no-default-fatures` to `false` if you want to build only certain features.
    pub no_default_features: bool,

    /// Docs.rs is running on `x86_64-unknown-linux-gnu` target system and default documentation
    /// is always built on this target. You can change default target by setting this.
    pub default_target: Option<String>,

    /// If you want a crate to build only for specific targets,
    /// set `extra-targets` to the list of targets to build, in addition to `default-target`.
    ///
    /// If you do not set `extra_targets`, all of the tier 1 supported targets will be built.
    /// If you set `extra_targets` to an empty array, only the default target will be built.
    pub extra_targets: Option<Vec<String>>,

    /// List of command line arguments for `rustc`.
    pub rustc_args: Option<Vec<String>>,

    /// List of command line arguments for `rustdoc`.
    pub rustdoc_args: Option<Vec<String>>,
}

impl Metadata {
    pub(crate) fn from_source_dir(source_dir: &Path) -> Result<Metadata> {
        for c in ["Cargo.toml.orig", "Cargo.toml"].iter() {
            let manifest_path = source_dir.clone().join(c);
            if manifest_path.exists() {
                return Ok(Metadata::from_manifest(manifest_path));
            }
        }
        Err(err_msg("Manifest not found"))
    }

    fn from_manifest<P: AsRef<Path>>(path: P) -> Metadata {
        use std::fs::File;
        use std::io::Read;
        let mut f = match File::open(path) {
            Ok(f) => f,
            Err(_) => return Metadata::default(),
        };
        let mut s = String::new();
        if let Err(_) = f.read_to_string(&mut s) {
            return Metadata::default();
        }
        Metadata::from_str(&s)
    }

    // This is similar to Default trait but it's private
    fn default() -> Metadata {
        Metadata {
            features: None,
            all_features: false,
            no_default_features: false,
            default_target: None,
            rustc_args: None,
            rustdoc_args: None,
            extra_targets: None,
        }
    }

    fn from_str(manifest: &str) -> Metadata {
        let mut metadata = Metadata::default();

        let manifest = match manifest.parse::<Value>() {
            Ok(m) => m,
            Err(_) => return metadata,
        };

        if let Some(table) = manifest.get("package").and_then(|p| p.as_table())
            .and_then(|p| p.get("metadata")).and_then(|p| p.as_table())
                .and_then(|p| p.get("docs")).and_then(|p| p.as_table())
                .and_then(|p| p.get("rs")).and_then(|p| p.as_table()) {
                    metadata.features = table.get("features").and_then(|f| f.as_array())
                        .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
                    metadata.no_default_features = table.get("no-default-features")
                        .and_then(|v| v.as_bool()).unwrap_or(metadata.no_default_features);
                    metadata.all_features = table.get("all-features")
                        .and_then(|v| v.as_bool()).unwrap_or(metadata.all_features);
                    metadata.default_target = table.get("default-target")
                        .and_then(|v| v.as_str()).map(|v| v.to_owned());
                    metadata.extra_targets = table.get("extra-targets").and_then(|f| f.as_array())
                        .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
                    metadata.rustc_args = table.get("rustc-args").and_then(|f| f.as_array())
                        .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
                    metadata.rustdoc_args = table.get("rustdoc-args").and_then(|f| f.as_array())
                        .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
                }

        metadata
    }
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::Metadata;

    #[test]
    fn test_cratesfyi_metadata() {
        let _ = env_logger::try_init();
        let manifest = r#"
            [package]
            name = "test"

            [package.metadata.docs.rs]
            features = [ "feature1", "feature2" ]
            all-features = true
            no-default-features = true
            default-target = "x86_64-unknown-linux-gnu"
            extra-targets = [ "x86_64-apple-darwin", "x86_64-pc-windows-msvc" ]
            rustc-args = [ "--example-rustc-arg" ]
            rustdoc-args = [ "--example-rustdoc-arg" ]
        "#;

        let metadata = Metadata::from_str(manifest);

        assert!(metadata.features.is_some());
        assert!(metadata.all_features == true);
        assert!(metadata.no_default_features == true);
        assert!(metadata.default_target.is_some());
        assert!(metadata.rustdoc_args.is_some());

        let features = metadata.features.unwrap();
        assert_eq!(features.len(), 2);
        assert_eq!(features[0], "feature1".to_owned());
        assert_eq!(features[1], "feature2".to_owned());

        assert_eq!(metadata.default_target.unwrap(), "x86_64-unknown-linux-gnu".to_owned());

        let extra_targets = metadata.extra_targets;
        assert_eq!(extra_targets.len(), 2);
        assert_eq!(extra_targets[0], "x86_64-apple-darwin");
        assert_eq!(extra_targets[1], "x86_64-pc-windows-msvc");

        let rustc_args = metadata.rustc_args.unwrap();
        assert_eq!(rustc_args.len(), 1);
        assert_eq!(rustc_args[0], "--example-rustc-arg".to_owned());

        let rustdoc_args = metadata.rustdoc_args.unwrap();
        assert_eq!(rustdoc_args.len(), 1);
        assert_eq!(rustdoc_args[0], "--example-rustdoc-arg".to_owned());
    }
}
<commit_msg>Fix tests (and new test)<commit_after>
use std::path::Path;
use toml::Value;
use error::Result;
use failure::err_msg;

/// Metadata for custom builds
///
/// You can customize docs.rs builds by defining `[package.metadata.docs.rs]` table in your
/// crates' `Cargo.toml`.
///
/// An example metadata:
///
/// ```text
/// [package]
/// name = "test"
///
/// [package.metadata.docs.rs]
/// features = [ "feature1", "feature2" ]
/// all-features = true
/// no-default-features = true
/// default-target = "x86_64-unknown-linux-gnu"
/// extra-targets = [ "x86_64-apple-darwin", "x86_64-pc-windows-msvc" ]
/// rustc-args = [ "--example-rustc-arg" ]
/// rustdoc-args = [ "--example-rustdoc-arg" ]
/// ```
///
/// You can define one or more fields in your `Cargo.toml`.
pub struct Metadata {
    /// List of features docs.rs will build.
    ///
    /// By default, docs.rs will only build default features.
    pub features: Option<Vec<String>>,

    /// Set `all-features` to true if you want docs.rs to build all features for your crate
    pub all_features: bool,

    /// Docs.rs will always build default features.
    ///
    /// Set `no-default-fatures` to `false` if you want to build only certain features.
    pub no_default_features: bool,

    /// Docs.rs is running on `x86_64-unknown-linux-gnu` target system and default documentation
    /// is always built on this target. You can change default target by setting this.
    pub default_target: Option<String>,

    /// If you want a crate to build only for specific targets,
    /// set `extra-targets` to the list of targets to build, in addition to `default-target`.
    ///
    /// If you do not set `extra_targets`, all of the tier 1 supported targets will be built.
    /// If you set `extra_targets` to an empty array, only the default target will be built.
    pub extra_targets: Option<Vec<String>>,

    /// List of command line arguments for `rustc`.
    pub rustc_args: Option<Vec<String>>,

    /// List of command line arguments for `rustdoc`.
    pub rustdoc_args: Option<Vec<String>>,
}

impl Metadata {
    pub(crate) fn from_source_dir(source_dir: &Path) -> Result<Metadata> {
        for c in ["Cargo.toml.orig", "Cargo.toml"].iter() {
            let manifest_path = source_dir.clone().join(c);
            if manifest_path.exists() {
                return Ok(Metadata::from_manifest(manifest_path));
            }
        }
        Err(err_msg("Manifest not found"))
    }

    fn from_manifest<P: AsRef<Path>>(path: P) -> Metadata {
        use std::fs::File;
        use std::io::Read;
        let mut f = match File::open(path) {
            Ok(f) => f,
            Err(_) => return Metadata::default(),
        };
        let mut s = String::new();
        if let Err(_) = f.read_to_string(&mut s) {
            return Metadata::default();
        }
        Metadata::from_str(&s)
    }

    // This is similar to Default trait but it's private
    fn default() -> Metadata {
        Metadata {
            features: None,
            all_features: false,
            no_default_features: false,
            default_target: None,
            rustc_args: None,
            rustdoc_args: None,
            extra_targets: None,
        }
    }

    fn from_str(manifest: &str) -> Metadata {
        let mut metadata = Metadata::default();

        let manifest = match manifest.parse::<Value>() {
            Ok(m) => m,
            Err(_) => return metadata,
        };

        if let Some(table) = manifest.get("package").and_then(|p| p.as_table())
            .and_then(|p| p.get("metadata")).and_then(|p| p.as_table())
                .and_then(|p| p.get("docs")).and_then(|p| p.as_table())
                .and_then(|p| p.get("rs")).and_then(|p| p.as_table()) {
                    metadata.features = table.get("features").and_then(|f| f.as_array())
                        .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
                    metadata.no_default_features = table.get("no-default-features")
                        .and_then(|v| v.as_bool()).unwrap_or(metadata.no_default_features);
                    metadata.all_features = table.get("all-features")
                        .and_then(|v| v.as_bool()).unwrap_or(metadata.all_features);
                    metadata.default_target = table.get("default-target")
                        .and_then(|v| v.as_str()).map(|v| v.to_owned());
                    metadata.extra_targets = table.get("extra-targets").and_then(|f| f.as_array())
                        .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
                    metadata.rustc_args = table.get("rustc-args").and_then(|f| f.as_array())
                        .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
                    metadata.rustdoc_args = table.get("rustdoc-args").and_then(|f| f.as_array())
                        .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
                }

        metadata
    }
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::Metadata;

    #[test]
    fn test_cratesfyi_metadata() {
        let _ = env_logger::try_init();
        let manifest = r#"
            [package]
            name = "test"

            [package.metadata.docs.rs]
            features = [ "feature1", "feature2" ]
            all-features = true
            no-default-features = true
            default-target = "x86_64-unknown-linux-gnu"
            extra-targets = [ "x86_64-apple-darwin", "x86_64-pc-windows-msvc" ]
            rustc-args = [ "--example-rustc-arg" ]
            rustdoc-args = [ "--example-rustdoc-arg" ]
        "#;

        let metadata = Metadata::from_str(manifest);

        assert!(metadata.features.is_some());
        assert!(metadata.all_features == true);
        assert!(metadata.no_default_features == true);
        assert!(metadata.default_target.is_some());
        assert!(metadata.rustdoc_args.is_some());

        let features = metadata.features.unwrap();
        assert_eq!(features.len(), 2);
        assert_eq!(features[0], "feature1".to_owned());
        assert_eq!(features[1], "feature2".to_owned());

        assert_eq!(metadata.default_target.unwrap(), "x86_64-unknown-linux-gnu".to_owned());

        let extra_targets = metadata.extra_targets.expect("should have explicit extra target");
        assert_eq!(extra_targets.len(), 2);
        assert_eq!(extra_targets[0], "x86_64-apple-darwin");
        assert_eq!(extra_targets[1], "x86_64-pc-windows-msvc");

        let rustc_args = metadata.rustc_args.unwrap();
        assert_eq!(rustc_args.len(), 1);
        assert_eq!(rustc_args[0], "--example-rustc-arg".to_owned());

        let rustdoc_args = metadata.rustdoc_args.unwrap();
        assert_eq!(rustdoc_args.len(), 1);
        assert_eq!(rustdoc_args[0], "--example-rustdoc-arg".to_owned());
    }

    #[test]
    fn test_no_extra_targets() {
        // metadata section but no extra_targets
        let manifest = r#"
            [package]
            name = "test"

            [package.metadata.docs.rs]
            features = [ "feature1", "feature2" ]
        "#;
        let metadata = Metadata::from_str(manifest);
        assert!(metadata.extra_targets.is_none());

        // no package.metadata.docs.rs section
        let metadata = Metadata::from_str(r#"
            [package]
            name = "test"
        "#);
        assert!(metadata.extra_targets.is_none());

        // extra targets explicitly set to empty array
        let metadata = Metadata::from_str(r#"
            [package.metadata.docs.rs]
            extra-targets = []
        "#);
        assert!(metadata.extra_targets.unwrap().is_empty());
    }
}
<|endoftext|>