Artifact Content
Not logged in

Artifact 9f9572db47bf01059750341dc7bafffa03d593df:


// Copyright 2015 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License,
// version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which
// licence you accepted on initial access to the Software (the "Licences").
//
// By contributing code to the SAFE Network Software, or to this project generally, you agree to be
// bound by the terms of the MaidSafe Contributor Agreement.  This, along with the Licenses can be
// found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR.
//
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
//
// Please review the Licences for the specific language governing permissions and limitations
// relating to use of the SAFE Network Software.

//! Email Stress Test

// For explanation of lint checks, run `rustc -W help` or see
// https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md
#![forbid(exceeding_bitshifts, mutable_transmutes, no_mangle_const_items,
          unknown_crate_types, warnings)]
#![deny(bad_style, deprecated, improper_ctypes, missing_docs,
        non_shorthand_field_patterns, overflowing_literals, plugin_as_library,
        private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion,
        unknown_lints, unused, unused_allocation, unused_attributes,
        unused_comparisons, unused_features, unused_parens, while_true)]
#![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]
#![allow(box_pointers, fat_ptr_transmutes, missing_copy_implementations,
         missing_debug_implementations, variant_size_differences)]

#[macro_use]
extern crate log;
#[macro_use]
extern crate unwrap;

extern crate crossbeam;
extern crate docopt;
extern crate rand;
extern crate rust_sodium;
extern crate rustc_serialize;
extern crate safe_core;

use docopt::Docopt;
use rand::{Rng, SeedableRng, XorShiftRng};
use rust_sodium::crypto::hash::sha256::{self, Digest};
use safe_core::ffi::app::*;
use safe_core::ffi::logging::*;
use safe_core::ffi::low_level_api::{AppendableDataHandle, CipherOptHandle};
use safe_core::ffi::low_level_api::appendable_data::*;
use safe_core::ffi::low_level_api::cipher_opt::*;
use safe_core::ffi::low_level_api::data_id::*;
use safe_core::ffi::low_level_api::immut_data::*;
use safe_core::ffi::low_level_api::misc::*;
use safe_core::ffi::session::*;
use std::{ptr, slice};
use std::sync::Mutex;
use std::time::Instant;

static USAGE: &'static str = "
Usage:
  email_stress_test [options]

Options:
  --seed <seed>  Seed for a pseudo-random number generator.
  --get-only     Only Get the data, don't Put it.
  -h, --help     Display this help message and exit.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_seed: Option<u32>,
    flag_get_only: bool,
    flag_help: bool,
}

const BOTS: usize = 5;
const MSGS_SENT_BY_EACH_BOT: usize = 10;

struct Bot {
    app_h: *mut App,
    session_h: *mut SessionHandle,
    email: String,
    tx_msgs: Vec<Vec<u8>>,
}

impl Bot {
    fn new(n: usize, rng: &mut XorShiftRng, account_exists: bool) -> Self {
        let mut app_h: *mut App = ptr::null_mut();
        let mut session_h: *mut SessionHandle = ptr::null_mut();

        let mut sec_0: String = rng.gen_iter::<char>().take(10).collect();
        let mut sec_1: String = rng.gen_iter::<char>().take(10).collect();
        let mut invitation: String = rng.gen_iter::<char>().take(10).collect();

        sec_0.push_str(&n.to_string());
        sec_1.push_str(&n.to_string());
        invitation.push_str(&n.to_string());

        if account_exists {
            unsafe {
                assert_eq!(log_in(sec_0.as_bytes().as_ptr(),
                                  sec_0.as_bytes().len(),
                                  sec_1.as_bytes().as_ptr(),
                                  sec_1.as_bytes().len(),
                                  &mut session_h),
                           0);
            }
        } else {
            unsafe {
                assert_eq!(create_account(sec_0.as_bytes().as_ptr(),
                                          sec_0.as_bytes().len(),
                                          sec_1.as_bytes().as_ptr(),
                                          sec_1.as_bytes().len(),
                                          invitation.as_bytes().as_ptr(),
                                          invitation.as_bytes().len(),
                                          &mut session_h),
                           0);
            }
        }

        let app_name = format!("Bot-{}", n);
        let unique_token = format!("Bot-{}", n);
        let vendor = "MaidSafe".to_string();

        unsafe {
            assert_eq!(register_app(session_h,
                                    app_name.as_bytes().as_ptr(),
                                    app_name.as_bytes().len(),
                                    unique_token.as_bytes().as_ptr(),
                                    unique_token.as_bytes().len(),
                                    vendor.as_bytes().as_ptr(),
                                    vendor.as_bytes().len(),
                                    false,
                                    &mut app_h),
                       0);
        }

        let prefix: String = rng.gen_iter::<char>().take(10).collect();
        let email = format!("{}-Bot-{}-mail", prefix, n);

        let tx_msgs = (0..MSGS_SENT_BY_EACH_BOT)
            .map(|x| {
                     let mut msg: Vec<_> = rng.gen_iter::<u8>().take(10).collect();
                     msg.extend(format!("Bot-{}-msg-{}", n, x).into_bytes());
                     msg
                 })
            .collect();

        Bot {
            app_h: app_h,
            session_h: session_h,
            email: email,
            tx_msgs: tx_msgs,
        }
    }

