use lox_cli::{self, networking::HyperNet, *}; use getopts::Options; use lox_library::{ bridge_table::{BridgeLine, MAX_BRIDGES_PER_BUCKET}, scalar_u32, IssuerPubKey, }; use serde::Serialize; use std::{env::args, fs::File, io::Write, path::Path}; // Prints the argument details for this program fn print_usage(program: &str, opts: Options) { let brief = format!("Usage: {} [options]", program); print!("{}", opts.usage(&brief)); } // Helper function to save serializable objects to files fn save_object(obj: T, filename: &str) { let mut outfile = File::create(filename).expect(&("Failed to create ".to_string() + filename)); write!(outfile, "{}", serde_json::to_string(&obj).unwrap()) .expect(&("Failed to write to ".to_string() + filename)); } #[tokio::main] async fn main() { let args: Vec = args().collect(); // files used to store various data let bucket_filename = "bucket.json"; let invite_filename = "invite.json"; let lox_auth_pubkeys_filename = "lox_auth_pubkeys.json"; let lox_cred_filename = "lox_cred.json"; let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); opts.optflag("I", "invite", "generate invitation for a friend"); opts.optflag("L", "level-up", "increase trust level"); opts.optflag("N", "new-lox-cred", "get a new Lox Credential"); opts.optopt("R", "redeem", "redeem invitation", "INVITE_FILE"); opts.optopt( "", "server", "Lox Auth server address [http://localhost:8001]", "ADDR", ); let matches = match opts.parse(&args[1..]) { Ok(m) => m, Err(f) => { panic!("{}", f.to_string()) } }; if matches.opt_present("h") { print_usage(&args[0], opts); return; } let net = if matches.opt_present("server") { HyperNet { hostname: matches.opt_str("server").unwrap(), } } else { HyperNet { hostname: "http://localhost:8001".to_string(), } }; // Get Lox Authority public keys let lox_auth_pubkeys: Vec = if Path::new(lox_auth_pubkeys_filename).exists() { // read in file let lox_auth_pubkeys_infile = File::open(lox_auth_pubkeys_filename).unwrap(); serde_json::from_reader(lox_auth_pubkeys_infile).unwrap() } else { // download from Lox Auth let pubkeys = get_lox_auth_keys(&net).await.unwrap(); // save to file for next time save_object(&pubkeys, &lox_auth_pubkeys_filename); pubkeys }; // Get Lox Credential and Bucket let (lox_cred, bucket) = if matches.opt_present("R") && Path::new(invite_filename).exists() { // redeem invite let invite_infile = File::open(invite_filename).unwrap(); let invite = serde_json::from_reader(invite_infile).unwrap(); let (cred, bucket) = redeem_invite( &net, &invite, get_lox_pub(&lox_auth_pubkeys), get_invitation_pub(&lox_auth_pubkeys), ) .await .unwrap(); // save to files for future use save_object(&cred, &lox_cred_filename); save_object(&bucket, &bucket_filename); (cred, bucket) } else if matches.opt_present("N") || !Path::new(lox_cred_filename).exists() || !Path::new(bucket_filename).exists() { // get new Lox Credential let open_invite = get_open_invitation(&net).await.unwrap(); let (cred, bl) = get_lox_credential(&net, &open_invite, get_lox_pub(&lox_auth_pubkeys)) .await .unwrap(); let mut bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET]; // note: this is a bucket with one real bridgeline and n-1 // default (zeroed out) bridgelines bucket[0] = bl; // save to files for future use save_object(&cred, &lox_cred_filename); save_object(&bucket, &bucket_filename); (cred, bucket) } else { // Read existing Lox Credential and BridgeLine from files let cred = serde_json::from_reader(File::open(lox_cred_filename).unwrap()).unwrap(); let bucket = serde_json::from_reader(File::open(bucket_filename).unwrap()).unwrap(); (cred, bucket) }; let (lox_cred, bucket) = if matches.opt_present("L") { let old_level = scalar_u32(&lox_cred.trust_level).unwrap(); // If trust level is 0, do trust promotion, otherwise level up. let (cred, bucket) = if old_level == 0 { if eligible_for_trust_promotion(&net, &lox_cred).await { let migration_cred = trust_promotion(&net, &lox_cred, get_lox_pub(&lox_auth_pubkeys)) .await .unwrap(); let cred = trust_migration( &net, &lox_cred, &migration_cred, get_lox_pub(&lox_auth_pubkeys), get_migration_pub(&lox_auth_pubkeys), ) .await .unwrap(); let bucket = get_bucket(&net, &cred).await.unwrap().0; (cred, bucket) } else { (lox_cred, bucket) } } else { if eligible_for_level_up(&net, &lox_cred).await { let (bucket, reachcred) = get_bucket(&net, &lox_cred).await.unwrap(); let cred = level_up( &net, &lox_cred, &reachcred.unwrap(), get_lox_pub(&lox_auth_pubkeys), get_reachability_pub(&lox_auth_pubkeys), ) .await .unwrap(); let bucket = get_bucket(&net, &lox_cred).await.unwrap().0; (cred, bucket) } else { (lox_cred, bucket) } }; save_object(&cred, &lox_cred_filename); save_object(&bucket, &bucket_filename); let new_level = scalar_u32(&cred.trust_level).unwrap(); if new_level > old_level { println!("Old level: {}\nNew level: {}", old_level, new_level); } else if new_level == old_level { println!("Unable to level up. Current level: {}", new_level); } (cred, bucket) } else { (lox_cred, bucket) }; // Invite a friend let lox_cred = if matches.opt_present("I") { if scalar_u32(&lox_cred.invites_remaining).unwrap() > 0 { let (cred, invite) = issue_invite( &net, &lox_cred, &get_reachability_credential(&net).await.unwrap(), get_lox_pub(&lox_auth_pubkeys), get_reachability_pub(&lox_auth_pubkeys), get_invitation_pub(&lox_auth_pubkeys), ) .await .unwrap(); // TODO: Make this unique per-run (e.g., add timestamp) save_object(&invite, &invite_filename); save_object(&cred, &lox_cred_filename); println!("Invite saved in {}", &invite_filename); println!( "Invites left: {}", scalar_u32(&cred.invites_remaining).unwrap() ); cred } else { println!("No invites left"); lox_cred } } else { lox_cred }; }