<commit_before>pub use self::inner::*;

#[cfg(unix)]
mod inner {
    use libc::{c_int, c_long, c_char, time_t};
    use std::mem;
    use std::io;
    use ::Tm;

    #[cfg(any(target_os = "macos", target_os = "ios"))]
    pub use self::mac::*;
    #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))]
    pub use self::unix::*;

    /// ctime's `tm`
    #[repr(C)]
    struct tm {
        tm_sec: c_int,
        tm_min: c_int,
        tm_hour: c_int,
        tm_mday: c_int,
        tm_mon: c_int,
        tm_year: c_int,
        tm_wday: c_int,
        tm_yday: c_int,
        tm_isdst: c_int,
        tm_gmtoff: c_long,
        tm_zone: *const c_char,
    }

    fn rust_tm_to_tm(rust_tm: &Tm, tm: &mut tm) {
        tm.tm_sec = rust_tm.tm_sec;
        tm.tm_min = rust_tm.tm_min;
        tm.tm_hour = rust_tm.tm_hour;
        tm.tm_mday = rust_tm.tm_mday;
        tm.tm_mon = rust_tm.tm_mon;
        tm.tm_year = rust_tm.tm_year;
        tm.tm_wday = rust_tm.tm_wday;
        tm.tm_yday = rust_tm.tm_yday;
        tm.tm_isdst = rust_tm.tm_isdst;
    }

    fn tm_to_rust_tm(tm: &tm, utcoff: i32, rust_tm: &mut Tm) {
        rust_tm.tm_sec = tm.tm_sec;
        rust_tm.tm_min = tm.tm_min;
        rust_tm.tm_hour = tm.tm_hour;
        rust_tm.tm_mday = tm.tm_mday;
        rust_tm.tm_mon = tm.tm_mon;
        rust_tm.tm_year = tm.tm_year;
        rust_tm.tm_wday = tm.tm_wday;
        rust_tm.tm_yday = tm.tm_yday;
        rust_tm.tm_isdst = tm.tm_isdst;
        rust_tm.tm_utcoff = utcoff;
    }

    extern {
        fn gmtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
        fn localtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
        fn timegm(tm: *const tm) -> time_t;
        fn mktime(tm: *const tm) -> time_t;
    }

    pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) {
        unsafe {
            let mut out = mem::zeroed();
            if gmtime_r(&sec, &mut out).is_null() {
                panic!("gmtime_r failed: {}", io::Error::last_os_error());
            }
            tm_to_rust_tm(&out, 0, tm);
        }
    }

    pub fn time_to_local_tm(sec: i64, tm: &mut Tm) {
        unsafe {
            let mut out = mem::zeroed();
            if localtime_r(&sec, &mut out).is_null() {
                panic!("localtime_r failed: {}", io::Error::last_os_error());
            }
            tm_to_rust_tm(&out, out.tm_gmtoff as i32, tm);
        }
    }

    pub fn utc_tm_to_time(rust_tm: &Tm) -> i64 {
        let mut tm = unsafe { mem::zeroed() };
        rust_tm_to_tm(rust_tm, &mut tm);
        unsafe { timegm(&tm) }
    }

    pub fn local_tm_to_time(rust_tm: &Tm) -> i64 {
        let mut tm = unsafe { mem::zeroed() };
        rust_tm_to_tm(rust_tm, &mut tm);
        unsafe { mktime(&tm) }
    }

    #[cfg(any(target_os = "macos", target_os = "ios"))]
    mod mac {
        use libc::{timeval, timezone, c_int, mach_timebase_info};
        use std::sync::{Once, ONCE_INIT};

        extern {
            fn gettimeofday(tp: *mut timeval, tzp: *mut timezone) -> c_int;
            fn mach_absolute_time() -> u64;
            fn mach_timebase_info(info: *mut mach_timebase_info) -> c_int;
        }

        fn info() -> &'static mach_timebase_info {
            static mut INFO: mach_timebase_info = mach_timebase_info {
                numer: 0,
                denom: 0,
            };
            static ONCE: Once = ONCE_INIT;

            unsafe {
                ONCE.call_once(|| {
                    mach_timebase_info(&mut INFO);
                });
                &INFO
            }
        }

        pub fn get_time() -> (i64, i32) {
            use std::ptr;
            let mut tv = libc::timeval { tv_sec: 0, tv_usec: 0 };
            unsafe { gettimeofday(&mut tv, ptr::null_mut()); }
            (tv.tv_sec as i64, tv.tv_usec * 1000)
        }

        pub fn get_precise_ns() -> u64 {
            unsafe {
                let time = mach_absolute_time();
                let info = info();
                time * info.numer as u64 / info.denom as u64
            }
        }
    }

    #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))]
    mod unix {
        use libc::{self, c_int, timespec};

        #[cfg(all(not(target_os = "android"),
                  not(target_os = "bitrig"),
                  not(target_os = "nacl"),
                  not(target_os = "openbsd")))]
        #[link(name = "rt")]
        extern {}

        extern {
            fn clock_gettime(clk_id: c_int, tp: *mut timespec) -> c_int;
        }

        pub fn get_time() -> (i64, i32) {
            let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 };
            unsafe { clock_gettime(libc::CLOCK_REALTIME, &mut tv); }
            (tv.tv_sec as i64, tv.tv_nsec as i32)
        }

        pub fn get_precise_ns() -> u64 {
            let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
            unsafe {
                clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
            }
            (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64)
        }
    }
}

#[cfg(windows)]
#[allow(non_snake_case)]
mod inner {
    use ::Tm;
    use std::mem;
    use std::sync::{Once, ONCE_INIT};

    use winapi::*;
    use kernel32::*;

    fn frequency() -> LARGE_INTEGER {
        static mut FREQUENCY: LARGE_INTEGER = 0;
        static ONCE: Once = ONCE_INIT;

        unsafe {
            ONCE.call_once(|| {
                QueryPerformanceFrequency(&mut FREQUENCY);
            });
            FREQUENCY
        }
    }

    const HECTONANOSECS_IN_SEC: u64 = 10_000_000;
    const HECTONANOSEC_TO_UNIX_EPOCH: u64 = 11_644_473_600 * HECTONANOSECS_IN_SEC;

    fn time_to_file_time(sec: i64) -> FILETIME {
        let t = (sec as u64 * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH;
        FILETIME {
            dwLowDateTime: t as DWORD,
            dwHighDateTime: (t >> 32) as DWORD
        }
    }

    fn file_time_to_nsec(ft: &FILETIME) -> i32 {
        let t = ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64);
        (((t - HECTONANOSEC_TO_UNIX_EPOCH) % HECTONANOSECS_IN_SEC) * 100) as i32
    }

    fn file_time_to_time(ft: &FILETIME) -> i64 {
        let t = ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64);
        ((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64
    }

    fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME {
        let mut sys: SYSTEMTIME = unsafe { mem::zeroed() };
        sys.wSecond = tm.tm_sec as WORD;
        sys.wMinute = tm.tm_min as WORD;
        sys.wHour = tm.tm_hour as WORD;
        sys.wDay = tm.tm_mday as WORD;
        sys.wDayOfWeek = tm.tm_wday as WORD;
        sys.wMonth = (tm.tm_mon + 1) as WORD;
        sys.wYear = (tm.tm_year + 1900) as WORD;
        sys
    }

    fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) {
        tm.tm_sec = sys.wSecond as i32;
        tm.tm_min = sys.wMinute as i32;
        tm.tm_hour = sys.wHour as i32;
        tm.tm_mday = sys.wDay as i32;
        tm.tm_wday = sys.wDayOfWeek as i32;
        tm.tm_mon = (sys.wMonth - 1) as i32;
        tm.tm_year = (sys.wYear - 1900) as i32;
        tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday);

        fn yday(year: i32, month: i32, day: i32) -> i32 {
            let leap = if month > 2 {
                if year % 4 == 0 { 1 } else { 2 }
            } else {
                0
            };
            let july = if month > 7 { 1 } else { 0 };

            (month - 1) * 30 + month / 2 + (day - 1) - leap + july
        }
    }

    pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) {
        let mut out = unsafe { mem::zeroed() };
        let ft = time_to_file_time(sec);
        unsafe {
            FileTimeToSystemTime(&ft, &mut out);
        }
        system_time_to_tm(&out, tm);
        tm.tm_utcoff = 0;
    }

    pub fn time_to_local_tm(sec: i64, tm: &mut Tm) {
        let mut out = unsafe { mem::zeroed() };
        let tz = time_zone(None);
        let ft = time_to_file_time(sec);
        unsafe {
            let mut utc = mem::zeroed();
            FileTimeToSystemTime(&ft, &mut utc);
            SystemTimeToTzSpecificLocalTime(tz, &mut utc, &mut out);
            system_time_to_tm(&out, tm);
            tm.tm_utcoff = -(*tz).Bias * 60;
        }
    }

    pub fn utc_tm_to_time(tm: &Tm) -> i64 {
        unsafe {
            let mut ft = mem::zeroed();
            let sys_time = tm_to_system_time(tm);
            SystemTimeToFileTime(&sys_time, &mut ft);
            file_time_to_time(&ft)
        }
    }

    pub fn local_tm_to_time(tm: &Tm) -> i64 {
        unsafe {
            let mut ft = mem::zeroed();
            let mut utc = mem::zeroed();
            let mut sys_time = tm_to_system_time(tm);
            TzSpecificLocalTimeToSystemTime(time_zone(None), &mut sys_time, &mut utc);
            SystemTimeToFileTime(&utc, &mut ft);
            file_time_to_time(&ft)
        }
    }

    pub fn get_time() -> (i64, i32) {
        unsafe {
            let mut ft = mem::zeroed();
            GetSystemTimeAsFileTime(&mut ft);
            (file_time_to_time(&ft), file_time_to_nsec(&ft))
        }
    }

    pub fn get_precise_ns() -> u64 {
        let mut ticks = 0;
        unsafe {
            assert!(QueryPerformanceCounter(&mut ticks) == 1);
        }
        mul_div_i64(ticks as i64, 1000000000, frequency() as i64) as u64

    }

    pub fn time_zone(bias: Option<i32>) -> *const TIME_ZONE_INFORMATION {
        use std::ptr;
        static mut TZ: Option<TIME_ZONE_INFORMATION> = None;
        static ONCE: Once = ONCE_INIT;

        unsafe {
            ONCE.call_once(|| {
                if let Some(bias) = bias {
                    let mut tz: TIME_ZONE_INFORMATION = mem::zeroed();
                    tz.Bias = bias;
                    TZ = Some(tz);
                }
            });

            match TZ {
                Some(ref tz) => tz,
                None => ptr::null()
            }
        }
    }

    // Computes (value*numer)/denom without overflow, as long as both
    // (numer*denom) and the overall result fit into i64 (which is the case
    // for our time conversions).
    fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 {
        let q = value / denom;
        let r = value % denom;
        // Decompose value as (value/denom*denom + value%denom),
        // substitute into (value*numer)/denom and simplify.
        // r < denom, so (denom*numer) is the upper bound of (r*numer)
        q * numer + r * numer / denom
    }

    #[test]
    fn test_muldiv() {
        assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000),
                   1_000_000_000_001_000);
        assert_eq!(mul_div_i64(-1_000_000_000_001, 1_000_000_000, 1_000_000),
                   -1_000_000_000_001_000);
        assert_eq!(mul_div_i64(-1_000_000_000_001,-1_000_000_000, 1_000_000),
                   1_000_000_000_001_000);
        assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000,-1_000_000),
                   -1_000_000_000_001_000);
        assert_eq!(mul_div_i64( 1_000_000_000_001,-1_000_000_000,-1_000_000),
                   1_000_000_000_001_000);
    }
}
<commit_msg>Add error handling to windows calls<commit_after>pub use self::inner::*;

