<commit_before>/*
 * Tool statically unpacking HotS binaries.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 athre0z
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

extern crate crypto;
mod pe;

use std::fs::File;
use std::path::Path;
use std::io::prelude::*;
use std::error::Error;
use std::mem;
use std::slice;
use std::env;

use crypto::{buffer, aes, blockmodes};

#[allow(unused_imports)]
use crypto::symmetriccipher::Encryptor;

/// Entry-point.
fn main() {
    println!("HotS-unpack by athre0z (c) 2015 zyantific.com\n");

    // Parse arguments.
    let args: Vec<_> = env::args().collect();
    if args.len() < 2 || args.len() > 3 {
        println!("[*] Usage: {} <input binary> [output binary]", args[0]);
        return;
    }

    // Read file.
    let mut f = match File::open(&Path::new(&args[1])) {
        Err(why) => panic!("[-] Couldn't open input file: {}", Error::description(&why)),
        Ok(file) => file,
    };

    let out: String = match args.len() {
        3 => args[2].to_string(),
	_ => "out.exe".to_string()
    };

    let mut data = Vec::new();
    let data_size = match f.read_to_end(&mut data) {
        Err(why) => panic!("[-] Couldn't read input file: {}", Error::description(&why)),
        Ok(x) => x
    };

    println!("[+] Read file: {} bytes", data_size);

    // Parse DOS-header.
    let mz = get_data::<pe::ImageDosHeader>(&mut data, 0);
    if unsafe { (*mz).magic } != pe::DOS_MAGIC {
        panic!("[-] Invalid input file: invalid DOS header");
    }

    // Parse NT-header.
    let nt_header_offs = unsafe { (*mz).lfanew } as usize;
    let nt = get_data::<pe::ImageNtHeaders32>(&mut data, nt_header_offs);
    if unsafe { (*nt).signature } != pe::PE_MAGIC {
        panic!("[-] Invalid input file: invalid NT header");
    }

    // Obtain useful information.
    let sec_table_offs = nt_header_offs + mem::size_of::<pe::ImageNtHeaders32>();
    let num_secs = unsafe { (*nt).file_header.number_of_sections } as usize;
    let data_offs = sec_table_offs
        + mem::size_of::<pe::ImageSectionHeader>() * num_secs;
    let image_base = unsafe { (*nt).opt_header.image_base } as usize;

    // Scan for chunk table.
    println!("[*] Heuristically locating chunk table ...");
    let mut chunk_table_offs = 0;
    let mut chunk_table_len = 0;

    'scan: for i in data_offs..data.len() {
        let mut offs = 0usize;
        let mut last_addr = 0u32;

        // Check if candidate looks like a chunk table.
        while i + offs + mem::size_of::<ChunkEntry>() <= data.len() {
            unsafe {
                let cur = get_data::<ChunkEntry>(&mut data, i + offs);

                // The chunk table's end is indicated with an 0xFFFFFFFF.
                if offs >= 1000 * mem::size_of::<ChunkEntry>() && (*cur).offset == 0xFFFFFFFF {
                    chunk_table_offs = i;
                    chunk_table_len = (offs / mem::size_of::<ChunkEntry>()) - 1;
                    break 'scan;
                }

                // The chunk table entries are stored ascending.
                if (*cur).offset <= last_addr {
                    break;
                }

                last_addr = (*cur).offset;
                offs += mem::size_of::<ChunkEntry>();
            }
        }
    }

    if chunk_table_offs == 0 || chunk_table_len == 0 {
        panic!("[-] Unable to locate chunk table.");
    }

    println!("[+] Found chunk table @ FO 0x{:x} ({} entries).",
        chunk_table_offs, chunk_table_len);

    // Scan for AES key.
    println!("[*] Locating AES key ...");

    // 0xFF -> wildcard
    let pattern = [
        0x68, 0x80, 0x00, 0x00, 0x00, // push 80h
        0x68, 0xFF, 0xFF, 0xFF, 0xFF, // push offset g_aesInputKey
        0x68, 0xFF, 0xFF, 0xFF, 0xFF, // push offset g_aesKey
        0xE8, 0xFF, 0xFF, 0xFF, 0xFF, // call AesSetDecryptKey
        0xE9,                         // jmp  XXX
    ];

    let mut match_ = None;
    for offs in sec_table_offs..(data.len() - pattern.len()) {
        let mut exhausted = true;
        for (i, cur_pat_byte) in pattern.iter().enumerate() {
            if *cur_pat_byte == 0xFF { continue; }
            if data[offs + i] != *cur_pat_byte {
                exhausted = false;
                break;
            }
        }

        if exhausted {
            match_ = Some(offs);
            break;
        }
    }

    let aes_key_rva = match match_ {
        Some(x) => unsafe { *get_data::<u32>(&mut data, x + 6) as usize - image_base },
        None => panic!("[-] Unable to locate AES key.")
    };

    print!("[+] Found AES key @ RVA 0x{:x}: ", aes_key_rva);

    // Dump AES key.
    let aes_key = unsafe { *get_data_rva::<[u8; 16]>(
        &mut data, sec_table_offs, num_secs, aes_key_rva) };

    for i in aes_key.iter() {
        print!("{:02x} ", i);
    }
    print!("\n");

    // Collect all encrypted chunks into one buffer (we got plenty of memory, don't we? :p)
    println!("[*] Merging encrypted chunks ...", );
    let mut encrypted_blob = Vec::<u8>::new();
    for i in 0..chunk_table_len {
        let chunk_info = get_data::<ChunkEntry>(
            &mut data, chunk_table_offs + mem::size_of::<ChunkEntry>() * i);
        unsafe {
            encrypted_blob.extend(get_slice_rva::<u8>(
                &mut data, sec_table_offs, num_secs, (*chunk_info).offset as usize,
                (*chunk_info).size as usize).to_vec());
        }
    }

    let padding_length = 16 - encrypted_blob.len() % 16;
    if padding_length > 0 {
        encrypted_blob.extend(vec![0; padding_length]);
    }

    // Decrypt blob.
    println!("[*] Decrypting ...");
    let mut decryptor = aes::ecb_decryptor(
        aes::KeySize::KeySize128,
        &aes_key,
        blockmodes::NoPadding);
    let mut decrypted_blob = vec![0u8; encrypted_blob.len()];

    {
        let mut read_buf = buffer::RefReadBuffer::new(&encrypted_blob);
        let mut write_buf = buffer::RefWriteBuffer::new(&mut decrypted_blob);
        match decryptor.decrypt(&mut read_buf, &mut write_buf, true) {
            Err(why) => panic!("[-] Unable to decrypt data: {:?}", why),
            Ok(result) => match result {
                buffer::BufferResult::BufferUnderflow => {},
                buffer::BufferResult::BufferOverflow => unreachable!(),
            }
        }
    }

    // Write data from blob back into our file buffer.
    println!("[*] Putting decrypted data where it belongs ...");
    let mut read_offset = 0usize;
    for i in 0..chunk_table_len {
        let chunk_info = get_data::<ChunkEntry>(
            &mut data, chunk_table_offs + mem::size_of::<ChunkEntry>() * i);
        unsafe {
            let chunk_size = (*chunk_info).size as usize;
            let mut dst = get_slice_rva::<u8>(&mut data, sec_table_offs, num_secs,
                (*chunk_info).offset as usize, chunk_size);
            let src = &decrypted_blob[read_offset..read_offset + chunk_size];

            for (cur_src_byte, cur_dst_byte) in src.iter().zip(dst.iter_mut()) {
                *cur_dst_byte = *cur_src_byte;
            }

            read_offset += chunk_size;
        }
    }

    // Blizz moved the IAT to the .reloc section, which isn't loaded by IDA
    // when loading a file without the "manual load" option, which results in
    // unresolved API calls. Rename it to make IDA map it into the IDB by default.
    println!("[*] Fixing section table ...");
    for i in 0..num_secs {
        let cur_sec = get_data::<pe::ImageSectionHeader>(
            &mut data, sec_table_offs + mem::size_of::<pe::ImageSectionHeader>() * i);

        unsafe {
            if (*cur_sec).name.iter().zip(".reloc".as_bytes().iter()).all(|(a, b)| a == b) {
                for (src, dst) in ".reloc_".as_bytes().iter().zip((*cur_sec).name.iter_mut()) {
                    *dst = *src;
                }
            }
        }
    }

    // Write output file.
    println!("[*] Writing output file ...");
    let mut out_f = match File::create(&Path::new(&out)) {
        Err(why) => panic!("[-] Couldn't open output file: {}", Error::description(&why)),
        Ok(file) => file,
    };

    match out_f.write_all(&data) {
        Err(why) => panic!("[-] Couldn't write to output file: {}", Error::description(&why)),
        Ok(..) => println!("[+] Wrote {:?} bytes ({}).", data.len(), &out),
    }
}

#[repr(C)]
#[packed]
struct ChunkEntry {
    pub offset: u32,
    pub size: u32,
}

/// Obtains a ptr to a struct inside the input file by it's file offset.
fn get_data<T>(data: &mut Vec<u8>, file_offset: usize) -> *mut T {
    if mem::size_of::<T>() > data.len() - file_offset {
        panic!("[-] Invalid input file: file address exceeds file boundaries.");
    }
    unsafe { mem::transmute(data.as_ptr() as usize + file_offset) }
}

/// Obtains a ptr to a struct inside the input file by it's RVA.
fn get_data_rva<T>(data: &mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize) -> *mut T {

    match rva_to_fo(data, sec_table_offs, num_secs, rva) {
        Some(rva) => get_data::<T>(data, rva),
        None => panic!("[-] Invalid input file: Pointer to an invalid RVA encountered.")
    }
}

/// Obtains a mutable slice from the input file by it's RVA.
fn get_slice_rva<'a, T>(data: &'a mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize, len: usize) -> &'a mut [T] {

    let file_offset = match rva_to_fo(data, sec_table_offs, num_secs, rva) {
        Some(fo) => fo,
        None => panic!("[-] Invalid input file: Pointer to an invalid RVA encountered.")
    };

    if mem::size_of::<T>() * len > data.len() - file_offset {
        panic!("[-] Invalid input file: file address exceeds file boundaries.");
    }

    unsafe { slice::from_raw_parts_mut(mem::transmute(
        data.as_ptr() as usize + file_offset), len) }
}

/// Translates an RVA to a file offset.
fn rva_to_fo(data: &mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize) -> Option<usize> {

    for i in 0..num_secs {
        let cur_sec = get_data::<pe::ImageSectionHeader>(
            data, sec_table_offs + mem::size_of::<pe::ImageSectionHeader>() * i);

        unsafe {
            let sec_va = (*cur_sec).virtual_address as usize;
            if rva >= sec_va && rva <= sec_va + (*cur_sec).virtual_size as usize {
                return Some(rva - sec_va + (*cur_sec).pointer_to_raw_data as usize);
            }
        }
    }

    None
}<commit_msg>replaced multiple checks with one match expression<commit_after>/*
 * Tool statically unpacking HotS binaries.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 athre0z
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

extern crate crypto;
mod pe;

use std::fs::File;
use std::path::Path;
use std::io::prelude::*;
use std::error::Error;
use std::mem;
use std::slice;
use std::env;

use crypto::{buffer, aes, blockmodes};

#[allow(unused_imports)]
use crypto::symmetriccipher::Encryptor;

/// Entry-point.
fn main() {
    println!("HotS-unpack by athre0z (c) 2015 zyantific.com\n");

    // Parse arguments.
    let args: Vec<_> = env::args().collect();
    let (in_path, out_path) = match args.len() {
        2 => (&args[1], "out.exe".to_string()),
        3 => (&args[1], args[2].clone()),
        _ => {
            println!("[*] Usage: {} <input binary> [output binary]", args[0]);
            return;
        }
    };

    // Read file.
    let mut f = match File::open(&Path::new(&in_path)) {
        Err(why) => panic!("[-] Couldn't open input file: {}", Error::description(&why)),
        Ok(file) => file,
    };

    let mut data = Vec::new();
    let data_size = match f.read_to_end(&mut data) {
        Err(why) => panic!("[-] Couldn't read input file: {}", Error::description(&why)),
        Ok(x) => x
    };

    println!("[+] Read file: {} bytes", data_size);

    // Parse DOS-header.
    let mz = get_data::<pe::ImageDosHeader>(&mut data, 0);
    if unsafe { (*mz).magic } != pe::DOS_MAGIC {
        panic!("[-] Invalid input file: invalid DOS header");
    }

    // Parse NT-header.
    let nt_header_offs = unsafe { (*mz).lfanew } as usize;
    let nt = get_data::<pe::ImageNtHeaders32>(&mut data, nt_header_offs);
    if unsafe { (*nt).signature } != pe::PE_MAGIC {
        panic!("[-] Invalid input file: invalid NT header");
    }

    // Obtain useful information.
    let sec_table_offs = nt_header_offs + mem::size_of::<pe::ImageNtHeaders32>();
    let num_secs = unsafe { (*nt).file_header.number_of_sections } as usize;
    let data_offs = sec_table_offs
        + mem::size_of::<pe::ImageSectionHeader>() * num_secs;
    let image_base = unsafe { (*nt).opt_header.image_base } as usize;

    // Scan for chunk table.
    println!("[*] Heuristically locating chunk table ...");
    let mut chunk_table_offs = 0;
    let mut chunk_table_len = 0;

    'scan: for i in data_offs..data.len() {
        let mut offs = 0usize;
        let mut last_addr = 0u32;

        // Check if candidate looks like a chunk table.
        while i + offs + mem::size_of::<ChunkEntry>() <= data.len() {
            unsafe {
                let cur = get_data::<ChunkEntry>(&mut data, i + offs);

                // The chunk table's end is indicated with an 0xFFFFFFFF.
                if offs >= 1000 * mem::size_of::<ChunkEntry>() && (*cur).offset == 0xFFFFFFFF {
                    chunk_table_offs = i;
                    chunk_table_len = (offs / mem::size_of::<ChunkEntry>()) - 1;
                    break 'scan;
                }

                // The chunk table entries are stored ascending.
                if (*cur).offset <= last_addr {
                    break;
                }

                last_addr = (*cur).offset;
                offs += mem::size_of::<ChunkEntry>();
            }
        }
    }

    if chunk_table_offs == 0 || chunk_table_len == 0 {
        panic!("[-] Unable to locate chunk table.");
    }

    println!("[+] Found chunk table @ FO 0x{:x} ({} entries).",
        chunk_table_offs, chunk_table_len);

    // Scan for AES key.
    println!("[*] Locating AES key ...");

    // 0xFF -> wildcard
    let pattern = [
        0x68, 0x80, 0x00, 0x00, 0x00, // push 80h
        0x68, 0xFF, 0xFF, 0xFF, 0xFF, // push offset g_aesInputKey
        0x68, 0xFF, 0xFF, 0xFF, 0xFF, // push offset g_aesKey
        0xE8, 0xFF, 0xFF, 0xFF, 0xFF, // call AesSetDecryptKey
        0xE9,                         // jmp  XXX
    ];

    let mut match_ = None;
    for offs in sec_table_offs..(data.len() - pattern.len()) {
        let mut exhausted = true;
        for (i, cur_pat_byte) in pattern.iter().enumerate() {
            if *cur_pat_byte == 0xFF { continue; }
            if data[offs + i] != *cur_pat_byte {
                exhausted = false;
                break;
            }
        }

        if exhausted {
            match_ = Some(offs);
            break;
        }
    }

    let aes_key_rva = match match_ {
        Some(x) => unsafe { *get_data::<u32>(&mut data, x + 6) as usize - image_base },
        None => panic!("[-] Unable to locate AES key.")
    };

    print!("[+] Found AES key @ RVA 0x{:x}: ", aes_key_rva);

    // Dump AES key.
    let aes_key = unsafe { *get_data_rva::<[u8; 16]>(
        &mut data, sec_table_offs, num_secs, aes_key_rva) };

    for i in aes_key.iter() {
        print!("{:02x} ", i);
    }
    print!("\n");

    // Collect all encrypted chunks into one buffer (we got plenty of memory, don't we? :p)
    println!("[*] Merging encrypted chunks ...", );
    let mut encrypted_blob = Vec::<u8>::new();
    for i in 0..chunk_table_len {
        let chunk_info = get_data::<ChunkEntry>(
            &mut data, chunk_table_offs + mem::size_of::<ChunkEntry>() * i);
        unsafe {
            encrypted_blob.extend(get_slice_rva::<u8>(
                &mut data, sec_table_offs, num_secs, (*chunk_info).offset as usize,
                (*chunk_info).size as usize).to_vec());
        }
    }

    let padding_length = 16 - encrypted_blob.len() % 16;
    if padding_length > 0 {
        encrypted_blob.extend(vec![0; padding_length]);
    }

    // Decrypt blob.
    println!("[*] Decrypting ...");
    let mut decryptor = aes::ecb_decryptor(
        aes::KeySize::KeySize128,
        &aes_key,
        blockmodes::NoPadding);
    let mut decrypted_blob = vec![0u8; encrypted_blob.len()];

    {
        let mut read_buf = buffer::RefReadBuffer::new(&encrypted_blob);
        let mut write_buf = buffer::RefWriteBuffer::new(&mut decrypted_blob);
        match decryptor.decrypt(&mut read_buf, &mut write_buf, true) {
            Err(why) => panic!("[-] Unable to decrypt data: {:?}", why),
            Ok(result) => match result {
                buffer::BufferResult::BufferUnderflow => {},
                buffer::BufferResult::BufferOverflow => unreachable!(),
            }
        }
    }

    // Write data from blob back into our file buffer.
    println!("[*] Putting decrypted data where it belongs ...");
    let mut read_offset = 0usize;
    for i in 0..chunk_table_len {
        let chunk_info = get_data::<ChunkEntry>(
            &mut data, chunk_table_offs + mem::size_of::<ChunkEntry>() * i);
        unsafe {
            let chunk_size = (*chunk_info).size as usize;
            let mut dst = get_slice_rva::<u8>(&mut data, sec_table_offs, num_secs,
                (*chunk_info).offset as usize, chunk_size);
            let src = &decrypted_blob[read_offset..read_offset + chunk_size];

            for (cur_src_byte, cur_dst_byte) in src.iter().zip(dst.iter_mut()) {
                *cur_dst_byte = *cur_src_byte;
            }

            read_offset += chunk_size;
        }
    }

    // Blizz moved the IAT to the .reloc section, which isn't loaded by IDA
    // when loading a file without the "manual load" option, which results in
    // unresolved API calls. Rename it to make IDA map it into the IDB by default.
    println!("[*] Fixing section table ...");
    for i in 0..num_secs {
        let cur_sec = get_data::<pe::ImageSectionHeader>(
            &mut data, sec_table_offs + mem::size_of::<pe::ImageSectionHeader>() * i);

        unsafe {
            if (*cur_sec).name.iter().zip(".reloc".as_bytes().iter()).all(|(a, b)| a == b) {
                for (src, dst) in ".reloc_".as_bytes().iter().zip((*cur_sec).name.iter_mut()) {
                    *dst = *src;
                }
            }
        }
    }

    // Write output file.
    println!("[*] Writing output file ...");
    let mut out_f = match File::create(&Path::new(&out_path)) {
        Err(why) => panic!("[-] Couldn't open output file: {}", Error::description(&why)),
        Ok(file) => file,
    };

    match out_f.write_all(&data) {
        Err(why) => panic!("[-] Couldn't write to output file: {}", Error::description(&why)),
        Ok(..) => println!("[+] Wrote {:?} bytes ({}).", data.len(), &out_path),
    }
}

#[repr(C)]
#[packed]
struct ChunkEntry {
    pub offset: u32,
    pub size: u32,
}

/// Obtains a ptr to a struct inside the input file by it's file offset.
fn get_data<T>(data: &mut Vec<u8>, file_offset: usize) -> *mut T {
    if mem::size_of::<T>() > data.len() - file_offset {
        panic!("[-] Invalid input file: file address exceeds file boundaries.");
    }
    unsafe { mem::transmute(data.as_ptr() as usize + file_offset) }
}

/// Obtains a ptr to a struct inside the input file by it's RVA.
fn get_data_rva<T>(data: &mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize) -> *mut T {

    match rva_to_fo(data, sec_table_offs, num_secs, rva) {
        Some(rva) => get_data::<T>(data, rva),
        None => panic!("[-] Invalid input file: Pointer to an invalid RVA encountered.")
    }
}

/// Obtains a mutable slice from the input file by it's RVA.
fn get_slice_rva<'a, T>(data: &'a mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize, len: usize) -> &'a mut [T] {

    let file_offset = match rva_to_fo(data, sec_table_offs, num_secs, rva) {
        Some(fo) => fo,
        None => panic!("[-] Invalid input file: Pointer to an invalid RVA encountered.")
    };

    if mem::size_of::<T>() * len > data.len() - file_offset {
        panic!("[-] Invalid input file: file address exceeds file boundaries.");
    }

    unsafe { slice::from_raw_parts_mut(mem::transmute(
        data.as_ptr() as usize + file_offset), len) }
}

/// Translates an RVA to a file offset.
fn rva_to_fo(data: &mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize) -> Option<usize> {

    for i in 0..num_secs {
        let cur_sec = get_data::<pe::ImageSectionHeader>(
            data, sec_table_offs + mem::size_of::<pe::ImageSectionHeader>() * i);

        unsafe {
            let sec_va = (*cur_sec).virtual_address as usize;
            if rva >= sec_va && rva <= sec_va + (*cur_sec).virtual_size as usize {
                return Some(rva - sec_va + (*cur_sec).pointer_to_raw_data as usize);
            }
        }
    }

    None
}<|endoftext|>


<commit_before>extern crate rff;
extern crate clap;

use std::io::{self, Write, BufWriter};
use rff::{stdin, match_and_score};
use rff::interface::Interface;
use clap::{App, Arg};

fn main() {
    let matches = App::new("rff").
        version(env!("CARGO_PKG_VERSION")).
        author("Andrew S. <andrew@stwrt.ca>").
        about("A fuzzy finder.").
        arg(
            Arg::with_name("QUERY").
                short("s").
                long("search").
                help("Term to search for")
        ).
        arg(
            Arg::with_name("benchmark").
                long("benchmark").
                help("Run rff in benchmark mode")
        ).
        get_matches();

    let lines = stdin::slurp();

    if matches.is_present("QUERY") {
        let query = matches.value_of("QUERY").unwrap();

        if matches.is_present("benchmark") {
            benchmark(query, lines);
        } else {
            search(query, lines);
        }
    } else {
        Interface::new(lines).run();
    }
}

fn benchmark(needle: &str, lines: Vec<String>) {
    // in benchmark mode, we run the match/score/sort loop 100 times
    for _ in 0..100 {
        lines
            .iter()
            .filter_map(|line| match_and_score(needle, line))
            .collect::<Vec<_>>()
            .sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap().reverse());
    }
}

fn search(needle: &str, lines: Vec<String>) {
    let mut lines: Vec<_> = lines
        .iter()
        .filter_map(|line| match_and_score(needle, line))
        .collect();

    lines.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap().reverse());

    let stdout = io::stdout();
    let mut stdout = BufWriter::new(stdout.lock());

    for line in &lines {
        writeln!(stdout, "{}", line.0).unwrap();
    }
}
<commit_msg>Separate main() and run(), make use of exit codes<commit_after>extern crate rff;
extern crate clap;

use std::io::{self, Write, BufWriter};
use rff::{stdin, match_and_score};
use rff::interface::Interface;
use clap::{App, Arg};

fn main() {
    let status_code = run();
    std::process::exit(status_code);
}

fn run() -> i32 {
    let matches = App::new("rff").
        version(env!("CARGO_PKG_VERSION")).
        author("Andrew S. <andrew@stwrt.ca>").
        about("A fuzzy finder.").
        arg(
            Arg::with_name("QUERY").
                short("s").
                long("search").
                help("Term to search for")
        ).
        arg(
            Arg::with_name("benchmark").
                long("benchmark").
                help("Run rff in benchmark mode")
        ).
        get_matches();

    let lines = stdin::slurp();

    if matches.is_present("QUERY") {
        let query = matches.value_of("QUERY").unwrap();

        if matches.is_present("benchmark") {
            benchmark(query, lines);
        } else {
            search(query, lines);
        }
    } else {
        Interface::new(lines).run();
    }

    return 0
}

fn benchmark(needle: &str, lines: Vec<String>) {
    // in benchmark mode, we run the match/score/sort loop 100 times
    for _ in 0..100 {
        lines
            .iter()
            .filter_map(|line| match_and_score(needle, line))
            .collect::<Vec<_>>()
            .sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap().reverse());
    }
}

fn search(needle: &str, lines: Vec<String>) {
    let mut lines: Vec<_> = lines
        .iter()
        .filter_map(|line| match_and_score(needle, line))
        .collect();

    lines.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap().reverse());

    let stdout = io::stdout();
    let mut stdout = BufWriter::new(stdout.lock());

    for line in &lines {
        writeln!(stdout, "{}", line.0).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::old_io;
use std::rand;

fn main() {
    let random = (rand::random::<u32>() % 10) + 1;
    println!("{}", random);
    println!("Hello, it's a guessing game!");
    println!("I'll draw a number between 1-10 and you need to guess which one.");
    println!("Only one shot!");

    let input = old_io::stdin().read_line().ok().expect("You need to pass nummber");

    // It seems, that we need to remove newline before parsing (bug?)
    let parsed_input: Result<u32, _> = input.trim().parse();

    let num = match parsed_input {
        Ok(num) => num,
        Err(_) => {
            println!("Please input number!");
            return;
        }
    };

    if random == num {
        println!("You won!");
    } else {
        println!("Looser!");
    }
}
<commit_msg>Multiple tries<commit_after>use std::old_io;
use std::rand;
use std::cmp::Ordering;

fn cmp(val1: u32, val2: u32) -> Ordering {
    if val1 < val2 { Ordering::Less }
    else if val1 > val2 { Ordering::Greater }
    else { Ordering::Equal }
}

fn main() {
    let random = (rand::random::<u32>() % 10) + 1;
    println!("Hello, it's a guessing game!");
    println!("I'll draw a number between 1-10 and you need to guess which one.");

    loop {
        println!("Please enter your number");
        let input = old_io::stdin().read_line().ok().expect("You need to pass nummber");

        // It seems, that we need to remove newline before parsing
        let parsed_input: Result<u32, _> = input.trim().parse();

        let num = match parsed_input {
            Ok(num) => num,
            Err(_) => {
                println!("Please input number!");
                return;
            }
        };

        match cmp(num, random) {
            Ordering::Less => println!("Your number is too low!"),
            Ordering::Greater => println!("Your number is too big!"),
            Ordering::Equal => {
                println!("You have won!");
                break;
            },
        }
    }
}
<|endoftext|>


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

// Import crates with necessary types into a new project.

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

// Import necessary types from crates.

use exonum::blockchain::{self, Blockchain, Service, GenesisConfig, ValidatorKeys, Transaction,
                         ApiContext};
use exonum::node::{Node, NodeConfig, NodeApiConfig, TransactionSend, ApiSender, NodeChannel};
use exonum::messages::{RawTransaction, FromRaw, Message};
use exonum::storage::{Fork, MemoryDB, MapIndex};
use exonum::crypto::{PublicKey, Hash, HexValue};
use exonum::encoding;
use exonum::api::{Api, ApiError};
use iron::prelude::*;
use iron::Handler;
use router::Router;

// // // // // // // // // // CONSTANTS // // // // // // // // // //

// Define service ID for the service trait.

const SERVICE_ID: u16 = 1;

// Define constants for transaction types within the service.

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

// Define initial balance of a newly created wallet.

const INIT_BALANCE: u64 = 100;

// // // // // // // // // // PERSISTENT DATA // // // // // // // // // //

// Declare the data to be stored in the blockchain. In the present case,
// declare a type for storing information about the wallet and its balance.

/// Declare a [serializable][1]
/// [1]: https://github.com/exonum/exonum-doc/blob/master/src/architecture/serialization.md
/// struct and determine bounds of its fields with `encoding_struct!` macro.
encoding_struct! {
    struct Wallet {
        const SIZE = 48;

        field pub_key:            &PublicKey  [00 => 32]
        field name:               &str        [32 => 40]
        field balance:            u64         [40 => 48]
    }
}

/// Add methods to the `Wallet` type for changing balance.
impl Wallet {
    pub fn increase(self, amount: u64) -> Self {
        let balance = self.balance() + amount;
        Self::new(self.pub_key(), self.name(), balance)
    }

    pub fn decrease(self, amount: u64) -> Self {
        let balance = self.balance() - amount;
        Self::new(self.pub_key(), self.name(), balance)
    }
}

// // // // // // // // // // DATA LAYOUT // // // // // // // // // //

/// Create schema of the key-value storage implemented by `MemoryDB`. In the
/// present case a `Fork` of the database is used.
pub struct CurrencySchema<'a> {
    view: &'a mut Fork,
}

/// Declare layout of the data. Use an instance of [`MapIndex`][2]
/// [2]: https://github.com/exonum/exonum-doc/blob/master/src/architecture/storage.md#mapindex
/// to keep wallets in storage. Index values are serialized `Wallet` structs.
///
/// Isolate the wallets map into a separate entity by adding a unique prefix,
/// i.e. the first argument to the `MapIndex::new` call.
impl<'a> CurrencySchema<'a> {
    pub fn wallets(&mut self) -> MapIndex<&mut Fork, PublicKey, Wallet> {
        let prefix = blockchain::gen_prefix(SERVICE_ID, 0, &());
        MapIndex::new(prefix, self.view)
    }

    /// Get a separate wallet from the storage.
    pub fn wallet(&mut self, pub_key: &PublicKey) -> Option<Wallet> {
        self.wallets().get(pub_key)
    }
}

// // // // // // // // // // TRANSACTIONS // // // // // // // // // //

/// Create a new wallet.
message! {
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        field pub_key:     &PublicKey  [00 => 32]
        field name:        &str        [32 => 40]
    }
}

/// Transfer coins between the wallets.
message! {
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        field from:        &PublicKey  [00 => 32]
        field to:          &PublicKey  [32 => 64]
        field amount:      u64         [64 => 72]
        field seed:        u64         [72 => 80]
    }
}

// // // // // // // // // // CONTRACTS // // // // // // // // // //

/// Execute a transaction.
impl Transaction for TxCreateWallet {
    /// Verify integrity of the transaction by checking the transaction
    /// signature.
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    /// Apply logic to the storage when executing the transaction.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets().put(self.pub_key(), wallet)
        }
    }

    /// Provide information about the transaction to be used in the blockchain explorer.
    fn info(&self) -> serde_json::Value {
        serde_json::to_value(&self).expect("Cannot serialize transaction to JSON")
    }
}

impl Transaction for TxTransfer {
    /// Check if the sender is not the receiver. Check correctness of the
    /// sender's signature.
    fn verify(&self) -> bool {
        (*self.from() != *self.to()) && self.verify_signature(self.from())
    }

    /// Retrieve two wallets to apply the transfer. Check the sender's
    /// balance and apply changes to the balances of the wallets.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(sender), Some(receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                let sender = sender.decrease(amount);
                let receiver = receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }

    /// Provide information about the transaction to be used in the blockchain explorer.
    fn info(&self) -> serde_json::Value {
        serde_json::to_value(&self).expect("Cannot serialize transaction to JSON")
    }
}

// // // // // // // // // // REST API // // // // // // // // // //

/// Implement the node API.
#[derive(Clone)]
struct CryptocurrencyApi {
    channel: ApiSender<NodeChannel>,
    blockchain: Blockchain,
}

/// Shortcut to get data on wallets.
impl CryptocurrencyApi {
    fn get_wallet(&self, pub_key: &PublicKey) -> Option<Wallet> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        schema.wallet(pub_key)
    }

    fn get_wallets(&self) -> Option<Vec<Wallet>> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        let idx = schema.wallets();
        let wallets: Vec<Wallet> = idx.values().collect();
        if wallets.is_empty() {
            None
        } else {
            Some(wallets)
        }
    }
}

/// Add an enum which joins transactions of both types to simplify request
/// processing.
#[serde(untagged)]
#[derive(Clone, Serialize, Deserialize)]
enum TransactionRequest {
    CreateWallet(TxCreateWallet),
    Transfer(TxTransfer),
}

/// Implement a trait for the enum for deserialized `TransactionRequest`s
/// to fit into the node channel.
impl Into<Box<Transaction>> for TransactionRequest {
    fn into(self) -> Box<Transaction> {
        match self {
            TransactionRequest::CreateWallet(trans) => Box::new(trans),
            TransactionRequest::Transfer(trans) => Box::new(trans),
        }
    }
}

/// The structure returned by the REST API.
#[derive(Serialize, Deserialize)]
struct TransactionResponse {
    tx_hash: Hash,
}

/// Implement the `Api` trait.
/// `Api` facilitates conversion between transactions/read requests and REST
/// endpoints; for example, it parses `POSTed` JSON into the binary transaction
/// representation used in Exonum internally.
impl Api for CryptocurrencyApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let transaction = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<TransactionRequest>>() {
                Ok(Some(transaction)) => {
                    let transaction: Box<Transaction> = transaction.into();
                    let tx_hash = transaction.hash();
                    self_.channel.send(transaction).map_err(ApiError::Events)?;
                    let json = TransactionResponse { tx_hash };
                    self_.ok_response(&serde_json::to_value(&json).unwrap())
                }
                Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
            }
        };

        // Gets status of all wallets.
        let self_ = self.clone();
        let wallets_info = move |_: &mut Request| -> IronResult<Response> {
            if let Some(wallets) = self_.get_wallets() {
                self_.ok_response(&serde_json::to_value(wallets).unwrap())
            } else {
                self_.not_found_response(
                    &serde_json::to_value("Wallets database is empty")
                        .unwrap(),
                )
            }
        };

        // Gets status of the wallet corresponding to the public key.
        let self_ = self.clone();
        let wallet_info = move |req: &mut Request| -> IronResult<Response> {
            let path = req.url.path();
            let wallet_key = path.last().unwrap();
            let public_key = PublicKey::from_hex(wallet_key).map_err(ApiError::FromHex)?;
            if let Some(wallet) = self_.get_wallet(&public_key) {
                self_.ok_response(&serde_json::to_value(wallet).unwrap())
            } else {
                self_.not_found_response(&serde_json::to_value("Wallet not found").unwrap())
            }
        };

        // Bind the transaction handler to a specific route.
        router.post("/v1/wallets/transaction", transaction, "transaction");
        router.get("/v1/wallets", wallets_info, "wallets_info");
        router.get("/v1/wallet/:pub_key", wallet_info, "wallet_info");
    }
}

// // // // // // // // // // SERVICE DECLARATION // // // // // // // // // //

/// Define the service.
struct CurrencyService;

/// Implement a `Service` trait for the service.
impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    /// Implement a method to deserialize transactions coming to the node.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        let trans: Box<Transaction> = match raw.message_type() {
            TX_TRANSFER_ID => Box::new(TxTransfer::from_raw(raw)?),
            TX_CREATE_WALLET_ID => Box::new(TxCreateWallet::from_raw(raw)?),
            _ => {
                return Err(encoding::Error::IncorrectMessageType {
                    message_type: raw.message_type(),
                });
            }
        };
        Ok(trans)
    }

    /// Create a REST `Handler` to process web requests to the node.
    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = CryptocurrencyApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

// // // // // // // // // // ENTRY POINT // // // // // // // // // //

fn main() {
    exonum::helpers::init_logger().unwrap();

    println!("Creating in-memory database...");
    let db = MemoryDB::new();
    let services: Vec<Box<Service>> = vec![Box::new(CurrencyService)];
    let blockchain = Blockchain::new(Box::new(db), services);

    let (consensus_public_key, consensus_secret_key) = exonum::crypto::gen_keypair();
    let (service_public_key, service_secret_key) = exonum::crypto::gen_keypair();

    let validator_keys = ValidatorKeys {
        consensus_key: consensus_public_key,
        service_key: service_public_key,
    };
    let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

    let api_address = "0.0.0.0:8000".parse().unwrap();
    let api_cfg = NodeApiConfig {
        public_api_address: Some(api_address),
        ..Default::default()
    };

    let peer_address = "0.0.0.0:2000".parse().unwrap();

    let node_cfg = NodeConfig {
        listen_address: peer_address,
        peers: vec![],
        service_public_key,
        service_secret_key,
        consensus_public_key,
        consensus_secret_key,
        genesis,
        external_address: None,
        network: Default::default(),
        whitelist: Default::default(),
        api: api_cfg,
        mempool: Default::default(),
        services_configs: Default::default(),
    };

    println!("Starting a single node...");
    let mut node = Node::new(blockchain, node_cfg);

    println!("Blockchain is ready for transactions!");
    node.run().unwrap();
}
<commit_msg>Move node config to separate function<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Import crates with necessary types into a new project.

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

// Import necessary types from crates.

use exonum::blockchain::{self, Blockchain, Service, GenesisConfig, ValidatorKeys, Transaction,
                         ApiContext};
use exonum::node::{Node, NodeConfig, NodeApiConfig, TransactionSend, ApiSender, NodeChannel};
use exonum::messages::{RawTransaction, FromRaw, Message};
use exonum::storage::{Fork, MemoryDB, MapIndex};
use exonum::crypto::{PublicKey, Hash, HexValue};
use exonum::encoding;
use exonum::api::{Api, ApiError};
use iron::prelude::*;
use iron::Handler;
use router::Router;

// // // // // // // // // // CONSTANTS // // // // // // // // // //

// Define service ID for the service trait.

const SERVICE_ID: u16 = 1;

// Define constants for transaction types within the service.

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

// Define initial balance of a newly created wallet.

const INIT_BALANCE: u64 = 100;

// // // // // // // // // // PERSISTENT DATA // // // // // // // // // //

// Declare the data to be stored in the blockchain. In the present case,
// declare a type for storing information about the wallet and its balance.

/// Declare a [serializable][1]
/// [1]: https://github.com/exonum/exonum-doc/blob/master/src/architecture/serialization.md
/// struct and determine bounds of its fields with `encoding_struct!` macro.
encoding_struct! {
    struct Wallet {
        const SIZE = 48;

        field pub_key:            &PublicKey  [00 => 32]
        field name:               &str        [32 => 40]
        field balance:            u64         [40 => 48]
    }
}

/// Add methods to the `Wallet` type for changing balance.
impl Wallet {
    pub fn increase(self, amount: u64) -> Self {
        let balance = self.balance() + amount;
        Self::new(self.pub_key(), self.name(), balance)
    }

    pub fn decrease(self, amount: u64) -> Self {
        let balance = self.balance() - amount;
        Self::new(self.pub_key(), self.name(), balance)
    }
}

// // // // // // // // // // DATA LAYOUT // // // // // // // // // //

/// Create schema of the key-value storage implemented by `MemoryDB`. In the
/// present case a `Fork` of the database is used.
pub struct CurrencySchema<'a> {
    view: &'a mut Fork,
}

/// Declare layout of the data. Use an instance of [`MapIndex`][2]
/// [2]: https://github.com/exonum/exonum-doc/blob/master/src/architecture/storage.md#mapindex
/// to keep wallets in storage. Index values are serialized `Wallet` structs.
///
/// Isolate the wallets map into a separate entity by adding a unique prefix,
/// i.e. the first argument to the `MapIndex::new` call.
impl<'a> CurrencySchema<'a> {
    pub fn wallets(&mut self) -> MapIndex<&mut Fork, PublicKey, Wallet> {
        let prefix = blockchain::gen_prefix(SERVICE_ID, 0, &());
        MapIndex::new(prefix, self.view)
    }

    /// Get a separate wallet from the storage.
    pub fn wallet(&mut self, pub_key: &PublicKey) -> Option<Wallet> {
        self.wallets().get(pub_key)
    }
}

// // // // // // // // // // TRANSACTIONS // // // // // // // // // //

/// Create a new wallet.
message! {
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        field pub_key:     &PublicKey  [00 => 32]
        field name:        &str        [32 => 40]
    }
}

/// Transfer coins between the wallets.
message! {
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        field from:        &PublicKey  [00 => 32]
        field to:          &PublicKey  [32 => 64]
        field amount:      u64         [64 => 72]
        field seed:        u64         [72 => 80]
    }
}

// // // // // // // // // // CONTRACTS // // // // // // // // // //

/// Execute a transaction.
impl Transaction for TxCreateWallet {
    /// Verify integrity of the transaction by checking the transaction
    /// signature.
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    /// Apply logic to the storage when executing the transaction.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets().put(self.pub_key(), wallet)
        }
    }

    /// Provide information about the transaction to be used in the blockchain explorer.
    fn info(&self) -> serde_json::Value {
        serde_json::to_value(&self).expect("Cannot serialize transaction to JSON")
    }
}

impl Transaction for TxTransfer {
    /// Check if the sender is not the receiver. Check correctness of the
    /// sender's signature.
    fn verify(&self) -> bool {
        (*self.from() != *self.to()) && self.verify_signature(self.from())
    }

    /// Retrieve two wallets to apply the transfer. Check the sender's
    /// balance and apply changes to the balances of the wallets.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(sender), Some(receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                let sender = sender.decrease(amount);
                let receiver = receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }

    /// Provide information about the transaction to be used in the blockchain explorer.
    fn info(&self) -> serde_json::Value {
        serde_json::to_value(&self).expect("Cannot serialize transaction to JSON")
    }
}

// // // // // // // // // // REST API // // // // // // // // // //

/// Implement the node API.
#[derive(Clone)]
struct CryptocurrencyApi {
    channel: ApiSender<NodeChannel>,
    blockchain: Blockchain,
}

/// Shortcut to get data on wallets.
impl CryptocurrencyApi {
    fn get_wallet(&self, pub_key: &PublicKey) -> Option<Wallet> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        schema.wallet(pub_key)
    }

    fn get_wallets(&self) -> Option<Vec<Wallet>> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        let idx = schema.wallets();
        let wallets: Vec<Wallet> = idx.values().collect();
        if wallets.is_empty() {
            None
        } else {
            Some(wallets)
        }
    }
}

/// Add an enum which joins transactions of both types to simplify request
/// processing.
#[serde(untagged)]
#[derive(Clone, Serialize, Deserialize)]
enum TransactionRequest {
    CreateWallet(TxCreateWallet),
    Transfer(TxTransfer),
}

/// Implement a trait for the enum for deserialized `TransactionRequest`s
/// to fit into the node channel.
impl Into<Box<Transaction>> for TransactionRequest {
    fn into(self) -> Box<Transaction> {
        match self {
            TransactionRequest::CreateWallet(trans) => Box::new(trans),
            TransactionRequest::Transfer(trans) => Box::new(trans),
        }
    }
}

/// The structure returned by the REST API.
#[derive(Serialize, Deserialize)]
struct TransactionResponse {
    tx_hash: Hash,
}

/// Implement the `Api` trait.
/// `Api` facilitates conversion between transactions/read requests and REST
/// endpoints; for example, it parses `POSTed` JSON into the binary transaction
/// representation used in Exonum internally.
impl Api for CryptocurrencyApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let transaction = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<TransactionRequest>>() {
                Ok(Some(transaction)) => {
                    let transaction: Box<Transaction> = transaction.into();
                    let tx_hash = transaction.hash();
                    self_.channel.send(transaction).map_err(ApiError::Events)?;
                    let json = TransactionResponse { tx_hash };
                    self_.ok_response(&serde_json::to_value(&json).unwrap())
                }
                Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
            }
        };

        // Gets status of all wallets.
        let self_ = self.clone();
        let wallets_info = move |_: &mut Request| -> IronResult<Response> {
            if let Some(wallets) = self_.get_wallets() {
                self_.ok_response(&serde_json::to_value(wallets).unwrap())
            } else {
                self_.not_found_response(
                    &serde_json::to_value("Wallets database is empty")
                        .unwrap(),
                )
            }
        };

        // Gets status of the wallet corresponding to the public key.
        let self_ = self.clone();
        let wallet_info = move |req: &mut Request| -> IronResult<Response> {
            let path = req.url.path();
            let wallet_key = path.last().unwrap();
            let public_key = PublicKey::from_hex(wallet_key).map_err(ApiError::FromHex)?;
            if let Some(wallet) = self_.get_wallet(&public_key) {
                self_.ok_response(&serde_json::to_value(wallet).unwrap())
            } else {
                self_.not_found_response(&serde_json::to_value("Wallet not found").unwrap())
            }
        };

        // Bind the transaction handler to a specific route.
        router.post("/v1/wallets/transaction", transaction, "transaction");
        router.get("/v1/wallets", wallets_info, "wallets_info");
        router.get("/v1/wallet/:pub_key", wallet_info, "wallet_info");
    }
}

// // // // // // // // // // SERVICE DECLARATION // // // // // // // // // //

/// Define the service.
pub struct CurrencyService;

/// Implement a `Service` trait for the service.
impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    /// Implement a method to deserialize transactions coming to the node.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        let trans: Box<Transaction> = match raw.message_type() {
            TX_TRANSFER_ID => Box::new(TxTransfer::from_raw(raw)?),
            TX_CREATE_WALLET_ID => Box::new(TxCreateWallet::from_raw(raw)?),
            _ => {
                return Err(encoding::Error::IncorrectMessageType {
                    message_type: raw.message_type(),
                });
            }
        };
        Ok(trans)
    }

    /// Create a REST `Handler` to process web requests to the node.
    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = CryptocurrencyApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

pub fn init_node() -> Node {
    let db = MemoryDB::new();
    let services: Vec<Box<Service>> = vec![Box::new(CurrencyService)];
    let blockchain = Blockchain::new(Box::new(db), services);

    let (consensus_public_key, consensus_secret_key) = exonum::crypto::gen_keypair();
    let (service_public_key, service_secret_key) = exonum::crypto::gen_keypair();

    let validator_keys = ValidatorKeys {
        consensus_key: consensus_public_key,
        service_key: service_public_key,
    };
    let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

    let api_address = "0.0.0.0:8000".parse().unwrap();
    let api_cfg = NodeApiConfig {
        public_api_address: Some(api_address),
        ..Default::default()
    };

    let peer_address = "0.0.0.0:2000".parse().unwrap();

    let node_cfg = NodeConfig {
        listen_address: peer_address,
        peers: vec![],
        service_public_key,
        service_secret_key,
        consensus_public_key,
        consensus_secret_key,
        genesis,
        external_address: None,
        network: Default::default(),
        whitelist: Default::default(),
        api: api_cfg,
        mempool: Default::default(),
        services_configs: Default::default(),
    };

    Node::new(blockchain, node_cfg)
}

fn main() {
    exonum::helpers::init_logger().unwrap();

    println!("Creating in-memory database...");
    let mut node = init_node();
    println!("Starting a single node...");
    node.run().unwrap();
    println!("Blockchain is ready for transactions!");
}
<|endoftext|>


<commit_before>//! Command line executable.
extern crate pico;
extern crate clap;

use std::str::FromStr;
use std::path::Path;
use std::io::stdout;
use pico::{HeaderFormat, major, minor};
use clap::{Arg, App};
use pico::file;

/// Executable description.
static DESCRIPTION: &str =
"Encode a file as Pico, decode a Pico-encoded file, or dump the header \
from a Pico-encoded file.";

static LONG_DESCRIPTION: &str =
"Input files are encoded by default.  If encoding, a .pico extension \
is added to the file.  If decoding, then the input must be Pico-encoded \
files, and a .raw extension is added by default.  If dumping the header, \
the input files must be Pico-encoded files, and the header is dumped as \
JSON to standard output.

The extension used can be overridden by --extension, which should include \
the dot.  Any provided suffix (by default there is none) is added to the \
file's base name.

The header kinds can be json, yaml, dict (python), or xml.

Keys must be specified as a list of hexadecimal digits (no spaces).";

/// Entry point when run from the command line.
fn main() {
    // Add some information to the end of the help.
    let after = format!(
        "{}\n\nPico Encoding Version: {}.{}\nSee: {}",
        LONG_DESCRIPTION, major(), minor(), env!("CARGO_PKG_HOMEPAGE")
    );

    // Parse command line arguments.
    let app_matches = App::new("Pico Rust Library")
        .version(env!("CARGO_PKG_VERSION"))
        .author("The Mons Pico Project")
        .about(DESCRIPTION)
        .after_help(after.as_str())
        .arg(Arg::with_name("verbose")
            .short("v")
            .long("verbose")
            .help("Increase verbosity.")
            .takes_value(false))
        .arg(Arg::with_name("debug")
            .long("debug")
            .help("Enable debugging.")
            .takes_value(false))
        .arg(Arg::with_name("decode")
            .conflicts_with("encode")
            .conflicts_with("header")
            .short("d")
            .long("decode")
            .help("Decode files.")
            .takes_value(false))
        .arg(Arg::with_name("encode")
            .conflicts_with("decode")
            .conflicts_with("header")
            .short("e")
            .long("encode")
            .help("Encode files.")
            .takes_value(false))
        .arg(Arg::with_name("extension")
            .long("extension")
            .help("Set output file extension.")
            .takes_value(true))
        .arg(Arg::with_name("header")
            .conflicts_with("encode")
            .conflicts_with("decode")
            .possible_values(&["DICT", "JSON", "YAML", "XML"])
            .short("H")
            .long("header")
            .value_name("format")
            .help("Dump header information.")
            .takes_value(true))
        .arg(Arg::with_name("suffix")
            .short("s")
            .long("suffix")
            .default_value("")
            .help("Suffix to add to output files.")
            .takes_value(true))
        .arg(Arg::with_name("files")
            .help("File names to process.")
            .multiple(true)
            .required(true)
            .takes_value(true))
        .get_matches();

    // Figure out correct operation.
    let filelist = app_matches.values_of("files").unwrap();
    enum Operation {
        Header, Encode, Decode,
    };
    let mut op = Operation::Encode;
    if app_matches.is_present("header") { op = Operation::Header; }
    if app_matches.is_present("decode") { op = Operation::Decode; }
    let header_format = match app_matches.value_of("header") {
        None => HeaderFormat::DICT,
        Some(name) => HeaderFormat::from_str(name).unwrap(),
    };
    let extension = match app_matches.value_of("extension") {
        None => {
            match op {
                Operation::Decode => ".raw",
                _ => ".pico",
            }
        },
        Some(ext) => ext,
    };
    let suffix = app_matches.value_of("suffix").unwrap();

    // Perform the operation for each specified file.
    for file in filelist {
        let filepath = Path::new(&file);
        let basename = filepath.file_stem().unwrap().to_string_lossy().into_owned();
        let oldname: String = filepath.to_string_lossy().into_owned();
        match op {
            Operation::Header => {
                println!("Pico Header as {:?} for: {:?}", header_format, filepath);
                file::dump_header(&oldname, stdout(), &header_format);
            },
            Operation::Encode => {
                let key = pico::gen_random_key(16);
                let newname = basename + suffix + extension;
                println!("Encoding {:?} -> {:?}", oldname, newname);
                file::encode(&oldname, &newname, key, vec![], 0);
            },
            Operation::Decode => {
                let newname = basename + suffix + extension;
                println!("Decoding {:?} -> {:?}", oldname, newname);
                file::decode(&oldname, &newname);
            },
        }
    }
}
<commit_msg>Added key switch, but not yet supported.<commit_after>//! Command line executable.
extern crate pico;
extern crate clap;

use std::str::FromStr;
use std::path::Path;
use std::io::stdout;
use pico::{HeaderFormat, major, minor};
use clap::{Arg, App};
use pico::file;

/// Executable description.
static DESCRIPTION: &str =
"Encode a file as Pico, decode a Pico-encoded file, or dump the header \
from a Pico-encoded file.";

static LONG_DESCRIPTION: &str =
"Input files are encoded by default.  If encoding, a .pico extension \
is added to the file.  If decoding, then the input must be Pico-encoded \
files, and a .raw extension is added by default.  If dumping the header, \
the input files must be Pico-encoded files, and the header is sent to \
standard output in the specified format.

The extension used can be overridden by --extension, which should include \
the dot.  Any provided suffix (by default there is none) is added to the \
file's base name.

The header kinds can be JSON, YAML, DICT (Python), or XML.

Keys must be specified as a list of hexadecimal digits (no spaces).  If \
no key is specified for encoding, a random key is generated.";

/// Entry point when run from the command line.
fn main() {
    // Add some information to the end of the help.
    let after = format!(
        "{}\n\nPico Encoding Version: {}.{}\nSee: {}",
        LONG_DESCRIPTION, major(), minor(), env!("CARGO_PKG_HOMEPAGE")
    );

    // Parse command line arguments.
    let app_matches = App::new("Pico Rust Library")
        .version(env!("CARGO_PKG_VERSION"))
        .author("The Mons Pico Project")
        .about(DESCRIPTION)
        .after_help(after.as_str())
        .arg(Arg::with_name("verbose")
            .short("v")
            .long("verbose")
            .help("Increase verbosity.")
            .takes_value(false))
        .arg(Arg::with_name("debug")
            .long("debug")
            .help("Enable debugging.")
            .takes_value(false))
        .arg(Arg::with_name("decode")
            .conflicts_with("encode")
            .conflicts_with("header")
            .short("d")
            .long("decode")
            .help("Decode files.")
            .takes_value(false))
        .arg(Arg::with_name("encode")
            .conflicts_with("decode")
            .conflicts_with("header")
            .short("e")
            .long("encode")
            .help("Encode files.")
            .takes_value(false))
        .arg(Arg::with_name("extension")
            .long("extension")
            .help("Set output file extension.")
            .takes_value(true))
        .arg(Arg::with_name("header")
            .conflicts_with("encode")
            .conflicts_with("decode")
            .possible_values(&["DICT", "JSON", "YAML", "XML"])
            .short("H")
            .long("header")
            .value_name("format")
            .help("Dump header information.")
            .takes_value(true))
        .arg(Arg::with_name("suffix")
            .short("s")
            .long("suffix")
            .default_value("")
            .help("Suffix to add to output files.")
            .takes_value(true))
        .arg(Arg::with_name("key")
            .short("k")
            .long("key")
            .help("Specify key for encoding.")
            .takes_value(true))
        .arg(Arg::with_name("files")
            .help("File names to process.")
            .multiple(true)
            .required(true)
            .takes_value(true))
        .get_matches();

    // Figure out correct operation.
    let filelist = app_matches.values_of("files").unwrap();
    enum Operation {
        Header, Encode, Decode,
    };
    let mut op = Operation::Encode;
    if app_matches.is_present("header") { op = Operation::Header; }
    if app_matches.is_present("decode") { op = Operation::Decode; }
    let header_format = match app_matches.value_of("header") {
        None => HeaderFormat::DICT,
        Some(name) => HeaderFormat::from_str(name).unwrap(),
    };
    let extension = match app_matches.value_of("extension") {
        None => {
            match op {
                Operation::Decode => ".raw",
                _ => ".pico",
            }
        },
        Some(ext) => ext,
    };
    let suffix = app_matches.value_of("suffix").unwrap();

    // Perform the operation for each specified file.
    for file in filelist {
        let filepath = Path::new(&file);
        let basename = filepath.file_stem().unwrap().to_string_lossy().into_owned();
        let oldname: String = filepath.to_string_lossy().into_owned();
        match op {
            Operation::Header => {
                println!("Pico Header as {:?} for: {:?}", header_format, filepath);
                file::dump_header(&oldname, stdout(), &header_format);
            },
            Operation::Encode => {
                let key = pico::gen_random_key(16);
                let newname = basename + suffix + extension;
                println!("Encoding {:?} -> {:?}", oldname, newname);
                file::encode(&oldname, &newname, key, vec![], 0);
            },
            Operation::Decode => {
                let newname = basename + suffix + extension;
                println!("Decoding {:?} -> {:?}", oldname, newname);
                file::decode(&oldname, &newname);
            },
        }
    }
}
<|endoftext|>


<commit_before>mod uci;
mod board;
mod tests;
mod search_algorithms;

#[macro_use]
extern crate log;

extern crate time;
extern crate rand;
extern crate ordered_float;
extern crate rayon;
extern crate log4rs;

use search_algorithms::alpha_beta;
use search_algorithms::alpha_beta::Score;
use search_algorithms::game_move::Move;
use search_algorithms::mcts;

use std::sync::{Arc, Mutex};
use std::io;
use std::fmt;
use std::hash::Hash;

use search_algorithms::board::EvalBoard;
use search_algorithms::board::GameResult;
use board::std_board::ChessBoard;
use board::crazyhouse_board::CrazyhouseBoard;
use board::sjadam_board::SjadamBoard;

use log4rs::config::{Appender, Config, Root};

fn init_log() -> Result<(), Box<std::error::Error>> {
    let appender = log4rs::append::file::FileAppender::builder().append(true).build("rasher.log")?;

    let config = Config::builder()
        .appender(Appender::builder().build("uci", Box::new(appender)))
        .build(Root::builder().appender("uci").build(log::LogLevelFilter::Debug))?;
    log4rs::init_config(config)?;
    Ok(())
}

#[macro_use]
extern crate lazy_static;
extern crate itertools;
fn main() {
    init_log().unwrap();
    let mut stdin = io::BufReader::new(io::stdin());
    info!("Opened log");
    loop {
        if let Ok(input) = uci::get_engine_input(&mut stdin) {

	    let tokens : Vec<&str> = input.split_whitespace().collect();

            match tokens[0] {
                "uci" => {
                    match uci::choose_variant(&mut stdin) {
                        Ok(_) => (),
                        Err(e) => {
                            error!("Engine front terminated unexpectedly with \"{}\"", e);
                        },
                    }
                    return;
                },
                "isready" => {
                    info!("received isready from GUI");
                    uci::uci_send("readyok");
                },

                "mem_usage" => {
                    use std::mem;
                    println!("Standard board: {}", mem::size_of::<board::std_board::ChessBoard>());
                    println!("Standard move: {}", mem::size_of::<board::std_move::ChessMove>());
                    println!("Standard piece: {}", mem::size_of::<board::std_board::Piece>());
                    println!("Board score: {}", mem::size_of::<Score>());
                    println!("Size of chess mcts node: {}",
                             mem::size_of::<mcts::MonteCarloTree<ChessBoard>>());

                },
                //"play_self" => play_game(&board::START_BOARD.clone()),
                "play_self" => {
                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let board = ChessBoard::start_board().clone();
                        play_game(board)
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let board = CrazyhouseBoard::start_board().clone();
                                play_game(board)
                            },
                            "sjadam" => {
                                let board = SjadamBoard::start_board().clone();
                                play_game(board)
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                },
                "play" => {
                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let board = ChessBoard::start_board().clone();
                        play_human(board)
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let board = CrazyhouseBoard::start_board().clone();
                                play_human(board)
                            },
                            "sjadam" => {
                                let board = SjadamBoard::start_board().clone();
                                play_human(board)
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                },
                "mcts" => {

                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let mut board = ChessBoard::start_board().clone();
                        search_algorithms::mcts::search_position(&mut board);
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let mut board = CrazyhouseBoard::start_board().clone();
                                search_algorithms::mcts::search_position(&mut board);
                            },
                            "sjadam" => {
                                let mut board = SjadamBoard::start_board().clone();
                                search_algorithms::mcts::search_position(&mut board);
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                }
                "mcts_debug" => mcts::play_human(ChessBoard::start_board()),
                s => warn!("Unrecognized command \"{}\".", s),
            }
        }
        else {
            return;
        }
    }
}

/// Makes the engine play a game against itself
fn play_game<B> (mut board : B)
    where B: EvalBoard + uci::UciBoard + fmt::Debug + Send + 'static + Hash + Eq,
<B as EvalBoard>::Move: Sync + Send {
    println!("Board:\n{:?}", board);
    println!("\n");
    match board.game_result() {
        None => {
            let (handle, channel) = alpha_beta::start_uci_search(
                board.clone(), uci::TimeRestriction::MoveTime(5000),
                uci::EngineOptions::new(),
                Arc::new(Mutex::new(uci::EngineComm::new())), None);

            let (score, move_str) = uci::get_uci_move_checked(handle, channel).unwrap();
            println!("Found move {} with score {}.", move_str, score);
            board.do_move(B::Move::from_alg(&move_str).unwrap());
            play_game(board);
        }
        Some(GameResult::WhiteWin) => println!("White won at move! Board:\n{:?}", board),
        Some(GameResult::BlackWin) => println!("Black won! Board:\n{:?}", board),
        Some(GameResult::Draw) => println!("The game was drawn! Board:\n{:?}", board),
    }
}
/// Play a game against the engine through stdin
fn play_human<B>(mut board : B)
    where B: EvalBoard + 'static + uci::UciBoard + fmt::Debug + Send + Hash + Eq, <B as EvalBoard>::Move: Sync + Send
{
    match board.game_result() {
        None => {
            use search_algorithms::board::Color::*;
            println!("Board:\n{:?}", board);
            // If black, play as human
            if board.to_move() == White {
                println!("Type your move as long algebraic notation (e2e4):");

                let reader = io::stdin();
                let mut input_str = "".to_string();
                let legal_moves = board.all_legal_moves();
                // Loop until user enters a valid move
                loop {
                    input_str.clear();
                    reader.read_line(&mut input_str).expect("Failed to read line");

                    match B::Move::from_alg(input_str.trim()) {
                        Ok(val) => {
                            if legal_moves.contains(&val) { break; }
                            println!("Move {:?} is illegal! Legal moves: {:?}", val, legal_moves);
                            println!("Try again: ");
                        }

                        Err(error) => {
                            println!("{}, try again.", error);
                        },
                    }
                }
                let c_move =  B::Move::from_alg(input_str.trim()).unwrap();
                board.do_move(c_move);
                play_human(board);
            }
            else {
                let (handle, channel) = alpha_beta::start_uci_search(
                    board.clone(), uci::TimeRestriction::MoveTime(5000), uci::EngineOptions::new(),
                    Arc::new(Mutex::new(uci::EngineComm::new())), None);

                let (score, move_str) = uci::get_uci_move_checked(handle, channel).unwrap();
                let best_move = <B as EvalBoard>::Move::from_alg(&move_str);
                println!("Computer played {:?} with score {}", best_move, score);
                board.do_move(best_move.unwrap());
                play_human(board);
            }
        }

        Some(GameResult::WhiteWin) => println!("White won at move! Board:\n{:?}", board),
        Some(GameResult::BlackWin) => println!("Black won! Board:\n{:?}", board),
        Some(GameResult::Draw) => println!("The game was drawn! Board:\n{:?}", board),
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NodeCount {
    intern: u64,
    leaf: u64,
    total: u64,
}

impl NodeCount {
    fn new() -> Self {
        NodeCount { intern: 0, leaf: 0, total: 0 }
    }
}

impl std::ops::Add for NodeCount {
    type Output = NodeCount;
    fn add(self, other: NodeCount) -> Self {
        NodeCount { intern: self.intern + other.intern, leaf: self.leaf + other.leaf,
                    total: self.total + other.total }
    }
}
<commit_msg>Engine no longer crashes without file system access<commit_after>mod uci;
mod board;
mod tests;
mod search_algorithms;

#[macro_use]
extern crate log;

extern crate time;
extern crate rand;
extern crate ordered_float;
extern crate rayon;
extern crate log4rs;

use search_algorithms::alpha_beta;
use search_algorithms::alpha_beta::Score;
use search_algorithms::game_move::Move;
use search_algorithms::mcts;

use std::sync::{Arc, Mutex};
use std::io;
use std::io::Write;
use std::fmt;
use std::hash::Hash;

use search_algorithms::board::EvalBoard;
use search_algorithms::board::GameResult;
use board::std_board::ChessBoard;
use board::crazyhouse_board::CrazyhouseBoard;
use board::sjadam_board::SjadamBoard;

use log4rs::config::{Appender, Config, Root};

fn init_log() -> Result<(), Box<std::error::Error>> {
    let appender = log4rs::append::file::FileAppender::builder().append(true).build("rasher.log")?;

    let config = Config::builder()
        .appender(Appender::builder().build("uci", Box::new(appender)))
        .build(Root::builder().appender("uci").build(log::LogLevelFilter::Debug))?;
    log4rs::init_config(config)?;
    Ok(())
}

#[macro_use]
extern crate lazy_static;
extern crate itertools;
fn main() {
    init_log().unwrap_or_else(|err| {
        let _ = writeln!(&mut io::stderr(), "Failed to open log file: {}", err);
    });
    let mut stdin = io::BufReader::new(io::stdin());
    info!("Opened log");
    loop {
        if let Ok(input) = uci::get_engine_input(&mut stdin) {

	    let tokens : Vec<&str> = input.split_whitespace().collect();

            match tokens[0] {
                "uci" => {
                    match uci::choose_variant(&mut stdin) {
                        Ok(_) => (),
                        Err(e) => {
                            error!("Engine front terminated unexpectedly with \"{}\"", e);
                        },
                    }
                    return;
                },
                "isready" => {
                    info!("received isready from GUI");
                    uci::uci_send("readyok");
                },

                "mem_usage" => {
                    use std::mem;
                    println!("Standard board: {}", mem::size_of::<board::std_board::ChessBoard>());
                    println!("Standard move: {}", mem::size_of::<board::std_move::ChessMove>());
                    println!("Standard piece: {}", mem::size_of::<board::std_board::Piece>());
                    println!("Board score: {}", mem::size_of::<Score>());
                    println!("Size of chess mcts node: {}",
                             mem::size_of::<mcts::MonteCarloTree<ChessBoard>>());

                },
                //"play_self" => play_game(&board::START_BOARD.clone()),
                "play_self" => {
                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let board = ChessBoard::start_board().clone();
                        play_game(board)
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let board = CrazyhouseBoard::start_board().clone();
                                play_game(board)
                            },
                            "sjadam" => {
                                let board = SjadamBoard::start_board().clone();
                                play_game(board)
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                },
                "play" => {
                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let board = ChessBoard::start_board().clone();
                        play_human(board)
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let board = CrazyhouseBoard::start_board().clone();
                                play_human(board)
                            },
                            "sjadam" => {
                                let board = SjadamBoard::start_board().clone();
                                play_human(board)
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                },
                "mcts" => {

                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let mut board = ChessBoard::start_board().clone();
                        search_algorithms::mcts::search_position(&mut board);
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let mut board = CrazyhouseBoard::start_board().clone();
                                search_algorithms::mcts::search_position(&mut board);
                            },
                            "sjadam" => {
                                let mut board = SjadamBoard::start_board().clone();
                                search_algorithms::mcts::search_position(&mut board);
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                }
                "mcts_debug" => mcts::play_human(ChessBoard::start_board()),
                s => warn!("Unrecognized command \"{}\".", s),
            }
        }
        else {
            return;
        }
    }
}

/// Makes the engine play a game against itself
fn play_game<B> (mut board : B)
    where B: EvalBoard + uci::UciBoard + fmt::Debug + Send + 'static + Hash + Eq,
<B as EvalBoard>::Move: Sync + Send {
    println!("Board:\n{:?}", board);
    println!("\n");
    match board.game_result() {
        None => {
            let (handle, channel) = alpha_beta::start_uci_search(
                board.clone(), uci::TimeRestriction::MoveTime(5000),
                uci::EngineOptions::new(),
                Arc::new(Mutex::new(uci::EngineComm::new())), None);

            let (score, move_str) = uci::get_uci_move_checked(handle, channel).unwrap();
            println!("Found move {} with score {}.", move_str, score);
            board.do_move(B::Move::from_alg(&move_str).unwrap());
            play_game(board);
        }
        Some(GameResult::WhiteWin) => println!("White won at move! Board:\n{:?}", board),
        Some(GameResult::BlackWin) => println!("Black won! Board:\n{:?}", board),
        Some(GameResult::Draw) => println!("The game was drawn! Board:\n{:?}", board),
    }
}
/// Play a game against the engine through stdin
fn play_human<B>(mut board : B)
    where B: EvalBoard + 'static + uci::UciBoard + fmt::Debug + Send + Hash + Eq, <B as EvalBoard>::Move: Sync + Send
{
    match board.game_result() {
        None => {
            use search_algorithms::board::Color::*;
            println!("Board:\n{:?}", board);
            // If black, play as human
            if board.to_move() == White {
                println!("Type your move as long algebraic notation (e2e4):");

                let reader = io::stdin();
                let mut input_str = "".to_string();
                let legal_moves = board.all_legal_moves();
                // Loop until user enters a valid move
                loop {
                    input_str.clear();
                    reader.read_line(&mut input_str).expect("Failed to read line");

                    match B::Move::from_alg(input_str.trim()) {
                        Ok(val) => {
                            if legal_moves.contains(&val) { break; }
                            println!("Move {:?} is illegal! Legal moves: {:?}", val, legal_moves);
                            println!("Try again: ");
                        }

                        Err(error) => {
                            println!("{}, try again.", error);
                        },
                    }
                }
                let c_move =  B::Move::from_alg(input_str.trim()).unwrap();
                board.do_move(c_move);
                play_human(board);
            }
            else {
                let (handle, channel) = alpha_beta::start_uci_search(
                    board.clone(), uci::TimeRestriction::MoveTime(5000), uci::EngineOptions::new(),
                    Arc::new(Mutex::new(uci::EngineComm::new())), None);

                let (score, move_str) = uci::get_uci_move_checked(handle, channel).unwrap();
                let best_move = <B as EvalBoard>::Move::from_alg(&move_str);
                println!("Computer played {:?} with score {}", best_move, score);
                board.do_move(best_move.unwrap());
                play_human(board);
            }
        }

        Some(GameResult::WhiteWin) => println!("White won at move! Board:\n{:?}", board),
        Some(GameResult::BlackWin) => println!("Black won! Board:\n{:?}", board),
        Some(GameResult::Draw) => println!("The game was drawn! Board:\n{:?}", board),
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NodeCount {
    intern: u64,
    leaf: u64,
    total: u64,
}

impl NodeCount {
    fn new() -> Self {
        NodeCount { intern: 0, leaf: 0, total: 0 }
    }
}

impl std::ops::Add for NodeCount {
    type Output = NodeCount;
    fn add(self, other: NodeCount) -> Self {
        NodeCount { intern: self.intern + other.intern, leaf: self.leaf + other.leaf,
                    total: self.total + other.total }
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate openssl;
use clap::{Arg, App};
use openssl::error::ErrorStack;
use openssl::nid;
use openssl::string::OpensslString;
use openssl::x509::X509;

use std::io;
use std::io::Read;
use std::fs::File;

fn main() {
    let args = App::new("X.509 Parse")
        .about("Experiment in parsing X.509 certificates")
        .arg(Arg::with_name("InputFile")
            .long("file")
            .short("f")
            .takes_value(true)
            .required(true))
        .get_matches();

    let input_file_path = args.value_of("InputFile").unwrap();
    let cert_bytes = get_cert_bytes(input_file_path).unwrap();
    let cert = X509::from_pem(&cert_bytes).unwrap();

    println!("------------COMMON NAMES------------");
    let cert_common_names = get_subject_name(&cert);
    for cn in cert_common_names {
        match cn {
            Ok(cn) => println!("{:?}", cn),
            Err(err) => println!("{:?}", err)
        }
    }

    println!("------------SUBJECT ALT NAMES------------");
    let cert_subject_alt_names = get_subject_alt_names(&cert);
    match cert_subject_alt_names {
        None => println!("{:?}", "No SANs listed in certificate".to_string()),
        Some(sans) => {
            for san in sans {
                println!("{:?}", san)
            }
        }
    }
}

fn get_cert_bytes(input_file_path: &str) -> Result<Vec<u8>, io::Error> {
    let mut input_data = Vec::new();

    File::open(input_file_path)?.read_to_end(&mut input_data)?;

    Ok(input_data)
}

fn get_subject_name(cert: &X509) -> Vec<Result<OpensslString, ErrorStack>> {
    let entries = cert.subject_name();
    let common_name_entries = entries.entries_by_nid(nid::COMMONNAME);
    common_name_entries.map(|x| (*x).data().as_utf8()).collect()
}

fn get_subject_alt_names(cert: & X509) -> Option<Vec<String>> {
    let entries = cert.subject_alt_names();
    match entries {
        None => None,
        Some(e) => Some(e.into_iter()
            .filter_map(|x| x.dnsname().and_then(|x| Some(x.to_owned())))
            .filter_map(Option::Some)
            .collect())
    }
}<commit_msg>Successfully getting dates from cert as Asn1TimeRefs and parsing them to chrono datetimes<commit_after>extern crate chrono;
extern crate clap;
extern crate openssl;
use chrono::prelude::*;
use clap::{Arg, App};
use openssl::asn1::Asn1TimeRef;
use openssl::error::ErrorStack;
use openssl::nid;
use openssl::string::OpensslString;
use openssl::x509::X509;

use std::io;
use std::io::Read;
use std::fs::File;

fn main() {
    let args = App::new("X.509 Parse")
        .about("Experiment in parsing X.509 certificates")
        .arg(Arg::with_name("InputFile")
            .long("file")
            .short("f")
            .takes_value(true)
            .required(true))
        .get_matches();

    let input_file_path = args.value_of("InputFile").unwrap();
    let cert_bytes = get_cert_bytes(input_file_path).unwrap();
    let cert = X509::from_pem(&cert_bytes).unwrap();

    println!("------------COMMON NAMES------------");
    let cert_common_names = get_subject_name(&cert);
    for cn in cert_common_names {
        match cn {
            Ok(cn) => println!("{:?}", cn),
            Err(err) => println!("{:?}", err)
        }
    }

    println!("------------SUBJECT ALT NAMES------------");
    let cert_subject_alt_names = get_subject_alt_names(&cert);
    match cert_subject_alt_names {
        None => println!("{:?}", "No SANs listed in certificate".to_string()),
        Some(sans) => {
            for san in sans {
                println!("{:?}", san)
            }
        }
    }

    println!("------------Validity Dates------------");
    let validity_dates = get_validity_dates(&cert);
    println!("Not before: {}", validity_dates.0);
    println!("Not after: {}", validity_dates.1);

    let not_before = convert_Asn1TimeRef_to_DateTimeUTC(validity_dates.0);
    match not_before {
        Err(e) => println!("Error parsing not_before date: {}", e),
        Ok(not_before) => println!("Not before: {:?}", not_before)
    }

    let not_after = convert_Asn1TimeRef_to_DateTimeUTC(validity_dates.1);
    match not_after {
        Err(e) => println!("Error parsing not_after date: {}", e),
        Ok(not_after) => println!("Not after: {:?}", not_after)
    }
}

fn get_cert_bytes(input_file_path: &str) -> Result<Vec<u8>, io::Error> {
    let mut input_data = Vec::new();

    File::open(input_file_path)?.read_to_end(&mut input_data)?;

    Ok(input_data)
}

fn get_subject_name(cert: &X509) -> Vec<Result<OpensslString, ErrorStack>> {
    let entries = cert.subject_name();
    let common_name_entries = entries.entries_by_nid(nid::COMMONNAME);
    common_name_entries.map(|x| (*x).data().as_utf8()).collect()
}

fn get_subject_alt_names(cert: & X509) -> Option<Vec<String>> {
    let entries = cert.subject_alt_names();
    match entries {
        None => None,
        Some(e) => Some(e.into_iter()
            .filter_map(|x| x.dnsname().and_then(|x| Some(x.to_owned())))
            .filter_map(Option::Some)
            .collect())
    }
}

fn get_validity_dates<'a>(cert: &'a X509) -> (&'a Asn1TimeRef, &'a Asn1TimeRef) {
    let not_before = cert.not_before();
    let not_after = cert.not_after();
    (not_before, not_after)
}

fn convert_Asn1TimeRef_to_DateTimeUTC(input: & Asn1TimeRef) -> Result<chrono::DateTime<Utc>, chrono::ParseError> {
    use std::fmt::Write;

    let mut stringified = String::new();
    let result = write!(&mut stringified, "{}", input);
    if result.is_err() {
        panic!("Error writing Asn1TimeRef to string");
    }
    stringified = stringified.replace("GMT", "+00:00");

    let output = DateTime::parse_from_str(& stringified, "%b %d %H:%M:%S %Y %z")?;

    Ok(output.with_timezone(&Utc))
}<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate reqwest;

use clap::{Arg, App};
use reqwest::header::UserAgent;
use std::fs::File;
use std::io;
use std::io::BufWriter;
use std::io::prelude::*;
use std::path::Path;
use std::process;

static DEFAULT_USER_AGENT: &'static str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));

enum ExitStatus {
    UrlFailure = 1,
    OutputFailure = 2,
}

fn exit(exit_status: ExitStatus) -> ! {
    process::exit(exit_status as i32);
}

fn get_filename(url: &str) -> Option<&str> {
    url.rsplit('/').next()
}

fn main() {
    let app = App::new("download")
                  .version(crate_version!())
                  .about("remote file downloader command-line interface")
                  .arg(Arg::with_name("output")
                           .short("o")
                           .long("output")
                           .value_name("OUTPUT")
                           .help("output filename")
                           .takes_value(true))
                  .arg(Arg::with_name("remote-name")
                           .short("O")
                           .long("remote-name")
                           .help("output to a file using the same name as the remote"))
                  .arg(Arg::with_name("user-agent")
                           .short("U")
                           .long("user-agent")
                           .takes_value(true)
                           .help("use value as user-agent header"))
                  .arg(Arg::with_name("url").required(true));
    let args = app.get_matches();
    let source_url = args.value_of("url").unwrap();
    let user_agent = args.value_of("user-agent")
                         .unwrap_or(DEFAULT_USER_AGENT);

    let mut client = reqwest::Client::new().unwrap();
    client.gzip(true);
    // TODO: add redirect following options
    client.redirect(reqwest::RedirectPolicy::limited(1));
    let mut res = client.get(source_url)
                        .header(UserAgent(user_agent.to_owned()))
                        .send()
                        .unwrap_or_else(|e| {
                          let _ = writeln!(&mut std::io::stderr(), "{}", e);
                          process::exit(ExitStatus::UrlFailure as i32)
                        });

    // determine an output filename; if none are set then send to stdout
    let output_filename = {
        if args.is_present("remote-name") {
            get_filename(source_url)
        } else {
            args.value_of("output")
        }
    };

    if let Some(fname) = output_filename {
        let _ = File::create(Path::new(fname))
            .and_then(|output_file| {
                let mut writer = BufWriter::new(output_file);
                io::copy(&mut res, &mut writer)?;
                writer.flush()?;
                Ok(())
            })
            .map_err(|e| {
                let _ = writeln!(&mut std::io::stderr(), "{}", e);
                exit(ExitStatus::OutputFailure);
            });
    } else {
        io::copy(&mut res, &mut io::stdout())
            .unwrap_or_else(|e| {
                let _ = writeln!(&mut std::io::stderr(), "{}", e);
                exit(ExitStatus::OutputFailure)
            });
    }

    ()
}
<commit_msg>Fixing short option for user-agent<commit_after>#[macro_use]
extern crate clap;
extern crate reqwest;

use clap::{Arg, App};
use reqwest::header::UserAgent;
use std::fs::File;
use std::io;
use std::io::BufWriter;
use std::io::prelude::*;
use std::path::Path;
use std::process;

static DEFAULT_USER_AGENT: &'static str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));

enum ExitStatus {
    UrlFailure = 1,
    OutputFailure = 2,
}

fn exit(exit_status: ExitStatus) -> ! {
    process::exit(exit_status as i32);
}

fn get_filename(url: &str) -> Option<&str> {
    url.rsplit('/').next()
}

fn main() {
    let app = App::new("download")
                  .version(crate_version!())
                  .about("remote file downloader command-line interface")
                  .arg(Arg::with_name("output")
                           .short("o")
                           .long("output")
                           .value_name("OUTPUT")
                           .help("output filename")
                           .takes_value(true))
                  .arg(Arg::with_name("remote-name")
                           .short("O")
                           .long("remote-name")
                           .help("output to a file using the same name as the remote"))
                  .arg(Arg::with_name("user-agent")
                           .short("A")
                           .long("user-agent")
                           .takes_value(true)
                           .help("use value as user-agent header"))
                  .arg(Arg::with_name("url").required(true));
    let args = app.get_matches();
    let source_url = args.value_of("url").unwrap();
    let user_agent = args.value_of("user-agent")
                         .unwrap_or(DEFAULT_USER_AGENT);

    let mut client = reqwest::Client::new().unwrap();
    client.gzip(true);
    // TODO: add redirect following options
    client.redirect(reqwest::RedirectPolicy::limited(1));
    let mut res = client.get(source_url)
                        .header(UserAgent(user_agent.to_owned()))
                        .send()
                        .unwrap_or_else(|e| {
                          let _ = writeln!(&mut std::io::stderr(), "{}", e);
                          process::exit(ExitStatus::UrlFailure as i32)
                        });

    // determine an output filename; if none are set then send to stdout
    let output_filename = {
        if args.is_present("remote-name") {
            get_filename(source_url)
        } else {
            args.value_of("output")
        }
    };

    if let Some(fname) = output_filename {
        let _ = File::create(Path::new(fname))
            .and_then(|output_file| {
                let mut writer = BufWriter::new(output_file);
                io::copy(&mut res, &mut writer)?;
                writer.flush()?;
                Ok(())
            })
            .map_err(|e| {
                let _ = writeln!(&mut std::io::stderr(), "{}", e);
                exit(ExitStatus::OutputFailure);
            });
    } else {
        io::copy(&mut res, &mut io::stdout())
            .unwrap_or_else(|e| {
                let _ = writeln!(&mut std::io::stderr(), "{}", e);
                exit(ExitStatus::OutputFailure)
            });
    }

    ()
}
<|endoftext|>