Skip to content

Commit

Permalink
Rita exits talking to exit root server for exit list
Browse files Browse the repository at this point in the history
  • Loading branch information
ch-iara committed Oct 11, 2024
1 parent 118127f commit 076305b
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 32 deletions.
34 changes: 15 additions & 19 deletions exit_trust_root/src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use config::{load_config, CONFIG};
use crypto_box::aead::{Aead, AeadCore, OsRng};
use crypto_box::{PublicKey, SalsaBox, SecretKey};
use env_logger::Env;
use lazy_static::lazy_static;
use log::info;
use rita_client_registration::client_db::get_exits_list;
use rustls::ServerConfig;
Expand All @@ -33,27 +32,22 @@ pub const DOMAIN: &str = if cfg!(test) || DEVELOPMENT {
/// The backend RPC port for the info server fucntions implemented in this repo
const SERVER_PORT: u16 = 9000;

lazy_static! {
static ref EXIT_CONTRACT_CACHE: Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>> =
Arc::new(RwLock::new(HashMap::new()));
}

/// This endpoint retrieves and signs the data from any specified exit contract,
/// allowing this server to serve as a root of trust for several different exit contracts.
#[get("/{exit_contract}")]
async fn return_exit_contract_data(
exit_contract: web::Path<Address>,
pubkey: web::Data<[u8; 32]>,
cache: web::Data<Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>>>,
) -> impl Responder {
let contract = exit_contract.into_inner();
let cache = EXIT_CONTRACT_CACHE.read().unwrap().clone();
match cache.get(&contract) {
match cache.read().unwrap().get(&contract) {
Some(cache) => {
// return an encrypted exit server list based on the given key
HttpResponse::Ok().json(cache.to_encrypted_exit_server_list((*pubkey.get_ref()).into()))
}
None => {
match retrieve_exit_server_list(contract).await {
match retrieve_exit_server_list(contract, cache.get_ref().clone()).await {
Ok(cache_value) => {
// encrypt and return
return HttpResponse::Ok().json(
Expand All @@ -72,6 +66,7 @@ async fn return_exit_contract_data(

async fn retrieve_exit_server_list(
exit_contract: Address,
cache: Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>>,
) -> Result<ExitContractSignatureCacheValue, Web3Error> {
const WEB3_TIMEOUT: Duration = Duration::from_secs(10);
let exits = get_exits_list(
Expand All @@ -94,7 +89,7 @@ async fn retrieve_exit_server_list(
nonce: nonce.into(),
};
// add this new exit to the cache
EXIT_CONTRACT_CACHE
cache
.write()
.unwrap()
.insert(exit_contract, cache_value.clone());
Expand Down Expand Up @@ -138,19 +133,18 @@ const SIGNATURE_UPDATE_SLEEP: Duration = Duration::from_secs(300);
/// In order to improve scalability this loop grabs and signs an updated list of exits from each exit contract
/// that has previously been requested from this server every 5 minutes. This allows the server to return instantly
/// on the next request from the client without having to perform rpc query 1-1 with requests.
fn signature_update_loop() {
fn signature_update_loop(cache: Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>>) {
thread::spawn(move || loop {
let runner = System::new();
runner.block_on(async move {
let cache = EXIT_CONTRACT_CACHE.read().unwrap().clone();
for (exit_contract, _value) in cache.iter() {
runner.block_on(async {
let cache_iter = cache.read().unwrap().clone();
for (exit_contract, _value) in cache_iter.iter() {
// get the latest exit list from the contract
match retrieve_exit_server_list(*exit_contract).await {
match retrieve_exit_server_list(*exit_contract, cache.clone()).await {
// grab the cache here so we don't lock it while awaiting for every single contract
Ok(cache_value) => {
let mut cache = EXIT_CONTRACT_CACHE.write().unwrap();
// update the cache
cache.insert(*exit_contract, cache_value);
cache.write().unwrap().insert(*exit_contract, cache_value);
}
Err(e) => {
info!("Failed to get exit list from contract {:?}", e);
Expand All @@ -171,8 +165,10 @@ async fn main() -> std::io::Result<()> {
// lazy static variable after this
load_config();

signature_update_loop();
let web_data = web::Data::new(EXIT_CONTRACT_CACHE.read().unwrap().clone());
let exit_contract_data_cache: Arc<RwLock<HashMap<Address, ExitContractSignatureCacheValue>>> =
Arc::new(RwLock::new(HashMap::new()));
signature_update_loop(exit_contract_data_cache.clone());
let web_data = web::Data::new(exit_contract_data_cache.clone());

let server = HttpServer::new(move || {
App::new()
Expand Down
2 changes: 2 additions & 0 deletions integration_tests/src/setup_utils/rita.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rita_common::rita_loop::{
start_core_rita_endpoints, start_rita_common_loops,
write_to_disk::{save_to_disk_loop, SettingsOnDisk},
};
use rita_exit::rita_loop::start_rita_exit_list_endpoint;
use rita_exit::{
dashboard::start_rita_exit_dashboard,
operator_update::update_loop::start_operator_update_loop,
Expand Down Expand Up @@ -259,6 +260,7 @@ pub fn spawn_rita_exit(
let workers = 4;
start_core_rita_endpoints(workers as usize);
start_rita_exit_endpoints(workers as usize);
start_rita_exit_list_endpoint(workers as usize);
start_rita_exit_dashboard(Arc::new(RwLock::new(None)));

// this one blocks
Expand Down
2 changes: 2 additions & 0 deletions rita_bin/src/exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use althea_types::Identity;
use clarity::Address;
#[cfg(feature = "jemalloc")]
use jemallocator::Jemalloc;
use rita_exit::rita_loop::start_rita_exit_list_endpoint;
use std::sync::Arc;
use std::sync::RwLock;
use std::time::Duration;
Expand Down Expand Up @@ -159,6 +160,7 @@ async fn main() {
let workers = settings.workers;
start_core_rita_endpoints(workers as usize);
start_rita_exit_endpoints(workers as usize);
start_rita_exit_list_endpoint(workers as usize);

start_rita_common_loops();
start_operator_update_loop();
Expand Down
1 change: 1 addition & 0 deletions rita_client/src/exit_manager/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ pub async fn exit_status_request(exit: IpAddr) -> Result<(), RitaClientError> {
}

/// Hits the exit_list endpoint for a given exit.
/// todo this is where we ask the exit for a new list (and do we have to decrypt it?)
pub async fn get_exit_list(exit: IpAddr) -> Result<ExitListV2, RitaClientError> {
let current_exit = match settings::get_rita_client()
.exit_client
Expand Down
63 changes: 52 additions & 11 deletions rita_exit/src/network_endpoints/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,40 @@ use crate::database::{client_status, signup_client};

use crate::RitaExitError;
use actix_web_async::{http::StatusCode, web::Json, HttpRequest, HttpResponse, Result};
use althea_types::exit_identity_to_id;
use althea_types::regions::Regions;
use althea_types::ExitListV2;
use althea_types::Identity;
use althea_types::{
decrypt_exit_client_id, encrypt_exit_list, encrypt_exit_list_v2, encrypt_setup_return,
};
use althea_types::{exit_identity_to_id, EncryptedExitList, EncryptedExitServerList};
use althea_types::{
EncryptedExitClientIdentity, EncryptedExitState, ExitClientIdentity, ExitState, ExitSystemTime,
};
use althea_types::{ExitList, WgKey};
use crypto_box::SecretKey;
use clarity::Address;
use crypto_box::{PublicKey, SecretKey};
use num256::Int256;
use reqwest::ClientBuilder;
use rita_client_registration::client_db::get_exits_list;
use rita_common::blockchain_oracle::potential_payment_issues_detected;
use rita_common::debt_keeper::get_debts_list;
use rita_common::rita_loop::get_web3_server;
use serde_json::json;
use settings::get_rita_exit;
use std::collections::HashSet;
use std::net::SocketAddr;
use std::time::Duration;
use std::time::SystemTime;
use web30::client::Web3;
use web30::jsonrpc::response;

// Timeout to contact Althea contract and query info about a user
pub const CLIENT_STATUS_TIMEOUT: Duration = Duration::from_secs(20);

// IP serving exit lists from the root server
pub const EXIT_LIST_IP: &str = "10.10.10.10";

/// helper function for returning from secure_setup_request()
enum DecryptResult {
Expand Down Expand Up @@ -189,6 +196,8 @@ pub async fn get_exit_timestamp_http(_req: HttpRequest) -> HttpResponse {
})
}

// todo somewhere between get exit list and get list v2 is where we get the new hard coded exit list.....
// exits must have a hardcoded ip server to host calls for the exit list endpoint! this cant go on the old server
/// This function takes a list of exit ips in the cluster from the exit registration smart
/// contract, and returns a list of exit ips that are in the same region and currency as the client
/// if this exit fits the region and currenty requirements it will always return a list containing itself
Expand All @@ -197,7 +206,8 @@ pub async fn get_exit_timestamp_http(_req: HttpRequest) -> HttpResponse {
pub async fn get_exit_list(request: Json<EncryptedExitClientIdentity>) -> HttpResponse {
let exit_settings = get_rita_exit();
let our_secretkey: WgKey = exit_settings.network.wg_private_key.unwrap();
let our_secretkey = our_secretkey.into();
let our_secretkey: SecretKey = our_secretkey.into();
let our_pubkey: PublicKey = our_secretkey.public_key();

let their_nacl_pubkey = request.pubkey.into();

Expand All @@ -211,9 +221,12 @@ pub async fn get_exit_list(request: Json<EncryptedExitClientIdentity>) -> HttpRe
.to_address();
let contract_addr = rita_exit.exit_network.registered_users_contract_addr;

// todo: we are receiving an EncryptedExitServerList from the root server- we need to a. decrypt it and b. reencrypt it
// with the client's key so that they can verify it came from the root server. Can we remove EncryptedExitList in favor of
// EncryptedExitServerList?
let ret: ExitList = ExitList {
exit_list: match get_exits_list(&contact, our_addr, contract_addr).await {
Ok(a) => {
exit_list: match get_exit_list_from_root(contract_addr, our_pubkey).await {
Some(a) => {
let exit_regions = rita_exit.exit_network.allowed_countries;

// only one payment type can be accepted for now, but this structure allows for
Expand Down Expand Up @@ -249,21 +262,49 @@ pub async fn get_exit_list(request: Json<EncryptedExitClientIdentity>) -> HttpRe
ret.push(our_id); // add ourselves to the list
ret
}
Err(e) => {
error!(
"Unable to retreive the exit list with {}, returning empty list",
e
);
None => {
error!("Unable to retreive the exit list, returning empty list");
vec![]
}
},
wg_exit_listen_port: settings::get_rita_exit().exit_network.wg_v2_tunnel_port,
};

let exit_list = encrypt_exit_list(&ret, &their_nacl_pubkey, &our_secretkey);
let exit_list = encrypt_exit_list(&ret, &our_secretkey, &their_nacl_pubkey);
HttpResponse::Ok().json(exit_list)
}

async fn get_exit_list_from_root(
contract_addr: Address,
pubkey: PublicKey,
) -> Option<EncryptedExitServerList> {
let request_url = format!("https://{}/{}", EXIT_LIST_IP, contract_addr);
let json_body = json!({
"pubkey": pubkey.to_bytes(),
});
let timeout = Duration::new(15, 0);
let client = ClientBuilder::new().timeout(timeout).build().unwrap();
let response = client
.head(request_url)
.json(&json_body)
.send()
.await
.expect("Could not receive data from exit root server");
if response.status().is_success() {
info!("Received an exit list");
match response.json().await {
Ok(a) => return Some(a),
Err(e) => {
error!("Failed to parse exit list from root server {:?}", e);
return None;
}
};
} else {
error!("Failed to get exit list from root server");
None
}
}

/// Exit list v2, for newer router that do the fitering (region and payment type) themselves, this endpoint
/// returns the entire list
pub async fn get_exit_list_v2(request: Json<EncryptedExitClientIdentity>) -> HttpResponse {
Expand Down
25 changes: 23 additions & 2 deletions rita_exit/src/rita_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,6 @@ pub fn start_rita_exit_endpoints(workers: usize) {
.route("/secure_status", web::post().to(secure_status_request))
.route("/client_debt", web::post().to(get_client_debt))
.route("/time", web::get().to(get_exit_timestamp_http))
.route("/exit_list", web::post().to(get_exit_list))
.route("/exit_list_v2", web::post().to(get_exit_list_v2))
})
.workers(workers)
.bind(format!(
Expand All @@ -354,3 +352,26 @@ pub fn start_rita_exit_endpoints(workers: usize) {
});
});
}

// the exit list gets its own server on hardcoded IP so that clients go to the nearest
pub fn start_rita_exit_list_endpoint(workers: usize) {
thread::spawn(move || {
let runner = AsyncSystem::new();
runner.block_on(async move {
let _res = HttpServer::new(|| {
App::new()
.route("/exit_list", web::post().to(get_exit_list))
})
.workers(workers)
.bind(format!(
"{}:{}",
EXIT_LIST_IP,
settings::get_rita_exit().exit_network.exit_list_port
))
.unwrap()
.shutdown_timeout(0)
.run()
.await;
});
});
}
3 changes: 3 additions & 0 deletions settings/src/exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub struct ExitNetworkSettings {
/// This is the port which the exit registration happens over, and should only be accessible
/// over the mesh
pub exit_hello_port: u16,
/// This is the port which exit lists are served over
pub exit_list_port: u16,
/// This is the port which the exit tunnel listens on
pub wg_tunnel_port: u16,
pub wg_v2_tunnel_port: u16,
Expand Down Expand Up @@ -77,6 +79,7 @@ impl ExitNetworkSettings {
.parse()
.unwrap(),
allowed_countries: HashSet::new(),
exit_list_port: 5555,
}
}
}
Expand Down

0 comments on commit 076305b

Please sign in to comment.