#[cfg(unix)]
mod inner {
    use libc::{c_int, c_long, c_char, time_t};
    use std::mem;
    use std::io;
    use ::Tm;

    #[cfg(any(target_os = "macos", target_os = "ios"))]
    pub use self::mac::*;
    #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))]
    pub use self::unix::*;

    /// ctime's `tm`
    #[repr(C)]
    struct tm {
        tm_sec: c_int,
        tm_min: c_int,
        tm_hour: c_int,
        tm_mday: c_int,
        tm_mon: c_int,
        tm_year: c_int,
        tm_wday: c_int,
        tm_yday: c_int,
        tm_isdst: c_int,
        tm_gmtoff: c_long,
        tm_zone: *const c_char,
    }

    fn rust_tm_to_tm(rust_tm: &Tm, tm: &mut tm) {
        tm.tm_sec = rust_tm.tm_sec;
        tm.tm_min = rust_tm.tm_min;
        tm.tm_hour = rust_tm.tm_hour;
        tm.tm_mday = rust_tm.tm_mday;
        tm.tm_mon = rust_tm.tm_mon;
        tm.tm_year = rust_tm.tm_year;
        tm.tm_wday = rust_tm.tm_wday;
        tm.tm_yday = rust_tm.tm_yday;
        tm.tm_isdst = rust_tm.tm_isdst;
    }

    fn tm_to_rust_tm(tm: &tm, utcoff: i32, rust_tm: &mut Tm) {
        rust_tm.tm_sec = tm.tm_sec;
        rust_tm.tm_min = tm.tm_min;
        rust_tm.tm_hour = tm.tm_hour;
        rust_tm.tm_mday = tm.tm_mday;
        rust_tm.tm_mon = tm.tm_mon;
        rust_tm.tm_year = tm.tm_year;
        rust_tm.tm_wday = tm.tm_wday;
        rust_tm.tm_yday = tm.tm_yday;
        rust_tm.tm_isdst = tm.tm_isdst;
        rust_tm.tm_utcoff = utcoff;
    }

    extern {
        fn gmtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
        fn localtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
        fn timegm(tm: *const tm) -> time_t;
        fn mktime(tm: *const tm) -> time_t;
    }

    pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) {
        unsafe {
            let mut out = mem::zeroed();
            if gmtime_r(&sec, &mut out).is_null() {
                panic!("gmtime_r failed: {}", io::Error::last_os_error());
            }
            tm_to_rust_tm(&out, 0, tm);
        }
    }

    pub fn time_to_local_tm(sec: i64, tm: &mut Tm) {
        unsafe {
            let mut out = mem::zeroed();
            if localtime_r(&sec, &mut out).is_null() {
                panic!("localtime_r failed: {}", io::Error::last_os_error());
            }
            tm_to_rust_tm(&out, out.tm_gmtoff as i32, tm);
        }
    }

    pub fn utc_tm_to_time(rust_tm: &Tm) -> i64 {
        let mut tm = unsafe { mem::zeroed() };
        rust_tm_to_tm(rust_tm, &mut tm);
        unsafe { timegm(&tm) }
    }

    pub fn local_tm_to_time(rust_tm: &Tm) -> i64 {
        let mut tm = unsafe { mem::zeroed() };
        rust_tm_to_tm(rust_tm, &mut tm);
        unsafe { mktime(&tm) }
    }

    #[cfg(any(target_os = "macos", target_os = "ios"))]
    mod mac {
        use libc::{timeval, timezone, c_int, mach_timebase_info};
        use std::sync::{Once, ONCE_INIT};

        extern {
            fn gettimeofday(tp: *mut timeval, tzp: *mut timezone) -> c_int;
            fn mach_absolute_time() -> u64;
            fn mach_timebase_info(info: *mut mach_timebase_info) -> c_int;
        }

        fn info() -> &'static mach_timebase_info {
            static mut INFO: mach_timebase_info = mach_timebase_info {
                numer: 0,
                denom: 0,
            };
            static ONCE: Once = ONCE_INIT;

            unsafe {
                ONCE.call_once(|| {
                    mach_timebase_info(&mut INFO);
                });
                &INFO
            }
        }

        pub fn get_time() -> (i64, i32) {
            use std::ptr;
            let mut tv = libc::timeval { tv_sec: 0, tv_usec: 0 };
            unsafe { gettimeofday(&mut tv, ptr::null_mut()); }
            (tv.tv_sec as i64, tv.tv_usec * 1000)
        }

        pub fn get_precise_ns() -> u64 {
            unsafe {
                let time = mach_absolute_time();
                let info = info();
                time * info.numer as u64 / info.denom as u64
            }
        }
    }

    #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))]
    mod unix {
        use libc::{self, c_int, timespec};

        #[cfg(all(not(target_os = "android"),
                  not(target_os = "bitrig"),
                  not(target_os = "nacl"),
                  not(target_os = "openbsd")))]
        #[link(name = "rt")]
        extern {}

        extern {
            fn clock_gettime(clk_id: c_int, tp: *mut timespec) -> c_int;
        }

        pub fn get_time() -> (i64, i32) {
            let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 };
            unsafe { clock_gettime(libc::CLOCK_REALTIME, &mut tv); }
            (tv.tv_sec as i64, tv.tv_nsec as i32)
        }

        pub fn get_precise_ns() -> u64 {
            let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
            unsafe {
                clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
            }
            (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64)
        }
    }
}

#[cfg(windows)]
#[allow(non_snake_case)]
mod inner {
    use ::Tm;
    use std::io;
    use std::mem;
    use std::sync::{Once, ONCE_INIT};

    use winapi::*;
    use kernel32::*;

    fn frequency() -> LARGE_INTEGER {
        static mut FREQUENCY: LARGE_INTEGER = 0;
        static ONCE: Once = ONCE_INIT;

        unsafe {
            ONCE.call_once(|| {
                QueryPerformanceFrequency(&mut FREQUENCY);
            });
            FREQUENCY
        }
    }

    const HECTONANOSECS_IN_SEC: u64 = 10_000_000;
    const HECTONANOSEC_TO_UNIX_EPOCH: u64 = 11_644_473_600 * HECTONANOSECS_IN_SEC;

    fn time_to_file_time(sec: i64) -> FILETIME {
        let t = (sec as u64 * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH;
        FILETIME {
            dwLowDateTime: t as DWORD,
            dwHighDateTime: (t >> 32) as DWORD
        }
    }

    fn file_time_to_nsec(ft: &FILETIME) -> i32 {
        let t = ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64);
        (((t - HECTONANOSEC_TO_UNIX_EPOCH) % HECTONANOSECS_IN_SEC) * 100) as i32
    }

    fn file_time_to_time(ft: &FILETIME) -> i64 {
        let t = ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64);
        ((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64
    }

    fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME {
        let mut sys: SYSTEMTIME = unsafe { mem::zeroed() };
        sys.wSecond = tm.tm_sec as WORD;
        sys.wMinute = tm.tm_min as WORD;
        sys.wHour = tm.tm_hour as WORD;
        sys.wDay = tm.tm_mday as WORD;
        sys.wDayOfWeek = tm.tm_wday as WORD;
        sys.wMonth = (tm.tm_mon + 1) as WORD;
        sys.wYear = (tm.tm_year + 1900) as WORD;
        sys
    }

    fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) {
        tm.tm_sec = sys.wSecond as i32;
        tm.tm_min = sys.wMinute as i32;
        tm.tm_hour = sys.wHour as i32;
        tm.tm_mday = sys.wDay as i32;
        tm.tm_wday = sys.wDayOfWeek as i32;
        tm.tm_mon = (sys.wMonth - 1) as i32;
        tm.tm_year = (sys.wYear - 1900) as i32;
        tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday);

        fn yday(year: i32, month: i32, day: i32) -> i32 {
            let leap = if month > 2 {
                if year % 4 == 0 { 1 } else { 2 }
            } else {
                0
            };
            let july = if month > 7 { 1 } else { 0 };

            (month - 1) * 30 + month / 2 + (day - 1) - leap + july
        }
    }

    macro_rules! call {
        ($name:ident($($arg:expr),*)) => {
            if $name($($arg),*) == 0 {
                panic!(concat!(stringify!($name), " failed with: {}"),
                       io::Error::last_os_error());
            }
        }
    }

    pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) {
        let mut out = unsafe { mem::zeroed() };
        let ft = time_to_file_time(sec);
        unsafe {
            call!(FileTimeToSystemTime(&ft, &mut out));
        }
        system_time_to_tm(&out, tm);
        tm.tm_utcoff = 0;
    }

    pub fn time_to_local_tm(sec: i64, tm: &mut Tm) {
        let mut out = unsafe { mem::zeroed() };
        let tz = time_zone(None);
        let ft = time_to_file_time(sec);
        unsafe {
            let mut utc = mem::zeroed();
            call!(FileTimeToSystemTime(&ft, &mut utc));
            call!(SystemTimeToTzSpecificLocalTime(tz, &mut utc, &mut out));
            system_time_to_tm(&out, tm);
            tm.tm_utcoff = -(*tz).Bias * 60;
        }
    }

    pub fn utc_tm_to_time(tm: &Tm) -> i64 {
        unsafe {
            let mut ft = mem::zeroed();
            let sys_time = tm_to_system_time(tm);
            call!(SystemTimeToFileTime(&sys_time, &mut ft));
            file_time_to_time(&ft)
        }
    }

    pub fn local_tm_to_time(tm: &Tm) -> i64 {
        unsafe {
            let mut ft = mem::zeroed();
            let mut utc = mem::zeroed();
            let mut sys_time = tm_to_system_time(tm);
            call!(TzSpecificLocalTimeToSystemTime(time_zone(None),
                                                  &mut sys_time, &mut utc));
            call!(SystemTimeToFileTime(&utc, &mut ft));
            file_time_to_time(&ft)
        }
    }

    pub fn get_time() -> (i64, i32) {
        unsafe {
            let mut ft = mem::zeroed();
            GetSystemTimeAsFileTime(&mut ft);
            (file_time_to_time(&ft), file_time_to_nsec(&ft))
        }
    }

    pub fn get_precise_ns() -> u64 {
        let mut ticks = 0;
        unsafe {
            assert!(QueryPerformanceCounter(&mut ticks) == 1);
        }
        mul_div_i64(ticks as i64, 1000000000, frequency() as i64) as u64

    }

    pub fn time_zone(bias: Option<i32>) -> *const TIME_ZONE_INFORMATION {
        use std::ptr;
        static mut TZ: Option<TIME_ZONE_INFORMATION> = None;
        static ONCE: Once = ONCE_INIT;

        unsafe {
            ONCE.call_once(|| {
                if let Some(bias) = bias {
                    let mut tz: TIME_ZONE_INFORMATION = mem::zeroed();
                    tz.Bias = bias;
                    TZ = Some(tz);
                }
            });

            match TZ {
                Some(ref tz) => tz,
                None => ptr::null()
            }
        }
    }

    // Computes (value*numer)/denom without overflow, as long as both
    // (numer*denom) and the overall result fit into i64 (which is the case
    // for our time conversions).
    fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 {
        let q = value / denom;
        let r = value % denom;
        // Decompose value as (value/denom*denom + value%denom),
        // substitute into (value*numer)/denom and simplify.
        // r < denom, so (denom*numer) is the upper bound of (r*numer)
        q * numer + r * numer / denom
    }

    #[test]
    fn test_muldiv() {
        assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000),
                   1_000_000_000_001_000);
        assert_eq!(mul_div_i64(-1_000_000_000_001, 1_000_000_000, 1_000_000),
                   -1_000_000_000_001_000);
        assert_eq!(mul_div_i64(-1_000_000_000_001,-1_000_000_000, 1_000_000),
                   1_000_000_000_001_000);
        assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000,-1_000_000),
                   -1_000_000_000_001_000);
        assert_eq!(mul_div_i64( 1_000_000_000_001,-1_000_000_000,-1_000_000),
                   1_000_000_000_001_000);
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

