rust-abci 0.1.0

An implementation of the ABCI protocol used by Tendermint Core. Allows the building of ABCI apps in Rust.
Documentation
#![allow(unused_variables)]
#![allow(unused_must_use)]

extern crate byteorder;
extern crate grpc;
extern crate rust_abci;


use rust_abci::new_server;
use rust_abci::types::*;
use rust_abci::types_grpc::*;


use std::sync::Mutex;


use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};


struct CounterApp {
    serial: Mutex<bool>,
    tx_count: Mutex<u64>,
    hash_count: Mutex<u64>,
}

impl CounterApp {
    fn new(serial: bool) -> CounterApp {
        CounterApp {
            serial: Mutex::new(serial),
            tx_count: Mutex::new(0),
            hash_count: Mutex::new(0),
        }
    }
}

impl ABCIApplication for CounterApp {
    fn echo(&self, o: ::grpc::RequestOptions, p: RequestEcho) -> ::grpc::SingleResponse<ResponseEcho> {
        let echo = p.get_message();
        let mut response = ResponseEcho::new();
        response.set_message(echo.to_owned());
        ::grpc::SingleResponse::completed(response)
    }

    fn flush(&self, o: ::grpc::RequestOptions, p: RequestFlush) -> ::grpc::SingleResponse<ResponseFlush> {
        unimplemented!();
    }

    fn info(&self, o: ::grpc::RequestOptions, p: RequestInfo) -> ::grpc::SingleResponse<ResponseInfo> {
        let mut response = ResponseInfo::new();
        response.set_data("CounterApp".to_owned());
        response.set_version("0.1.0".to_owned());
        ::grpc::SingleResponse::completed(response)
    }

    fn set_option(&self, o: ::grpc::RequestOptions, p: RequestSetOption) -> ::grpc::SingleResponse<ResponseSetOption> {
        let mut response = ResponseSetOption::new();
        if p.get_key() == "serial" && p.get_value() == "on" {
            let mut serial = self.serial.lock().unwrap();
            *serial = true;
            response.set_log("Serial set to ON".to_owned());
        }
        ::grpc::SingleResponse::completed(response)
    }

    fn deliver_tx(&self, o: ::grpc::RequestOptions, p: RequestDeliverTx) -> ::grpc::SingleResponse<ResponseDeliverTx> {
        let mut response = ResponseDeliverTx::new();
        if *self.serial.lock().unwrap() {
            if p.get_tx().len() > 8 {
                response.set_code(CodeType::EncodingError);
                response.set_log("Max tx size is 8 bytes".to_owned());
                return ::grpc::SingleResponse::completed(response);
            }
        }
        let nonce = p.get_tx().read_uint::<BigEndian>(p.get_tx().len()).unwrap();
        if *self.tx_count.lock().unwrap() != nonce {
            response.set_code(CodeType::BadNonce);
            response.set_log("Invalid nonce.".to_owned());
            return ::grpc::SingleResponse::completed(response);
        }
        let mut tx_count = self.tx_count.lock().unwrap();
        *tx_count += 1;
        response.set_code(CodeType::OK);
        ::grpc::SingleResponse::completed(response)
    }

    fn check_tx(&self, o: ::grpc::RequestOptions, p: RequestCheckTx) -> ::grpc::SingleResponse<ResponseCheckTx> {
        let mut response = ResponseCheckTx::new();
        if *self.serial.lock().unwrap() {
            if p.get_tx().len() > 8 {
                response.set_code(CodeType::EncodingError);
                response.set_log("Max tx size is 8 bytes".to_owned());
                return ::grpc::SingleResponse::completed(response);
            }
        }
        let nonce = p.get_tx().read_uint::<BigEndian>(p.get_tx().len()).unwrap();
        if *self.tx_count.lock().unwrap() != nonce {
            response.set_code(CodeType::BadNonce);
            response.set_log("Invalid nonce.".to_owned());
            return ::grpc::SingleResponse::completed(response);
        }
        response.set_code(CodeType::OK);
        ::grpc::SingleResponse::completed(response)
    }

    fn query(&self, o: ::grpc::RequestOptions, p: RequestQuery) -> ::grpc::SingleResponse<ResponseQuery> {
        let mut response = ResponseQuery::new();
        match p.get_path() {
            "hash" => {
                let mut data = vec![];
                data.write_uint::<BigEndian>(*self.hash_count.lock().unwrap(), 8);
                response.set_value(data);
                return ::grpc::SingleResponse::completed(response);
            },
            "tx" => {
                let mut data = vec![];
                data.write_uint::<BigEndian>(*self.tx_count.lock().unwrap(), 8);
                response.set_value(data);
                return ::grpc::SingleResponse::completed(response);
            },
            _ => {
                response.set_log("Invalid query path. Expected hash or tx.".to_owned());
                return ::grpc::SingleResponse::completed(response);
            },
        }
    }

    fn commit(&self, o: ::grpc::RequestOptions, p: RequestCommit) -> ::grpc::SingleResponse<ResponseCommit> {
        let mut response = ResponseCommit::new();

        let mut hash_count = self.hash_count.lock().unwrap();
        *hash_count += 1;

        if *self.tx_count.lock().unwrap() == 0 {
            response.set_code(CodeType::OK);
            return ::grpc::SingleResponse::completed(response);
        }

        let mut data = vec![];
        data.write_uint::<BigEndian>(*self.tx_count.lock().unwrap(), 8);
        response.set_data(data);
        ::grpc::SingleResponse::completed(response)
    }

    fn init_chain(&self, o: ::grpc::RequestOptions, p: RequestInitChain) -> ::grpc::SingleResponse<ResponseInitChain> {
        let response = ResponseInitChain::new();
        ::grpc::SingleResponse::completed(response)
    }

    fn begin_block(&self, o: ::grpc::RequestOptions, p: RequestBeginBlock) -> ::grpc::SingleResponse<ResponseBeginBlock> {
        let response = ResponseBeginBlock::new();
        ::grpc::SingleResponse::completed(response)
    }

    fn end_block(&self, o: ::grpc::RequestOptions, p: RequestEndBlock) -> ::grpc::SingleResponse<ResponseEndBlock> {
        let response = ResponseEndBlock::new();
        ::grpc::SingleResponse::completed(response)
    }
}

fn main() {
    use std::env;
    use std::thread;

    let args: Vec<String> = env::args().collect();
    let connection_type: &str = &args[1];
    let listen_addr: &str = &args[2];

    let app = CounterApp::new(true);

    let _server = new_server(listen_addr, connection_type, app);

    loop {
        thread::park();
    }
}