    fn create_email(&self) {
        let Digest(digest) = sha256::hash(self.email.as_bytes());

        let mut ad_h = 0;
        unsafe {
            assert_eq!(appendable_data_new_priv(self.app_h, &digest, &mut ad_h), 0);
            assert_eq!(appendable_data_put(self.app_h, ad_h), 0);
        }
        assert_eq!(appendable_data_free(ad_h), 0);
    }

    fn get_peer_email_handles(&self, peer_email: &str) -> (AppendableDataHandle, CipherOptHandle) {
        let Digest(digest) = sha256::hash(peer_email.as_bytes());
        let mut data_id_h = 0;
        unsafe {
            assert_eq!(data_id_new_appendable_data(&digest, true, &mut data_id_h),
                       0);
        }

        let mut ad_h = 0;
        unsafe {
            assert_eq!(appendable_data_get(self.app_h, data_id_h, &mut ad_h), 0);
        }
        assert_eq!(data_id_free(data_id_h), 0);

        let mut cipher_opt_h = 0;
        let mut encrypt_key_h = 0;
        unsafe {
            assert_eq!(appendable_data_encrypt_key(ad_h, &mut encrypt_key_h), 0);
            assert_eq!(cipher_opt_new_asymmetric(encrypt_key_h, &mut cipher_opt_h),
                       0);
        }
        assert_eq!(misc_encrypt_key_free(encrypt_key_h), 0);

        (ad_h, cipher_opt_h)
    }

    fn send_email(&self, peer_ad_h: u64, cipher_opt_h: u64, msg: &[u8]) {
        let mut se_h = 0;
        unsafe {
            assert_eq!(immut_data_new_self_encryptor(self.app_h, &mut se_h), 0);
            assert_eq!(immut_data_write_to_self_encryptor(se_h, msg.as_ptr(), msg.len()),
                       0);
        }

        let mut data_id_h = 0;
        unsafe {
            assert_eq!(immut_data_close_self_encryptor(self.app_h,
                                                       se_h,
                                                       cipher_opt_h,
                                                       &mut data_id_h),
                       0);
            assert_eq!(appendable_data_append(self.app_h, peer_ad_h, data_id_h), 0);
        }

        assert_eq!(data_id_free(data_id_h), 0);
        assert_eq!(immut_data_self_encryptor_writer_free(se_h), 0);
    }

    fn get_all_emails(&self) -> Vec<Vec<u8>> {
        let Digest(digest) = sha256::hash(self.email.as_bytes());

        let mut data_id_h = 0;
        unsafe {
            assert_eq!(data_id_new_appendable_data(&digest, true, &mut data_id_h),
                       0);
        }

        let mut ad_h = 0;
        unsafe {
            assert_eq!(appendable_data_get(self.app_h, data_id_h, &mut ad_h), 0);
        }
        assert_eq!(data_id_free(data_id_h), 0);

        let mut num_of_emails = 0;
        unsafe {
            assert_eq!(appendable_data_num_of_data(ad_h, &mut num_of_emails), 0);
        }

        let mut rx_msgs = Vec::with_capacity(num_of_emails);

        for n in 0..num_of_emails {
            unsafe {
                assert_eq!(appendable_data_nth_data_id(self.app_h, ad_h, n, &mut data_id_h),
                           0);
            }

            let mut se_h = 0;
            unsafe {
                assert_eq!(immut_data_fetch_self_encryptor(self.app_h, data_id_h, &mut se_h),
                           0);
            }

            let mut total_size = 0;
            unsafe {
                assert_eq!(immut_data_size(se_h, &mut total_size), 0);
            }

            let mut data_ptr: *mut u8 = ptr::null_mut();
            let mut read_size = 0;
            let mut capacity = 0;
            unsafe {
                assert_eq!(immut_data_read_from_self_encryptor(se_h,
                                                               0,
                                                               total_size,
                                                               &mut data_ptr,
                                                               &mut read_size,
                                                               &mut capacity),
                           0);
            }

            // TODO Confirm that cloning is done - else this is UB as we are freeing the vector
            // separately.
            let rx_msg = unsafe { slice::from_raw_parts(data_ptr, read_size).to_owned() };
            rx_msgs.push(rx_msg);

            assert_eq!(data_id_free(data_id_h), 0);
            assert_eq!(immut_data_self_encryptor_reader_free(se_h), 0);
            unsafe {
                misc_u8_ptr_free(data_ptr, read_size, capacity);
            }
        }

        assert_eq!(appendable_data_free(ad_h), 0);

        rx_msgs
    }
}

impl Drop for Bot {
    fn drop(&mut self) {
        unsafe {
            drop_app(self.app_h);
            drop_session(self.session_h);
        }
    }
}

unsafe impl Send for Bot {}
unsafe impl Sync for Bot {}