#![deny(warnings)]
// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![deny(
  clippy::all,
  clippy::default_trait_access,
  clippy::expl_impl_clone_on_copy,
  clippy::if_not_else,
  clippy::needless_continue,
  clippy::unseparated_literal_suffix,
  // TODO: Falsely triggers for async/await:
  //   see https://github.com/rust-lang/rust-clippy/issues/5360
  // clippy::used_underscore_binding
)]
// It is often more clear to show that nothing is being moved.
#![allow(clippy::match_ref_pats)]
// Subjective style.
#![allow(
  clippy::len_without_is_empty,
  clippy::redundant_field_names,
  clippy::too_many_arguments
)]
// Default isn't as big a deal as people seem to think it is.
#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![allow(clippy::mutex_atomic)]

use std::future::Future;
use std::pin::Pin;
use std::time::Duration;

use futures::future::{self, FutureExt, TryFutureExt};
use indexmap::IndexMap;
use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle};
use uuid::Uuid;

use logging::logger::{StdioHandler, PANTS_LOGGER};
use task_executor::Executor;
use workunit_store::{format_workunit_duration, WorkunitStore};

pub struct ConsoleUI {
  workunit_store: WorkunitStore,
  local_parallelism: usize,
  // While the UI is running, there will be an Instance present.
  instance: Option<Instance>,
}

impl ConsoleUI {
  pub fn new(workunit_store: WorkunitStore, local_parallelism: usize) -> ConsoleUI {
    ConsoleUI {
      workunit_store,
      local_parallelism,
      instance: None,
    }
  }

  fn default_draw_target() -> ProgressDrawTarget {
    // NB: We render more frequently than we receive new data in order to minimize aliasing where a
    // render might barely miss a data refresh.
    ProgressDrawTarget::stderr_with_hz(Self::render_rate_hz() * 2)
  }

  ///
  /// The number of times per-second that `Self::render` should be called.
  ///
  pub fn render_rate_hz() -> u64 {
    10
  }

  pub fn render_interval() -> Duration {
    Duration::from_millis(1000 / Self::render_rate_hz())
  }

  pub async fn write_stdout(&mut self, msg: &str) -> Result<(), String> {
    if self.instance.is_some() {
      self.teardown().await?;
    }
    print!("{}", msg);
    Ok(())
  }

  pub fn write_stderr(&self, msg: &str) {
    if let Some(instance) = &self.instance {
      instance.bars[0].println(msg);
    } else {
      eprint!("{}", msg);
    }
  }

  pub async fn with_console_ui_disabled<F: FnOnce() -> T, T>(&mut self, f: F) -> T {
    if self.instance.is_some() {
      self.teardown().await.unwrap();
      f()
    } else {
      f()
    }
  }

  fn setup_bars(num_swimlanes: usize) -> (MultiProgress, Vec<ProgressBar>) {
    let multi_progress_bars = MultiProgress::with_draw_target(Self::default_draw_target());

    let bars = (0..num_swimlanes)
      .map(|_n| {
        let style = ProgressStyle::default_bar().template("{spinner} {wide_msg}");
        multi_progress_bars.add(ProgressBar::new(50).with_style(style))
      })
      .collect();

    (multi_progress_bars, bars)
  }

  fn get_label_from_heavy_hitters<'a>(
    tasks_to_display: impl Iterator<Item = (&'a String, &'a Option<Duration>)>,
  ) -> Vec<String> {
    tasks_to_display
      .map(|(label, maybe_duration)| {
        let duration_label = match maybe_duration {
          None => "(Waiting) ".to_string(),
          Some(duration) => format_workunit_duration(*duration),
        };
        format!("{}{}", duration_label, label)
      })
      .collect()
  }

  ///
  /// Updates all of the swimlane ProgressBars with new data from the WorkunitStore. For this
  /// method to have any effect, the `initialize` method must have been called first.
  ///
  /// *Technically this method does not do the "render"ing: rather, the `MultiProgress` instance
  /// running on a background thread is drives rendering, while this method feeds it new data.
  ///
  pub fn render(&mut self) {
    let instance = if let Some(i) = &mut self.instance {
      i
    } else {
      return;
    };

    let num_swimlanes = instance.bars.len();
    let heavy_hitters = self.workunit_store.heavy_hitters(num_swimlanes);
    let tasks_to_display = &mut instance.tasks_to_display;

    // Insert every one in the set of tasks to display.
    // For tasks already here, the durations are overwritten.
    tasks_to_display.extend(heavy_hitters.clone().into_iter());

    // And remove the tasks that no longer should be there.
    for (task, _) in tasks_to_display.clone().into_iter() {
      if !heavy_hitters.contains_key(&task) {
        tasks_to_display.swap_remove(&task);
      }
    }

    let swimlane_labels: Vec<String> = Self::get_label_from_heavy_hitters(tasks_to_display.iter());
    for (n, pbar) in instance.bars.iter().enumerate() {
      match swimlane_labels.get(n) {
        Some(label) => pbar.set_message(label),
        None => pbar.set_message(""),
      }
    }
  }

  ///
  /// If the ConsoleUI is not already running, starts it.
  ///
  /// Errors if a ConsoleUI is already running (which would likely indicate concurrent usage of a
  /// Session attached to a console).
  ///
  pub fn initialize(
    &mut self,
    executor: Executor,
    stderr_handler: StdioHandler,
  ) -> Result<(), String> {
    if self.instance.is_some() {
      return Err("A ConsoleUI cannot render multiple UIs concurrently.".to_string());
    }

    // Setup bars, and then spawning rendering of the bars into a background task.
    let (multi_progress, bars) = Self::setup_bars(self.local_parallelism);
    let multi_progress_task = {
      executor
        .spawn_blocking(move || multi_progress.join())
        .boxed()
    };

    self.instance = Some(Instance {
      tasks_to_display: IndexMap::new(),
      multi_progress_task,
      logger_handle: PANTS_LOGGER.register_stderr_handler(stderr_handler),
      bars,
    });
    Ok(())
  }

  ///
  /// If the ConsoleUI is running, completes it.
  ///
  pub fn teardown(&mut self) -> impl Future<Output = Result<(), String>> {
    if let Some(instance) = self.instance.take() {
      PANTS_LOGGER.deregister_stderr_handler(instance.logger_handle);
      instance
        .multi_progress_task
        .map_err(|e| format!("Failed to render UI: {}", e))
        .boxed()
    } else {
      future::ok(()).boxed()
    }
  }
}

/// The state for one run of the ConsoleUI.
struct Instance {
  tasks_to_display: IndexMap<String, Option<Duration>>,
  multi_progress_task: Pin<Box<dyn Future<Output = std::io::Result<()>> + Send>>,
  bars: Vec<ProgressBar>,
  logger_handle: Uuid,
}
<commit_msg>Fix for log messages/dynamic UI teardown conflict (#10940)<commit_after>// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

#![deny(warnings)]
// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![deny(
  clippy::all,
  clippy::default_trait_access,
  clippy::expl_impl_clone_on_copy,
  clippy::if_not_else,
  clippy::needless_continue,
  clippy::unseparated_literal_suffix,
  // TODO: Falsely triggers for async/await:
  //   see https://github.com/rust-lang/rust-clippy/issues/5360
  // clippy::used_underscore_binding
)]
// It is often more clear to show that nothing is being moved.
#![allow(clippy::match_ref_pats)]
// Subjective style.
#![allow(
  clippy::len_without_is_empty,
  clippy::redundant_field_names,
  clippy::too_many_arguments
)]
// Default isn't as big a deal as people seem to think it is.
#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![allow(clippy::mutex_atomic)]

use std::future::Future;
use std::pin::Pin;
use std::sync::mpsc;
use std::time::Duration;

use futures::future::{self, FutureExt, TryFutureExt};
use indexmap::IndexMap;
use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle};
use uuid::Uuid;

use logging::logger::{StdioHandler, PANTS_LOGGER};
use task_executor::Executor;
use workunit_store::{format_workunit_duration, WorkunitStore};

pub struct ConsoleUI {
  workunit_store: WorkunitStore,
  local_parallelism: usize,
  // While the UI is running, there will be an Instance present.
  instance: Option<Instance>,
  teardown_in_progress: bool,
  teardown_mpsc: (mpsc::Sender<()>, mpsc::Receiver<()>),
}

impl ConsoleUI {
  pub fn new(workunit_store: WorkunitStore, local_parallelism: usize) -> ConsoleUI {
    ConsoleUI {
      workunit_store,
      local_parallelism,
      instance: None,
      teardown_in_progress: false,
      teardown_mpsc: mpsc::channel(),
    }
  }

  fn default_draw_target() -> ProgressDrawTarget {
    // NB: We render more frequently than we receive new data in order to minimize aliasing where a
    // render might barely miss a data refresh.
    ProgressDrawTarget::stderr_with_hz(Self::render_rate_hz() * 2)
  }

  ///
  /// The number of times per-second that `Self::render` should be called.
  ///
  pub fn render_rate_hz() -> u64 {
    10
  }

  pub fn render_interval() -> Duration {
    Duration::from_millis(1000 / Self::render_rate_hz())
  }

  pub async fn write_stdout(&mut self, msg: &str) -> Result<(), String> {
    if self.instance.is_some() {
      self.teardown().await?;
    }
    print!("{}", msg);
    Ok(())
  }

  pub fn write_stderr(&mut self, msg: &str) {
    if let Some(instance) = &self.instance {
      instance.bars[0].println(msg);
    } else {
      if self.teardown_in_progress {
        let receiver = &self.teardown_mpsc.1;
        let _ = receiver.recv();
        self.teardown_in_progress = false;
      }
      eprint!("{}", msg);
    }
  }

  pub async fn with_console_ui_disabled<F: FnOnce() -> T, T>(&mut self, f: F) -> T {
    if self.instance.is_some() {
      self.teardown().await.unwrap();
      f()
    } else {
      f()
    }
  }

  fn setup_bars(num_swimlanes: usize) -> (MultiProgress, Vec<ProgressBar>) {
    let multi_progress_bars = MultiProgress::with_draw_target(Self::default_draw_target());

    let bars = (0..num_swimlanes)
      .map(|_n| {
        let style = ProgressStyle::default_bar().template("{spinner} {wide_msg}");
        multi_progress_bars.add(ProgressBar::new(50).with_style(style))
      })
      .collect();

    (multi_progress_bars, bars)
  }

  fn get_label_from_heavy_hitters<'a>(
    tasks_to_display: impl Iterator<Item = (&'a String, &'a Option<Duration>)>,
  ) -> Vec<String> {
    tasks_to_display
      .map(|(label, maybe_duration)| {
        let duration_label = match maybe_duration {
          None => "(Waiting) ".to_string(),
          Some(duration) => format_workunit_duration(*duration),
        };
        format!("{}{}", duration_label, label)
      })
      .collect()
  }

  ///
  /// Updates all of the swimlane ProgressBars with new data from the WorkunitStore. For this
  /// method to have any effect, the `initialize` method must have been called first.
  ///
  /// *Technically this method does not do the "render"ing: rather, the `MultiProgress` instance
  /// running on a background thread is drives rendering, while this method feeds it new data.
  ///
  pub fn render(&mut self) {
    let instance = if let Some(i) = &mut self.instance {
      i
    } else {
      return;
    };

    let num_swimlanes = instance.bars.len();
    let heavy_hitters = self.workunit_store.heavy_hitters(num_swimlanes);
    let tasks_to_display = &mut instance.tasks_to_display;

    // Insert every one in the set of tasks to display.
    // For tasks already here, the durations are overwritten.
    tasks_to_display.extend(heavy_hitters.clone().into_iter());

    // And remove the tasks that no longer should be there.
    for (task, _) in tasks_to_display.clone().into_iter() {
      if !heavy_hitters.contains_key(&task) {
        tasks_to_display.swap_remove(&task);
      }
    }

    let swimlane_labels: Vec<String> = Self::get_label_from_heavy_hitters(tasks_to_display.iter());
    for (n, pbar) in instance.bars.iter().enumerate() {
      match swimlane_labels.get(n) {
        Some(label) => pbar.set_message(label),
        None => pbar.set_message(""),
      }
    }
  }

  ///
  /// If the ConsoleUI is not already running, starts it.
  ///
  /// Errors if a ConsoleUI is already running (which would likely indicate concurrent usage of a
  /// Session attached to a console).
  ///
  pub fn initialize(
    &mut self,
    executor: Executor,
    stderr_handler: StdioHandler,
  ) -> Result<(), String> {
    if self.instance.is_some() {
      return Err("A ConsoleUI cannot render multiple UIs concurrently.".to_string());
    }

    // Setup bars, and then spawning rendering of the bars into a background task.
    let (multi_progress, bars) = Self::setup_bars(self.local_parallelism);
    let multi_progress_task = {
      executor
        .spawn_blocking(move || multi_progress.join())
        .boxed()
    };

    self.instance = Some(Instance {
      tasks_to_display: IndexMap::new(),
      multi_progress_task,
      logger_handle: PANTS_LOGGER.register_stderr_handler(stderr_handler),
      bars,
    });
    Ok(())
  }

  ///
  /// If the ConsoleUI is running, completes it.
  ///
  pub fn teardown(&mut self) -> impl Future<Output = Result<(), String>> {
    if let Some(instance) = self.instance.take() {
      let sender = self.teardown_mpsc.0.clone();
      self.teardown_in_progress = true;
      PANTS_LOGGER.deregister_stderr_handler(instance.logger_handle);
      instance
        .multi_progress_task
        .map_err(|e| format!("Failed to render UI: {}", e))
        .and_then(move |()| {
          sender.send(()).unwrap();
          future::ok(())
        })
        .boxed()
    } else {
      future::ok(()).boxed()
    }
  }
}

/// The state for one run of the ConsoleUI.
struct Instance {
  tasks_to_display: IndexMap<String, Option<Duration>>,
  multi_progress_task: Pin<Box<dyn Future<Output = std::io::Result<()>> + Send>>,
  bars: Vec<ProgressBar>,
  logger_handle: Uuid,
}
<|endoftext|>


<commit_before>use crate::cached_beacon_state::CachedBeaconState;
use state_processing::validate_attestation_without_signature;
use std::collections::{HashMap, HashSet};
use types::{
    beacon_state::BeaconStateError, AggregateSignature, Attestation, AttestationData, BeaconState,
    Bitfield, ChainSpec, FreeAttestation, Signature,
};

const PHASE_0_CUSTODY_BIT: bool = false;

/// Provides the functionality to:
///
///  - Recieve a `FreeAttestation` and aggregate it into an `Attestation` (or create a new if it
///  doesn't exist).
///  - Store all aggregated or created `Attestation`s.
///  - Produce a list of attestations that would be valid for inclusion in some `BeaconState` (and
///  therefore valid for inclusion in a `BeaconBlock`.
///
///  Note: `Attestations` are stored in memory and never deleted. This is not scalable and must be
///  rectified in a future revision.
#[derive(Default)]
pub struct AttestationAggregator {
    store: HashMap<Vec<u8>, Attestation>,
}

pub struct Outcome {
    pub valid: bool,
    pub message: Message,
}

pub enum Message {
    /// The free attestation was added to an existing attestation.
    Aggregated,
    /// The free attestation has already been aggregated to an existing attestation.
    AggregationNotRequired,
    /// The free attestation was transformed into a new attestation.
    NewAttestationCreated,
    /// The supplied `validator_index` is not in the committee for the given `shard` and `slot`.
    BadValidatorIndex,
    /// The given `signature` did not match the `pubkey` in the given
    /// `state.validator_registry`.
    BadSignature,
    /// The given `slot` does not match the validators committee assignment.
    BadSlot,
    /// The given `shard` does not match the validators committee assignment.
    BadShard,
}

macro_rules! some_or_invalid {
    ($expression: expr, $error: expr) => {
        match $expression {
            Some(x) => x,
            None => {
                return Ok(Outcome {
                    valid: false,
                    message: $error,
                });
            }
        }
    };
}

impl AttestationAggregator {
    /// Instantiates a new AttestationAggregator with an empty database.
    pub fn new() -> Self {
        Self {
            store: HashMap::new(),
        }
    }

    /// Accepts some `FreeAttestation`, validates it and either aggregates it upon some existing
    /// `Attestation` or produces a new `Attestation`.
    ///
    /// The "validation" provided is not complete, instead the following points are checked:
    ///  - The given `validator_index` is in the committee for the given `shard` for the given
    ///  `slot`.
    ///  - The signature is verified against that of the validator at `validator_index`.
    pub fn process_free_attestation(
        &mut self,
        cached_state: &CachedBeaconState,
        free_attestation: &FreeAttestation,
        spec: &ChainSpec,
    ) -> Result<Outcome, BeaconStateError> {
        let (slot, shard, committee_index) = some_or_invalid!(
            cached_state.attestation_slot_and_shard_for_validator(
                free_attestation.validator_index as usize,
                spec,
            )?,
            Message::BadValidatorIndex
        );

        if free_attestation.data.slot != slot {
            return Ok(Outcome {
                valid: false,
                message: Message::BadSlot,
            });
        }
        if free_attestation.data.shard != shard {
            return Ok(Outcome {
                valid: false,
                message: Message::BadShard,
            });
        }

        let signable_message = free_attestation.data.signable_message(PHASE_0_CUSTODY_BIT);

        let validator_record = some_or_invalid!(
            cached_state
                .state
                .validator_registry
                .get(free_attestation.validator_index as usize),
            Message::BadValidatorIndex
        );

        if !free_attestation.signature.verify(
            &signable_message,
            spec.domain_attestation,
            &validator_record.pubkey,
        ) {
            return Ok(Outcome {
                valid: false,
                message: Message::BadSignature,
            });
        }

        if let Some(existing_attestation) = self.store.get(&signable_message) {
            if let Some(updated_attestation) = aggregate_attestation(
                existing_attestation,
                &free_attestation.signature,
                committee_index as usize,
            ) {
                self.store.insert(signable_message, updated_attestation);
                Ok(Outcome {
                    valid: true,
                    message: Message::Aggregated,
                })
            } else {
                Ok(Outcome {
                    valid: true,
                    message: Message::AggregationNotRequired,
                })
            }
        } else {
            let mut aggregate_signature = AggregateSignature::new();
            aggregate_signature.add(&free_attestation.signature);
            let mut aggregation_bitfield = Bitfield::new();
            aggregation_bitfield.set(committee_index as usize, true);
            let new_attestation = Attestation {
                data: free_attestation.data.clone(),
                aggregation_bitfield,
                custody_bitfield: Bitfield::new(),
                aggregate_signature,
            };
            self.store.insert(signable_message, new_attestation);
            Ok(Outcome {
                valid: true,
                message: Message::NewAttestationCreated,
            })
        }
    }

    /// Returns all known attestations which are:
    ///
    /// - Valid for the given state
    /// - Not already in `state.latest_attestations`.
    pub fn get_attestations_for_state(
        &self,
        state: &BeaconState,
        spec: &ChainSpec,
    ) -> Vec<Attestation> {
        let mut known_attestation_data: HashSet<AttestationData> = HashSet::new();

        state.latest_attestations.iter().for_each(|attestation| {
            known_attestation_data.insert(attestation.data.clone());
        });

        self.store
            .values()
            .filter_map(|attestation| {
                if validate_attestation_without_signature(&state, attestation, spec).is_ok()
                    && !known_attestation_data.contains(&attestation.data)
                {
                    Some(attestation.clone())
                } else {
                    None
                }
            })
            .collect()
    }
}

/// Produces a new `Attestation` where:
///
/// - `signature` is added to `Attestation.aggregate_signature`
/// - Attestation.aggregation_bitfield[committee_index]` is set to true.
fn aggregate_attestation(
    existing_attestation: &Attestation,
    signature: &Signature,
    committee_index: usize,
) -> Option<Attestation> {
    let already_signed = existing_attestation
        .aggregation_bitfield
        .get(committee_index)
        .unwrap_or(false);

    if already_signed {
        None
    } else {
        let mut aggregation_bitfield = existing_attestation.aggregation_bitfield.clone();
        aggregation_bitfield.set(committee_index, true);
        let mut aggregate_signature = existing_attestation.aggregate_signature.clone();
        aggregate_signature.add(&signature);

        Some(Attestation {
            aggregation_bitfield,
            aggregate_signature,
            ..existing_attestation.clone()
        })
    }
}
<commit_msg>Modify attestion_aggregation to use frok version in domain<commit_after>use crate::cached_beacon_state::CachedBeaconState;
use state_processing::validate_attestation_without_signature;
use std::collections::{HashMap, HashSet};
use types::{
    beacon_state::BeaconStateError, AggregateSignature, Attestation, AttestationData, BeaconState,
    Bitfield, ChainSpec, FreeAttestation, Signature,
};

const PHASE_0_CUSTODY_BIT: bool = false;

/// Provides the functionality to:
///
///  - Recieve a `FreeAttestation` and aggregate it into an `Attestation` (or create a new if it
///  doesn't exist).
///  - Store all aggregated or created `Attestation`s.
///  - Produce a list of attestations that would be valid for inclusion in some `BeaconState` (and
///  therefore valid for inclusion in a `BeaconBlock`.
///
///  Note: `Attestations` are stored in memory and never deleted. This is not scalable and must be
///  rectified in a future revision.
#[derive(Default)]
pub struct AttestationAggregator {
    store: HashMap<Vec<u8>, Attestation>,
}

pub struct Outcome {
    pub valid: bool,
    pub message: Message,
}