fn main() {
    // Sample timmings in release run with mock-routing and cleared VaultStorageSimulation:
    // ------------------------------------------------------------------------------------
    // Create accounts for 5 bots: 3 secs, 0 millis
    // Create emails for 5 bots: 0 secs, 218 millis
    // Send total of 200 emails by 5 bots: 23 secs, 71 millis
    // Read total of 200 emails by 5 bots: 0 secs, 30 millis
    //
    // Sample timmings in release run with actual-routing:
    // ------------------------------------------------------------------------------------
    // Create accounts for 5 bots: 27 secs, 0 millis
    // Create emails for 5 bots: 0 secs, 411 millis
    // Send total of 200 emails by 5 bots: 26 secs, 415 millis
    // Read total of 200 emails by 5 bots: 6 secs, 273 millis
    // ------------------------------------------------------------------------------------
    assert_eq!(init_logging(), 0);

    let args: Args = Docopt::new(USAGE)
        .and_then(|docopt| docopt.decode())
        .unwrap_or_else(|error| error.exit());

    let mut rng = XorShiftRng::from_seed(match args.flag_seed {
                                             Some(seed) => [0, 0, 0, seed],
                                             None => {
                                                 [rand::random(),
                                                  rand::random(),
                                                  rand::random(),
                                                  rand::random()]
                                             }
                                         });

    // ------------------------------------------------------------------------
    // Create bots
    let mut now = Instant::now();
    let bots: Vec<_> = (0..BOTS)
        .map(|n| Bot::new(n, &mut rng, args.flag_get_only))
        .collect();
    let mut duration = now.elapsed();
    info!("Create accounts for {} bots: {} secs, {} millis\n",
          BOTS,
          duration.as_secs(),
          duration.subsec_nanos() / 1000000);

    if !args.flag_get_only {
        // ------------------------------------------------------------------------
        // Create email in parallel
        now = Instant::now();
        crossbeam::scope(|scope| for bot in &bots {
                             let _ = scope.spawn(move || bot.create_email());
                         });
        duration = now.elapsed();
        info!("Create emails for {} bots: {} secs, {} millis\n",
              BOTS,
              duration.as_secs(),
              duration.subsec_nanos() / 1000000);

        // ------------------------------------------------------------------------
        // Send emails
        now = Instant::now();
        for (i, bot) in bots.iter().enumerate() {
            let peer_handles = Mutex::new(Vec::with_capacity(BOTS - 1));
            let peer_handles_ref = &peer_handles;

            // Get peer emails in parallel
            crossbeam::scope(|scope| for (j, peer_bot) in bots.iter().enumerate() {
                                 if i == j {
                                     continue;
                                 }
                                 let _ = scope.spawn(move || {
                                                         unwrap!(peer_handles_ref.lock())
                                            .push(bot.get_peer_email_handles(&peer_bot.email))
                                                     });
                             });

            // Send each email-msg from a bot in parallel to all others
            for msg in &bot.tx_msgs {
                let guard = unwrap!(peer_handles.lock());
                crossbeam::scope(|scope| for &(ad_h, cipher_opt_h) in &*guard {
                                     let job = move || bot.send_email(ad_h, cipher_opt_h, msg);
                                     let _ = scope.spawn(job);
                                 })
            }

            let guard = unwrap!(peer_handles.lock());
            for &(ad_h, cipher_opt_h) in &*guard {
                assert_eq!(appendable_data_free(ad_h), 0);
                assert_eq!(cipher_opt_free(cipher_opt_h), 0);
            }
        }
        duration = now.elapsed();
        info!("Sent total of {} emails by {} bots: {} secs, {} millis\n",
              MSGS_SENT_BY_EACH_BOT * (BOTS - 1) * BOTS,
              BOTS,
              duration.as_secs(),
              duration.subsec_nanos() / 1000000);
    }

    // ------------------------------------------------------------------------
    // Read and verify all emails by all bots in parallel
    now = Instant::now();
    crossbeam::scope(|scope| {
        let bots_ref = &bots;

        for (i, bot) in bots_ref.iter().enumerate() {
            let _ = scope.spawn(move || {
                let mut rx_emails = bot.get_all_emails();
                assert_eq!(rx_emails.len(), MSGS_SENT_BY_EACH_BOT * (BOTS - 1));

                for (j, peer_bot) in bots_ref.iter().enumerate() {
                    if i == j {
                        continue;
                    }
                    for tx_msg in &peer_bot.tx_msgs {
                        let pos = unwrap!(rx_emails
                                              .iter()
                                              .position(|rx_email| *rx_email == *tx_msg));
                        let _ = rx_emails.remove(pos);
                    }
                }
            });
        }
    });
    duration = now.elapsed();
    info!("Read total of {} emails by {} bots: {} secs, {} millis\n",
          MSGS_SENT_BY_EACH_BOT * (BOTS - 1) * BOTS,
          BOTS,
          duration.as_secs(),
          duration.subsec_nanos() / 1000000);

}