pub enum Message {
    /// The free attestation was added to an existing attestation.
    Aggregated,
    /// The free attestation has already been aggregated to an existing attestation.
    AggregationNotRequired,
    /// The free attestation was transformed into a new attestation.
    NewAttestationCreated,
    /// The supplied `validator_index` is not in the committee for the given `shard` and `slot`.
    BadValidatorIndex,
    /// The given `signature` did not match the `pubkey` in the given
    /// `state.validator_registry`.
    BadSignature,
    /// The given `slot` does not match the validators committee assignment.
    BadSlot,
    /// The given `shard` does not match the validators committee assignment.
    BadShard,
}

macro_rules! some_or_invalid {
    ($expression: expr, $error: expr) => {
        match $expression {
            Some(x) => x,
            None => {
                return Ok(Outcome {
                    valid: false,
                    message: $error,
                });
            }
        }
    };
}

impl AttestationAggregator {
    /// Instantiates a new AttestationAggregator with an empty database.
    pub fn new() -> Self {
        Self {
            store: HashMap::new(),
        }
    }

    /// Accepts some `FreeAttestation`, validates it and either aggregates it upon some existing
    /// `Attestation` or produces a new `Attestation`.
    ///
    /// The "validation" provided is not complete, instead the following points are checked:
    ///  - The given `validator_index` is in the committee for the given `shard` for the given
    ///  `slot`.
    ///  - The signature is verified against that of the validator at `validator_index`.
    pub fn process_free_attestation(
        &mut self,
        cached_state: &CachedBeaconState,
        free_attestation: &FreeAttestation,
        spec: &ChainSpec,
    ) -> Result<Outcome, BeaconStateError> {
        let (slot, shard, committee_index) = some_or_invalid!(
            cached_state.attestation_slot_and_shard_for_validator(
                free_attestation.validator_index as usize,
                spec,
            )?,
            Message::BadValidatorIndex
        );

        if free_attestation.data.slot != slot {
            return Ok(Outcome {
                valid: false,
                message: Message::BadSlot,
            });
        }
        if free_attestation.data.shard != shard {
            return Ok(Outcome {
                valid: false,
                message: Message::BadShard,
            });
        }

        let signable_message = free_attestation.data.signable_message(PHASE_0_CUSTODY_BIT);

        let validator_record = some_or_invalid!(
            cached_state
                .state
                .validator_registry
                .get(free_attestation.validator_index as usize),
            Message::BadValidatorIndex
        );

        if !free_attestation.signature.verify(
            &signable_message,
            cached_state.state.fork.get_domain(
                cached_state.state.current_epoch(spec),
                spec.domain_attestation,
            ),
            &validator_record.pubkey,
        ) {
            return Ok(Outcome {
                valid: false,
                message: Message::BadSignature,
            });
        }

        if let Some(existing_attestation) = self.store.get(&signable_message) {
            if let Some(updated_attestation) = aggregate_attestation(
                existing_attestation,
                &free_attestation.signature,
                committee_index as usize,
            ) {
                self.store.insert(signable_message, updated_attestation);
                Ok(Outcome {
                    valid: true,
                    message: Message::Aggregated,
                })
            } else {
                Ok(Outcome {
                    valid: true,
                    message: Message::AggregationNotRequired,
                })
            }
        } else {
            let mut aggregate_signature = AggregateSignature::new();
            aggregate_signature.add(&free_attestation.signature);
            let mut aggregation_bitfield = Bitfield::new();
            aggregation_bitfield.set(committee_index as usize, true);
            let new_attestation = Attestation {
                data: free_attestation.data.clone(),
                aggregation_bitfield,
                custody_bitfield: Bitfield::new(),
                aggregate_signature,
            };
            self.store.insert(signable_message, new_attestation);
            Ok(Outcome {
                valid: true,
                message: Message::NewAttestationCreated,
            })
        }
    }

    /// Returns all known attestations which are:
    ///
    /// - Valid for the given state
    /// - Not already in `state.latest_attestations`.
    pub fn get_attestations_for_state(
        &self,
        state: &BeaconState,
        spec: &ChainSpec,
    ) -> Vec<Attestation> {
        let mut known_attestation_data: HashSet<AttestationData> = HashSet::new();

        state.latest_attestations.iter().for_each(|attestation| {
            known_attestation_data.insert(attestation.data.clone());
        });

        self.store
            .values()
            .filter_map(|attestation| {
                if validate_attestation_without_signature(&state, attestation, spec).is_ok()
                    && !known_attestation_data.contains(&attestation.data)
                {
                    Some(attestation.clone())
                } else {
                    None
                }
            })
            .collect()
    }
}

/// Produces a new `Attestation` where:
///
/// - `signature` is added to `Attestation.aggregate_signature`
/// - Attestation.aggregation_bitfield[committee_index]` is set to true.
fn aggregate_attestation(
    existing_attestation: &Attestation,
    signature: &Signature,
    committee_index: usize,
) -> Option<Attestation> {
    let already_signed = existing_attestation
        .aggregation_bitfield
        .get(committee_index)
        .unwrap_or(false);

    if already_signed {
        None
    } else {
        let mut aggregation_bitfield = existing_attestation.aggregation_bitfield.clone();
        aggregation_bitfield.set(committee_index, true);
        let mut aggregate_signature = existing_attestation.aggregate_signature.clone();
        aggregate_signature.add(&signature);

        Some(Attestation {
            aggregation_bitfield,
            aggregate_signature,
            ..existing_attestation.clone()
        })
    }
}
<|endoftext|>


<commit_before>#![feature(type_ascription)]
#![feature(question_mark)]
#![feature(custom_derive)]
#![feature(plugin)]

#[macro_use]
extern crate rustless;
extern crate jsonway;
extern crate iron;
extern crate hyper;
extern crate valico;
extern crate env_logger;
extern crate clap;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate rand;

extern crate exonum;
extern crate blockchain_explorer;
extern crate digital_rights;

use std::net::SocketAddr;
use std::path::Path;
use std::thread;
use std::default::Default;

use clap::{Arg, App, SubCommand};
use rustless::json::ToJson;
use rustless::{Application, Api, Nesting, Versioning, Response, Client, ErrorResponse};
use rustless::batteries::cookie::{Cookie, CookieExt, CookieJar};
use rustless::batteries::swagger;
use valico::json_dsl;
use hyper::status::StatusCode;
use serde_json::value::from_value;

use exonum::node::{Node, Configuration, TxSender, NodeChannel};
use exonum::storage::{Database, MemoryDB, LevelDB, LevelDBOptions};
use exonum::storage::{Result as StorageResult, Error as StorageError};
use exonum::crypto::{gen_keypair, PublicKey, SecretKey, HexValue};
use exonum::messages::Message;
use exonum::config::ConfigFile;
use exonum::node::config::GenesisConfig;

use digital_rights::{DigitalRightsBlockchain, DigitalRightsTx, TxCreateOwner, TxCreateDistributor,
                     TxAddContent};
use digital_rights::api::{DigitalRightsApi, NewContent};

pub type Channel<B> = TxSender<B, NodeChannel<B>>;

fn save_user(storage: &mut CookieJar, role: &str, public_key: &PublicKey, secret_key: &SecretKey) {
    let p = storage.permanent();
    let e = p.encrypted();

    e.add(Cookie::new("public_key".to_string(), public_key.to_hex()));
    e.add(Cookie::new("secret_key".to_string(), secret_key.to_hex()));
    e.add(Cookie::new("role".to_string(), role.to_string()));
}

fn load_hex_value_from_cookie<'a>(storage: &'a CookieJar, key: &str) -> StorageResult<Vec<u8>> {
    if let Some(cookie) = storage.find(key) {
        println!("{}", cookie);
        if let Ok(value) = HexValue::from_hex(cookie.value) {
            return Ok(value);
        }
    }
    Err(StorageError::new(format!("Unable to find value with given key {}", key)))
}

fn load_user(storage: &CookieJar) -> StorageResult<(String, PublicKey, SecretKey)> {
    let p = storage.permanent();
    let e = p.encrypted();

    let public_key = PublicKey::from_slice(load_hex_value_from_cookie(&e, "public_key")?.as_ref());
    let secret_key = SecretKey::from_slice(load_hex_value_from_cookie(&e, "secret_key")?.as_ref());

    let public_key = public_key.ok_or(StorageError::new("Unable to read public key"))?;
    let secret_key = secret_key.ok_or(StorageError::new("Unable to read secret key"))?;
    let role = e.find("role").ok_or(StorageError::new("Unable to read role"))?.value;
    Ok((role, public_key, secret_key))
}

fn blockchain_explorer_api<D: Database>(api: &mut Api, b1: DigitalRightsBlockchain<D>) {
    blockchain_explorer::make_api::<DigitalRightsBlockchain<D>, DigitalRightsTx>(api, b1);
}

fn send_tx<'a, D: Database>(tx: DigitalRightsTx,
                            client: Client<'a>,
                            ch: Channel<DigitalRightsBlockchain<D>>)
                            -> Result<Client<'a>, ErrorResponse> {
    let tx_hash = tx.hash().to_hex();
    ch.send(tx);
    let json = &jsonway::object(|json| json.set("tx_hash", tx_hash)).unwrap();
    client.json(json)
}

fn digital_rights_api<D: Database>(api: &mut Api,
                                   blockchain: DigitalRightsBlockchain<D>,
                                   channel: Channel<DigitalRightsBlockchain<D>>) {
    api.namespace("drm", move |api| {
        let ch = channel.clone();
        api.post("owners", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("name", json_dsl::string());
            });

            endpoint.handle(move |client, params| {
                let name = params.find("name").unwrap().as_str().unwrap();

                let (public_key, secret_key) = gen_keypair();
                {
                    let mut cookies = client.request.cookies();
                    save_user(&mut cookies, "owner", &public_key, &secret_key);
                }
                let tx = TxCreateOwner::new(&public_key, &name, &secret_key);
                send_tx(DigitalRightsTx::CreateOwner(tx), client, ch.clone())
            })
        });

        let ch = channel.clone();
        api.post("distributors", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("name", json_dsl::string());
            });

            endpoint.handle(move |client, params| {
                let name = params.find("name").unwrap().as_str().unwrap();

                let (public_key, secret_key) = gen_keypair();
                {
                    let mut cookies = client.request.cookies();
                    save_user(&mut cookies, "distributor", &public_key, &secret_key);
                }
                let tx = TxCreateDistributor::new(&public_key, &name, &secret_key);
                send_tx(DigitalRightsTx::CreateDistributor(tx), client, ch.clone())
            })
        });

        let b = blockchain.clone();
        api.get("distributors/:id", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("id", json_dsl::u64());
            });

            endpoint.handle(move |client, params| {
                let id = params.find("id").unwrap().as_u64().unwrap();

                let drm = DigitalRightsApi::new(b.clone());
                match drm.distributor_info(id) {
                    Ok(Some(info)) => client.json(&info.to_json()),
                    _ => client.error(StorageError::new("Unable to get distributor")),
                }
            })
        });

        let b = blockchain.clone();
        api.get("owners/:id", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("id", json_dsl::u64());
            });

            endpoint.handle(move |client, params| {
                let id = params.find("id").unwrap().as_u64().unwrap();

                let drm = DigitalRightsApi::new(b.clone());
                match drm.owner_info(id) {
                    Ok(Some(info)) => client.json(&info.to_json()),
                    _ => client.error(StorageError::new("Unable to get owner")),
                }
            })
        });

        let ch = channel.clone();
        api.put("contents", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("title", json_dsl::string());
                params.req_typed("fingerprint", json_dsl::string());
                params.req_typed("additional_conditions", json_dsl::string());
                params.req_typed("price_per_listen", json_dsl::u64());
                params.req_typed("min_plays", json_dsl::u64());
                params.req_nested("owners", json_dsl::array(), |params| {
                    params.req_typed("owner_id", json_dsl::u64());
                    params.req_typed("share", json_dsl::u64());
                });
            });

            endpoint.handle(move |client, params| {
                let content_info = from_value::<NewContent>(params.clone()).unwrap();
                let (role, pub_key, sec_key) = {
                    let r = {
                        let cookies = client.request.cookies();
                        load_user(&cookies)
                    };
                    match r {
                        Ok((r, p, s)) => (r, p, s),
                        Err(e) => return client.error(e),
                    }
                };
                match role.as_ref() {
                    "owner" => {
                        let owners = content_info.owners
                            .iter()
                            .cloned()
                            .map(|info| info.into())
                            .collect::<Vec<u32>>();

                        let tx = TxAddContent::new(&pub_key,
                                                   &content_info.fingerprint.0,
                                                   &content_info.title,
                                                   content_info.price_per_listen as u32,
                                                   content_info.min_plays as u32,
                                                   &owners,
                                                   &content_info.additional_conditions,
                                                   &sec_key);
                        send_tx(DigitalRightsTx::AddContent(tx), client, ch.clone())
                    }
                    _ => client.error(StorageError::new("Unknown role")),
                }
            })
        });

    });
}

fn run_node<D: Database>(blockchain: DigitalRightsBlockchain<D>,
                         node_cfg: Configuration,
                         port: Option<u16>) {
    if let Some(port) = port {
        let mut node = Node::new(blockchain.clone(), node_cfg);
        let channel = node.channel();

        let api_thread = thread::spawn(move || {
            let channel = channel.clone();
            let blockchain = blockchain.clone();

            let api = Api::build(move |api| {
                // Specify API version
                api.version("v1", Versioning::Path);
                api.prefix("api");

                api.error_formatter(|err, _media| {
                    if let Some(e) = err.downcast::<StorageError>() {
                        let body = format!("An internal error occured: {}", e);
                        Some(Response::from(StatusCode::InternalServerError, Box::new(body)))
                    } else {
                        None
                    }
                });

                blockchain_explorer_api(api, blockchain.clone());
                digital_rights_api(api, blockchain.clone(), channel.clone());
                api.mount(swagger::create_api("docs"));
            });

            let listen_address: SocketAddr = format!("127.0.0.1:{}", port).parse().unwrap();
            println!("DigitalRights node server started on {}", listen_address);

            let mut app = Application::new(api);

            swagger::enable(&mut app,
                            swagger::Spec {
                                info: swagger::Info {
                                    title: "DigitalRights API".to_string(),
                                    description: Some("Simple API to demonstration".to_string()),
                                    contact: Some(swagger::Contact {
                                        name: "Aleksey Sidorov".to_string(),
                                        url: Some("aleksei.sidorov@xdev.re".to_string()),
                                        ..Default::default()
                                    }),
                                    license: Some(swagger::License {
                                        name: "Demo".to_string(),
                                        url: "http://exonum.com".to_string(),
                                    }),
                                    ..Default::default()
                                },
                                ..Default::default()
                            });

            let mut chain = iron::Chain::new(app);
            let api_key = b"abacabsasdainblabla23nx8Hasojd8";
            let cookie = ::rustless::batteries::cookie::new(api_key);
            chain.link(cookie);
            iron::Iron::new(chain).http(listen_address).unwrap();
        });

        node.run().unwrap();
        api_thread.join().unwrap();
    } else {
        Node::new(blockchain, node_cfg).run().unwrap();
    }
}

fn main() {
    env_logger::init().unwrap();

    let app = App::new("Digital rights manager api")
        .version(env!("CARGO_PKG_VERSION"))
        .author("Aleksey S. <aleksei.sidorov@xdev.re>")
        .about("Demo drm validator node")
        .arg(Arg::with_name("CONFIG")
            .short("c")
            .long("config")
            .value_name("CONFIG_PATH")
            .help("Sets a node config file")
            .required(true)
            .takes_value(true))
        .subcommand(SubCommand::with_name("generate")
            .about("Generates default configuration file")
            .version(env!("CARGO_PKG_VERSION"))
            .author("Aleksey S. <aleksei.sidorov@xdev.re>")
            .arg(Arg::with_name("COUNT")
                .help("Validators count")
                .required(true)
                .index(1)))
        .subcommand(SubCommand::with_name("run")
            .about("Run demo node with the given validator id")
            .version(env!("CARGO_PKG_VERSION"))
            .author("Aleksey S. <aleksei.sidorov@xdev.re>")
            .arg(Arg::with_name("LEVELDB_PATH")
                .short("d")
                .long("leveldb-path")
                .value_name("LEVELDB_PATH")
                .help("Use leveldb database with the given path")
                .takes_value(true))
            .arg(Arg::with_name("HTTP_PORT")
                .short("p")
                .long("port")
                .value_name("HTTP_PORT")
                .help("Run http server on given port")
                .takes_value(true))
            .arg(Arg::with_name("PEERS")
                .long("known-peers")
                .value_name("PEERS")
                .help("Comma separated list of known validator ids")
                .takes_value(true))
            .arg(Arg::with_name("VALIDATOR")
                .help("Sets a validator id")
                .required(true)
                .index(1)));

    let matches = app.get_matches();
    let path = Path::new(matches.value_of("CONFIG").unwrap());
    match matches.subcommand() {
        ("generate", Some(matches)) => {
            let count: u8 = matches.value_of("COUNT").unwrap().parse().unwrap();
            let cfg = GenesisConfig::gen(count);
            ConfigFile::save(&cfg, &path).unwrap();
            println!("The configuration was successfully written to file {:?}",
                     path);
        }
        ("run", Some(matches)) => {
            let cfg: GenesisConfig = ConfigFile::load(path).unwrap();
            let idx: usize = matches.value_of("VALIDATOR").unwrap().parse().unwrap();
            let port: Option<u16> = matches.value_of("HTTP_PORT").map(|x| x.parse().unwrap());
            let peers = match matches.value_of("PEERS") {
                Some(string) => {
                    string.split(" ")
                        .map(|x| -> usize { x.parse().unwrap() })
                        .map(|x| cfg.validators[x].address)
                        .collect()
                }
                None => {
                    cfg.validators
                        .iter()
                        .map(|v| v.address)
                        .collect()
                }
            };
            let node_cfg = cfg.to_node_configuration(idx, peers);
            match matches.value_of("LEVELDB_PATH") {
                Some(ref db_path) => {
                    println!("Using levedb storage with path: {}", db_path);
                    let mut options = LevelDBOptions::new();
                    options.create_if_missing = true;
                    let leveldb = LevelDB::new(&Path::new(db_path), options).unwrap();

                    let blockchain = DigitalRightsBlockchain { db: leveldb };
                    run_node(blockchain, node_cfg, port);
                }
                None => {
                    println!("Using memorydb storage");

                    let blockchain = DigitalRightsBlockchain { db: MemoryDB::new() };
                    run_node(blockchain, node_cfg, port);
                }
            };
        }
        _ => {
            unreachable!("Wrong subcommand");
        }
    }
}
<commit_msg>Add contracts/:fingerprint endpoint<commit_after>#![feature(type_ascription)]
#![feature(question_mark)]
#![feature(custom_derive)]
#![feature(plugin)]

#[macro_use]
extern crate rustless;
extern crate jsonway;
extern crate iron;
extern crate hyper;
extern crate valico;
extern crate env_logger;
extern crate clap;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate rand;

extern crate exonum;
extern crate blockchain_explorer;
extern crate digital_rights;

use std::net::SocketAddr;
use std::path::Path;
use std::thread;
use std::default::Default;

use clap::{Arg, App, SubCommand};
use rustless::json::ToJson;
use rustless::{Application, Api, Nesting, Versioning, Response, Client, ErrorResponse};
use rustless::batteries::cookie::{Cookie, CookieExt, CookieJar};
use rustless::batteries::swagger;
use valico::json_dsl;
use hyper::status::StatusCode;
use serde_json::value::from_value;

use exonum::node::{Node, Configuration, TxSender, NodeChannel};
use exonum::storage::{Database, MemoryDB, LevelDB, LevelDBOptions};
use exonum::storage::{Result as StorageResult, Error as StorageError};
use exonum::crypto::{gen_keypair, PublicKey, SecretKey, HexValue, Hash};
use exonum::messages::Message;
use exonum::config::ConfigFile;
use exonum::node::config::GenesisConfig;
use blockchain_explorer::HexField;

use digital_rights::{DigitalRightsBlockchain, DigitalRightsTx, TxCreateOwner, TxCreateDistributor,
                     TxAddContent, TxAddContract};
use digital_rights::api::{DigitalRightsApi, NewContent};

pub type Channel<B> = TxSender<B, NodeChannel<B>>;

fn save_user(storage: &mut CookieJar, role: &str, public_key: &PublicKey, secret_key: &SecretKey) {
    let p = storage.permanent();
    let e = p.encrypted();

    e.add(Cookie::new("public_key".to_string(), public_key.to_hex()));
    e.add(Cookie::new("secret_key".to_string(), secret_key.to_hex()));
    e.add(Cookie::new("role".to_string(), role.to_string()));
}

fn load_hex_value_from_cookie<'a>(storage: &'a CookieJar, key: &str) -> StorageResult<Vec<u8>> {
    if let Some(cookie) = storage.find(key) {
        println!("{}", cookie);
        if let Ok(value) = HexValue::from_hex(cookie.value) {
            return Ok(value);
        }
    }
    Err(StorageError::new(format!("Unable to find value with given key {}", key)))
}

fn load_user(storage: &CookieJar) -> StorageResult<(String, PublicKey, SecretKey)> {
    let p = storage.permanent();
    let e = p.encrypted();

    let public_key = PublicKey::from_slice(load_hex_value_from_cookie(&e, "public_key")?.as_ref());
    let secret_key = SecretKey::from_slice(load_hex_value_from_cookie(&e, "secret_key")?.as_ref());

    let public_key = public_key.ok_or(StorageError::new("Unable to read public key"))?;
    let secret_key = secret_key.ok_or(StorageError::new("Unable to read secret key"))?;
    let role = e.find("role").ok_or(StorageError::new("Unable to read role"))?.value;
    Ok((role, public_key, secret_key))
}

fn blockchain_explorer_api<D: Database>(api: &mut Api, b1: DigitalRightsBlockchain<D>) {
    blockchain_explorer::make_api::<DigitalRightsBlockchain<D>, DigitalRightsTx>(api, b1);
}

fn send_tx<'a, D: Database>(tx: DigitalRightsTx,
                            client: Client<'a>,
                            ch: Channel<DigitalRightsBlockchain<D>>)
                            -> Result<Client<'a>, ErrorResponse> {
    let tx_hash = tx.hash().to_hex();
    ch.send(tx);
    let json = &jsonway::object(|json| json.set("tx_hash", tx_hash)).unwrap();
    client.json(json)
}

fn digital_rights_api<D: Database>(api: &mut Api,
                                   blockchain: DigitalRightsBlockchain<D>,
                                   channel: Channel<DigitalRightsBlockchain<D>>) {
    api.namespace("drm", move |api| {
        let ch = channel.clone();
        api.post("owners", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("name", json_dsl::string());
            });

            endpoint.handle(move |client, params| {
                let name = params.find("name").unwrap().as_str().unwrap();

                let (public_key, secret_key) = gen_keypair();
                {
                    let mut cookies = client.request.cookies();
                    save_user(&mut cookies, "owner", &public_key, &secret_key);
                }
                let tx = TxCreateOwner::new(&public_key, &name, &secret_key);
                send_tx(DigitalRightsTx::CreateOwner(tx), client, ch.clone())
            })
        });

        let ch = channel.clone();
        api.post("distributors", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("name", json_dsl::string());
            });

            endpoint.handle(move |client, params| {
                let name = params.find("name").unwrap().as_str().unwrap();

                let (public_key, secret_key) = gen_keypair();
                {
                    let mut cookies = client.request.cookies();
                    save_user(&mut cookies, "distributor", &public_key, &secret_key);
                }
                let tx = TxCreateDistributor::new(&public_key, &name, &secret_key);
                send_tx(DigitalRightsTx::CreateDistributor(tx), client, ch.clone())
            })
        });

        let b = blockchain.clone();
        api.get("distributors/:id", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("id", json_dsl::u64());
            });

            endpoint.handle(move |client, params| {
                let id = params.find("id").unwrap().as_u64().unwrap();

                let drm = DigitalRightsApi::new(b.clone());
                match drm.distributor_info(id) {
                    Ok(Some(info)) => client.json(&info.to_json()),
                    _ => client.error(StorageError::new("Unable to get distributor")),
                }
            })
        });

        let b = blockchain.clone();
        api.get("owners/:id", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("id", json_dsl::u64());
            });

            endpoint.handle(move |client, params| {
                let id = params.find("id").unwrap().as_u64().unwrap();

                let drm = DigitalRightsApi::new(b.clone());
                match drm.owner_info(id) {
                    Ok(Some(info)) => client.json(&info.to_json()),
                    _ => client.error(StorageError::new("Unable to get owner")),
                }
            })
        });

        let ch = channel.clone();
        api.put("contents", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("title", json_dsl::string());
                params.req_typed("fingerprint", json_dsl::string());
                params.req_typed("additional_conditions", json_dsl::string());
                params.req_typed("price_per_listen", json_dsl::u64());
                params.req_typed("min_plays", json_dsl::u64());
                params.req_nested("owners", json_dsl::array(), |params| {
                    params.req_typed("owner_id", json_dsl::u64());
                    params.req_typed("share", json_dsl::u64());
                });
            });

            endpoint.handle(move |client, params| {
                let content_info = from_value::<NewContent>(params.clone()).unwrap();
                let (role, pub_key, sec_key) = {
                    let r = {
                        let cookies = client.request.cookies();
                        load_user(&cookies)
                    };
                    match r {
                        Ok((r, p, s)) => (r, p, s),
                        Err(e) => return client.error(e),
                    }
                };
                match role.as_ref() {
                    "owner" => {
                        let owners = content_info.owners
                            .iter()
                            .cloned()
                            .map(|info| info.into())
                            .collect::<Vec<u32>>();

                        let tx = TxAddContent::new(&pub_key,
                                                   &content_info.fingerprint.0,
                                                   &content_info.title,
                                                   content_info.price_per_listen as u32,
                                                   content_info.min_plays as u32,
                                                   &owners,
                                                   &content_info.additional_conditions,
                                                   &sec_key);
                        send_tx(DigitalRightsTx::AddContent(tx), client, ch.clone())
                    }
                    _ => client.error(StorageError::new("Unknown role")),
                }
            })
        });

        let ch = channel.clone();
        api.put("contracts/:fingerprint", move |endpoint| {
            endpoint.params(|params| {
                params.req_typed("fingerprint", json_dsl::string());
            });

            endpoint.handle(move |client, params| {
                let fingerprint = {
                    let r = Hash::from_hex(params.find("fingerprint").unwrap().as_str().unwrap());
                    match r {
                        Ok(f) => f,
                        Err(e) => return client.error(e)
                    }
                };
                let (role, pub_key, sec_key) = {
                    let r = {
                        let cookies = client.request.cookies();
                        load_user(&cookies)
                    };
                    match r {
                        Ok((r, p, s)) => (r, p, s),
                        Err(e) => return client.error(e),
                    }
                };
                match role.as_ref() {
                    "distributor" => {
                        let id = 0; //TODO add table
                        let tx = TxAddContract::new(&pub_key, id, &fingerprint, &sec_key);
                        send_tx(DigitalRightsTx::AddContract(tx), client, ch.clone())
                    }
                    _ => client.error(StorageError::new("Unknown role")),
                }
            })
        });
    });
}

fn run_node<D: Database>(blockchain: DigitalRightsBlockchain<D>,
                         node_cfg: Configuration,
                         port: Option<u16>) {
    if let Some(port) = port {
        let mut node = Node::new(blockchain.clone(), node_cfg);
        let channel = node.channel();

        let api_thread = thread::spawn(move || {
            let channel = channel.clone();
            let blockchain = blockchain.clone();

            let api = Api::build(move |api| {
                // Specify API version
                api.version("v1", Versioning::Path);
                api.prefix("api");

                api.error_formatter(|err, _media| {
                    if let Some(e) = err.downcast::<StorageError>() {
                        let body = format!("An internal error occured: {}", e);
                        Some(Response::from(StatusCode::InternalServerError, Box::new(body)))
                    } else {
                        None
                    }
                });

                blockchain_explorer_api(api, blockchain.clone());
                digital_rights_api(api, blockchain.clone(), channel.clone());
                api.mount(swagger::create_api("docs"));
            });

            let listen_address: SocketAddr = format!("127.0.0.1:{}", port).parse().unwrap();
            println!("DigitalRights node server started on {}", listen_address);

            let mut app = Application::new(api);

            swagger::enable(&mut app,
                            swagger::Spec {
                                info: swagger::Info {
                                    title: "DigitalRights API".to_string(),
                                    description: Some("Simple API to demonstration".to_string()),
                                    contact: Some(swagger::Contact {
                                        name: "Aleksey Sidorov".to_string(),
                                        url: Some("aleksei.sidorov@xdev.re".to_string()),
                                        ..Default::default()
                                    }),
                                    license: Some(swagger::License {
                                        name: "Demo".to_string(),
                                        url: "http://exonum.com".to_string(),
                                    }),
                                    ..Default::default()
                                },
                                ..Default::default()
                            });

            let mut chain = iron::Chain::new(app);
            let api_key = b"abacabsasdainblabla23nx8Hasojd8";
            let cookie = ::rustless::batteries::cookie::new(api_key);
            chain.link(cookie);
            iron::Iron::new(chain).http(listen_address).unwrap();
        });

        node.run().unwrap();
        api_thread.join().unwrap();
    } else {
        Node::new(blockchain, node_cfg).run().unwrap();
    }
}

fn main() {
    env_logger::init().unwrap();

    let app = App::new("Digital rights manager api")
        .version(env!("CARGO_PKG_VERSION"))
        .author("Aleksey S. <aleksei.sidorov@xdev.re>")
        .about("Demo drm validator node")
        .arg(Arg::with_name("CONFIG")
            .short("c")
            .long("config")
            .value_name("CONFIG_PATH")
            .help("Sets a node config file")
            .required(true)
            .takes_value(true))
        .subcommand(SubCommand::with_name("generate")
            .about("Generates default configuration file")
            .version(env!("CARGO_PKG_VERSION"))
            .author("Aleksey S. <aleksei.sidorov@xdev.re>")
            .arg(Arg::with_name("COUNT")
                .help("Validators count")
                .required(true)
                .index(1)))
        .subcommand(SubCommand::with_name("run")
            .about("Run demo node with the given validator id")
            .version(env!("CARGO_PKG_VERSION"))
            .author("Aleksey S. <aleksei.sidorov@xdev.re>")
            .arg(Arg::with_name("LEVELDB_PATH")
                .short("d")
                .long("leveldb-path")
                .value_name("LEVELDB_PATH")
                .help("Use leveldb database with the given path")
                .takes_value(true))
            .arg(Arg::with_name("HTTP_PORT")
                .short("p")
                .long("port")
                .value_name("HTTP_PORT")
                .help("Run http server on given port")
                .takes_value(true))
            .arg(Arg::with_name("PEERS")
                .long("known-peers")
                .value_name("PEERS")
                .help("Comma separated list of known validator ids")
                .takes_value(true))
            .arg(Arg::with_name("VALIDATOR")
                .help("Sets a validator id")
                .required(true)
                .index(1)));

    let matches = app.get_matches();
    let path = Path::new(matches.value_of("CONFIG").unwrap());
    match matches.subcommand() {
        ("generate", Some(matches)) => {
            let count: u8 = matches.value_of("COUNT").unwrap().parse().unwrap();
            let cfg = GenesisConfig::gen(count);
            ConfigFile::save(&cfg, &path).unwrap();
            println!("The configuration was successfully written to file {:?}",
                     path);
        }
        ("run", Some(matches)) => {
            let cfg: GenesisConfig = ConfigFile::load(path).unwrap();
            let idx: usize = matches.value_of("VALIDATOR").unwrap().parse().unwrap();
            let port: Option<u16> = matches.value_of("HTTP_PORT").map(|x| x.parse().unwrap());
            let peers = match matches.value_of("PEERS") {
                Some(string) => {
                    string.split(" ")
                        .map(|x| -> usize { x.parse().unwrap() })
                        .map(|x| cfg.validators[x].address)
                        .collect()
                }
                None => {
                    cfg.validators
                        .iter()
                        .map(|v| v.address)
                        .collect()
                }
            };
            let node_cfg = cfg.to_node_configuration(idx, peers);
            match matches.value_of("LEVELDB_PATH") {
                Some(ref db_path) => {
                    println!("Using levedb storage with path: {}", db_path);
                    let mut options = LevelDBOptions::new();
                    options.create_if_missing = true;
                    let leveldb = LevelDB::new(&Path::new(db_path), options).unwrap();

                    let blockchain = DigitalRightsBlockchain { db: leveldb };
                    run_node(blockchain, node_cfg, port);
                }
                None => {
                    println!("Using memorydb storage");

                    let blockchain = DigitalRightsBlockchain { db: MemoryDB::new() };
                    run_node(blockchain, node_cfg, port);
                }
            };
        }
        _ => {
            unreachable!("Wrong subcommand");
        }
    }
}
<|endoftext|>


<commit_before>extern crate csv;
extern crate rustc_serialize;

mod distance;

use std::fs::File;
use csv::Reader;
use distance::*;

#[derive(RustcDecodable)]
struct User {
    id: u32,
    location: String,
    age: u32,
}

#[derive(RustcDecodable)]
struct Book {
    isbn: String,
    title: String,
    author: String,
    pub_year: u32,
    publisher: String,
}

#[derive(RustcDecodable)]
struct Rating {
    user_id: u32,
    book_isbn: String,
    rating: u32,
}

fn init_db(path: &str) -> Reader<File> {
    let mut rdr = csv::Reader::from_file(path).unwrap();
    rdr
}

// fn get_vector(db: &Value, name: &str) -> Vec<f32> {
//     let mut vec: Vec<f32> = vec![];
//     for elem in db["scores"][name].as_array().unwrap().iter() {
//         vec.push(elem.as_f64().unwrap() as f32);
//     }

//     vec
// }

// fn evaluate_movie_ratings() {
//     let mut rdr = init_db("./data/Movie_Ratings.csv");
// }

fn evaluate_book_ratings() {
    // let mut rdr = init_db("./data/BX-Dump/BX-Book-Ratings.csv");
    let mut rdr = init_db("./data/BX-Dump/BX-Users.csv").has_headers(false);

    let mut i = 0;
    for record in rdr.decode() {
        if i > 10 {
            break;
        }
        let (s1, s2, dist): (u32, String, u32) = record.unwrap();
        println!("{}: {}: [{}]", s1, s2, dist);
        i += 1;
        // let user: User = record.unwrap();
        // println!("{}: {}, {}", user.id, user.location, user.age);
    }
}

fn main() {
    evaluate_book_ratings();
    println!("Hello, world!");
}
<commit_msg>working with users<commit_after>extern crate csv;
extern crate rustc_serialize;

mod distance;

use std::fs::File;
use csv::Reader;
use distance::*;

#[derive(RustcDecodable)]
struct User {
    id: u32,
    location: String,
    age: u32,
}

#[derive(RustcDecodable)]
struct Book {
    isbn: String,
    title: String,
    author: String,
    pub_year: u32,
    publisher: String,
}

#[derive(RustcDecodable)]
struct Rating {
    user_id: u32,
    book_isbn: String,
    rating: u32,
}

fn init_db(path: &str) -> Reader<File> {
    let mut rdr = csv::Reader::from_file(path).unwrap();
    rdr
}

// fn get_vector(db: &Value, name: &str) -> Vec<f32> {
//     let mut vec: Vec<f32> = vec![];
//     for elem in db["scores"][name].as_array().unwrap().iter() {
//         vec.push(elem.as_f64().unwrap() as f32);
//     }

//     vec
// }

// fn evaluate_movie_ratings() {
//     let mut rdr = init_db("./data/Movie_Ratings.csv");
// }

fn evaluate_book_ratings() {
    // let mut rdr = init_db("./data/BX-Dump/BX-Book-Ratings.csv");
    let mut rdr = init_db("./data/BX-Dump/BX-Users.csv").has_headers(false);

    let mut i = 0;
    for record in rdr.decode() {
        if i > 10 {
            break;
        }
        let user: User = record.unwrap();
        println!("{}: {} [{}]", user.id, user.location, user.age);
        // let (s1, s2, dist): (u32, String, u32) = record.unwrap();
        // println!("{}: {}: [{}]", s1, s2, dist);
        i += 1;
    }
}

fn main() {
    evaluate_book_ratings();
    println!("Hello, world!");
}
<|endoftext|>


<commit_before>use std::collections::hash_map::DefaultHasher;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::hash::{Hash, Hasher};
use std::iter::repeat;

use failure::Error;
use ggez::{graphics, Context, GameResult};
use warmy::{LogicalKey, Store};

use crate::game::Game;
use crate::objects::TextureAtlas;
use crate::palette::PaletteSwaps;
use crate::piv::{extract_palette, Colour, PivImage};
use crate::text::Screen;

#[derive(Debug)]
pub struct Menu {
    pub screen: Screen,
    background: Option<graphics::Image>,
    pub palette: Vec<Colour>,
    pub palette_hash: u64,
}

fn calculate_hash<T: Hash>(t: &T) -> u64 {
    let mut s = DefaultHasher::new();
    t.hash(&mut s);
    s.finish()
}

impl Menu {
    pub fn new(ctx: &mut Context, store: &mut Store<Context>, key: &str) -> Result<Self, Error> {
        let screen = store
            .get::<_, Screen>(&warmy::LogicalKey::new(key), ctx)?
            .borrow()
            .clone();

        let (background, palette) = match &screen.background {
            Some(background) => {
                let piv = store.get::<_, PivImage>(&LogicalKey::new(background), ctx)?;
                let mut palette = piv.borrow().palette.to_vec();
                if palette.len() == 16 {
                    palette.extend(
                        repeat(Colour {
                            r: 0,
                            g: 0,
                            b: 0,
                            a: 0,
                        })
                        .take(16),
                    );
                }
                let image = graphics::Image::from_rgba8(ctx, 320, 200, &*piv.borrow().to_rgba8())?;
                (Some(image), palette)
            }
            None => {
                let swaps_res = store
                    .get::<_, PaletteSwaps>(&LogicalKey::new("/palettes.yaml"), ctx)
                    .expect("error loading palette.yaml");
                let swaps = swaps_res.borrow();
                let raw_palette = swaps
                    .0
                    .get("default")
                    .expect("failed to fetch default palette");
                let palette = extract_palette(raw_palette);
                (None, palette)
            }
        };

        let palette_hash = calculate_hash(&palette);
        Ok(Self {
            background,
            palette,
            screen,
            palette_hash,
        })
    }

    pub fn draw(&mut self, game: &mut Game, ctx: &mut Context) -> GameResult<()> {
        if let Some(background) = &self.background {
            graphics::draw_ex(
                ctx,
                background,
                graphics::DrawParam {
                    dest: graphics::Point2::new(0.0, 0.0),
                    scale: graphics::Point2::new(3.0, 3.0),
                    ..Default::default()
                },
            )?;
        }

        for text in &self.screen.text {
            let palette_name = self.screen.background.as_ref();
            let mut batch: graphics::spritebatch::SpriteBatch = text
                .as_sprite_batch(ctx, game, &self.palette, self.palette_hash)
                .expect("error drawing text to screen");
            graphics::draw_ex(
                ctx,
                &batch,
                graphics::DrawParam {
                    dest: graphics::Point2::new(0.0, 0.0),
                    scale: graphics::Point2::new(3.0, 3.0),
                    ..Default::default()
                },
            )?;
            batch.clear();
        }

        for image in &self.screen.images {
            let atlas = game
                .store
                .get::<_, TextureAtlas>(&LogicalKey::new(image.sheet.as_str()), ctx)
                .unwrap();

            let atlas_dimension = atlas.borrow().image.width as u32;
            let image_name = format!("{}{}", image.sheet, self.palette_hash);
            let ggez_image = match game.images.entry(image_name) {
                Occupied(i) => i.into_mut(),
                Vacant(i) => i.insert(graphics::Image::from_rgba8(
                    ctx,
                    atlas_dimension as u16,
                    atlas_dimension as u16,
                    &atlas.borrow().image.to_rgba8(&self.palette),
                )?),
            };

            let rect = atlas.borrow().rects[image.image];
            let texture_size = atlas.borrow().image.width as f32;
            let draw_params = graphics::DrawParam {
                src: graphics::Rect {
                    x: rect.x as f32 / texture_size,
                    y: rect.y as f32 / texture_size,
                    w: rect.w as f32 / texture_size,
                    h: rect.h as f32 / texture_size,
                },
                dest: graphics::Point2::new(image.x as f32 * 3.0, image.y as f32 * 3.0),
                scale: graphics::Point2::new(3.0, 3.0),
                ..Default::default()
            };
            graphics::draw_ex(ctx, ggez_image, draw_params)?;
        }
        Ok(())
    }
}
<commit_msg>ggez-v0.5: update menu.rs drawing.<commit_after>use std::collections::hash_map::DefaultHasher;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::hash::{Hash, Hasher};
use std::iter::repeat;

use failure::Error;
use ggez::nalgebra::{Point2, Vector2};
use ggez::{graphics, Context, GameResult};
use warmy::{LogicalKey, Store};

use crate::game::Game;
use crate::objects::TextureAtlas;
use crate::palette::PaletteSwaps;
use crate::piv::{extract_palette, Colour, PivImage};
use crate::text::Screen;

#[derive(Debug)]
pub struct Menu {
    pub screen: Screen,
    background: Option<graphics::Image>,
    pub palette: Vec<Colour>,
    pub palette_hash: u64,
}

fn calculate_hash<T: Hash>(t: &T) -> u64 {
    let mut s = DefaultHasher::new();
    t.hash(&mut s);
    s.finish()
}

impl Menu {
    pub fn new(ctx: &mut Context, store: &mut Store<Context>, key: &str) -> Result<Self, Error> {
        let screen = store
            .get::<_, Screen>(&warmy::LogicalKey::new(key), ctx)?
            .borrow()
            .clone();

        let (background, palette) = match &screen.background {
            Some(background) => {
                let piv = store.get::<_, PivImage>(&LogicalKey::new(background), ctx)?;
                let mut palette = piv.borrow().palette.to_vec();
                if palette.len() == 16 {
                    palette.extend(
                        repeat(Colour {
                            r: 0,
                            g: 0,
                            b: 0,
                            a: 0,
                        })
                        .take(16),
                    );
                }
                let image = graphics::Image::from_rgba8(ctx, 320, 200, &*piv.borrow().to_rgba8())?;
                (Some(image), palette)
            }
            None => {
                let swaps_res = store
                    .get::<_, PaletteSwaps>(&LogicalKey::new("/palettes.yaml"), ctx)
                    .expect("error loading palette.yaml");
                let swaps = swaps_res.borrow();
                let raw_palette = swaps
                    .0
                    .get("default")
                    .expect("failed to fetch default palette");
                let palette = extract_palette(raw_palette);
                (None, palette)
            }
        };

        let palette_hash = calculate_hash(&palette);
        Ok(Self {
            background,
            palette,
            screen,
            palette_hash,
        })
    }

    pub fn draw(&mut self, game: &mut Game, ctx: &mut Context) -> GameResult<()> {
        let draw_params = graphics::DrawParam::default().scale(Vector2::new(3.0, 3.0));
        if let Some(background) = &self.background {
            graphics::draw(ctx, background, draw_params)?;
        }

        for text in &self.screen.text {
            let palette_name = self.screen.background.as_ref();
            let mut batch: graphics::spritebatch::SpriteBatch = text
                .as_sprite_batch(ctx, game, &self.palette, self.palette_hash)
                .expect("error drawing text to screen");
            graphics::draw(ctx, &batch, draw_params)?;
            batch.clear();
        }

        for image in &self.screen.images {
            let atlas = game
                .store
                .get::<_, TextureAtlas>(&LogicalKey::new(image.sheet.as_str()), ctx)
                .unwrap();

            let atlas_dimension = atlas.borrow().image.width as u32;
            let image_name = format!("{}{}", image.sheet, self.palette_hash);
            let ggez_image = match game.images.entry(image_name) {
                Occupied(i) => i.into_mut(),
                Vacant(i) => i.insert(graphics::Image::from_rgba8(
                    ctx,
                    atlas_dimension as u16,
                    atlas_dimension as u16,
                    &atlas.borrow().image.to_rgba8(&self.palette),
                )?),
            };

            let rect = atlas.borrow().rects[image.image];
            let texture_size = atlas.borrow().image.width as f32;
            let draw_params = graphics::DrawParam::default()
                .src(graphics::Rect {
                    x: rect.x as f32 / texture_size,
                    y: rect.y as f32 / texture_size,
                    w: rect.w as f32 / texture_size,
                    h: rect.h as f32 / texture_size,
                })
                .dest(Point2::new(image.x as f32 * 3.0, image.y as f32 * 3.0))
                .scale(Vector2::new(3.0, 3.0));
            graphics::draw(ctx, ggez_image, draw_params)?;
        }
        Ok(())
    }
}
<|endoftext|>