diff --git a/Cargo.lock b/Cargo.lock index 6a809fbc5..0694d3957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2665,6 +2665,7 @@ dependencies = [ "althea_kernel_interface", "althea_types", "arrayvec", + "clarity", "clu", "compressed_log", "ctrlc", @@ -2681,7 +2682,6 @@ dependencies = [ "openssl-probe", "phonenumber", "r2d2", - "reqwest", "rita_client", "rita_client_registration", "rita_common", @@ -2691,6 +2691,7 @@ dependencies = [ "serde", "serde_json", "settings", + "web30", ] [[package]] @@ -2744,6 +2745,7 @@ dependencies = [ "log", "num-traits", "phonenumber", + "rand", "serde", "tokio", "web30", diff --git a/althea_kernel_interface/src/traffic_control.rs b/althea_kernel_interface/src/traffic_control.rs index 8336ff1b6..83b8fa33e 100644 --- a/althea_kernel_interface/src/traffic_control.rs +++ b/althea_kernel_interface/src/traffic_control.rs @@ -342,7 +342,6 @@ impl dyn KernelInterface { /// Generates a unique traffic class id for a exit user, essentially a really dumb hashing function pub fn get_class_id(&self, ip: Ipv4Addr) -> u32 { - error!("Trying to get class id for ip {}", ip); let num: u32 = ip.into(); num % 9999 //9999 is the maximum flow id value allowed } diff --git a/integration_tests/src/contract_test.rs b/integration_tests/src/contract_test.rs index b23439717..ab7840b7d 100644 --- a/integration_tests/src/contract_test.rs +++ b/integration_tests/src/contract_test.rs @@ -1,17 +1,17 @@ -use std::collections::HashSet; - use althea_types::{ExitIdentity, Regions, SystemChain}; use clarity::{Address, PrivateKey}; use rita_client_registration::client_db::{ - add_client_to_registered_list, add_exit_admin, add_exit_to_exit_list, add_user_admin, - get_all_regsitered_clients, get_client_exit_list, get_registered_client_using_wgkey, + add_exit_admin, add_exits_to_registration_list, add_users_to_registered_list, + check_and_add_user_admin, get_all_regsitered_clients, get_exits_list, + get_registered_client_using_wgkey, }; use rita_common::usage_tracker::tests::test::random_identity; +use std::collections::HashSet; use web30::{client::Web3, types::SendTxOption}; use crate::{ payments_eth::WEB3_TIMEOUT, - utils::{deploy_contracts, get_eth_node, wait_for_txids, MINER_PRIVATE_KEY, TX_TIMEOUT}, + utils::{deploy_contracts, get_eth_node, wait_for_txids, REGISTRATION_SERVER_KEY, TX_TIMEOUT}, }; pub async fn run_altheadb_contract_test() { @@ -27,7 +27,7 @@ pub async fn run_altheadb_contract_test() { } pub async fn validate_contract_exit_functionality(db_addr: Address) { - let miner_private_key: PrivateKey = MINER_PRIVATE_KEY.parse().unwrap(); + let miner_private_key: PrivateKey = REGISTRATION_SERVER_KEY.parse().unwrap(); let miner_pub_key = miner_private_key.to_address(); let contact = Web3::new(&get_eth_node(), WEB3_TIMEOUT); @@ -108,13 +108,13 @@ pub async fn validate_contract_exit_functionality(db_addr: Address) { .await .unwrap(); - let res = get_client_exit_list(&contact, miner_pub_key, db_addr).await; + let res = get_exits_list(&contact, miner_pub_key, db_addr).await; - assert!(res.is_err()); + assert_eq!(res.unwrap(), vec![]); - let _res = add_exit_to_exit_list( + let _res = add_exits_to_registration_list( &contact, - exit_1.clone(), + vec![exit_1.clone()], db_addr, miner_private_key, Some(TX_TIMEOUT), @@ -123,9 +123,9 @@ pub async fn validate_contract_exit_functionality(db_addr: Address) { .await .unwrap(); - let _res = add_exit_to_exit_list( + let _res = add_exits_to_registration_list( &contact, - exit_2.clone(), + vec![exit_2.clone()], db_addr, miner_private_key, Some(TX_TIMEOUT), @@ -134,7 +134,7 @@ pub async fn validate_contract_exit_functionality(db_addr: Address) { .await .unwrap(); - let res = get_client_exit_list(&contact, miner_pub_key, db_addr) + let res = get_exits_list(&contact, miner_pub_key, db_addr) .await .unwrap(); @@ -142,9 +142,9 @@ pub async fn validate_contract_exit_functionality(db_addr: Address) { assert_eq!(res, vec![exit_1.clone(), exit_2.clone()]); - let _res = add_exit_to_exit_list( + let _res = add_exits_to_registration_list( &contact, - exit_3.clone(), + vec![exit_3.clone()], db_addr, miner_private_key, Some(TX_TIMEOUT), @@ -153,7 +153,7 @@ pub async fn validate_contract_exit_functionality(db_addr: Address) { .await .unwrap(); - let res = get_client_exit_list(&contact, miner_pub_key, db_addr) + let res = get_exits_list(&contact, miner_pub_key, db_addr) .await .unwrap(); @@ -163,7 +163,7 @@ pub async fn validate_contract_exit_functionality(db_addr: Address) { } pub async fn validate_contract_user_functionality(db_addr: Address) { - let miner_private_key: PrivateKey = MINER_PRIVATE_KEY.parse().unwrap(); + let miner_private_key: PrivateKey = REGISTRATION_SERVER_KEY.parse().unwrap(); let miner_pub_key = miner_private_key.to_address(); let contact = Web3::new(&get_eth_node(), WEB3_TIMEOUT); @@ -176,7 +176,7 @@ pub async fn validate_contract_user_functionality(db_addr: Address) { let user_5 = random_identity(); let user_6 = random_identity(); - add_user_admin( + check_and_add_user_admin( &contact, db_addr, miner_pub_key, @@ -187,7 +187,7 @@ pub async fn validate_contract_user_functionality(db_addr: Address) { .await .unwrap(); - add_user_admin( + check_and_add_user_admin( &contact, db_addr, miner_pub_key, @@ -210,10 +210,16 @@ pub async fn validate_contract_user_functionality(db_addr: Address) { assert!(res.is_err()); // Add the first user - let res = - add_client_to_registered_list(&contact, user_1, db_addr, miner_private_key, None, vec![]) - .await - .unwrap(); + let res = add_users_to_registered_list( + &contact, + vec![user_1], + db_addr, + miner_private_key, + None, + vec![], + ) + .await + .unwrap(); contact .wait_for_transaction(res, TX_TIMEOUT, None) @@ -253,9 +259,9 @@ pub async fn validate_contract_user_functionality(db_addr: Address) { .unwrap(); // Add the second user - let res1 = add_client_to_registered_list( + let res1 = add_users_to_registered_list( &contact, - user_2, + vec![user_2], db_addr, miner_private_key, None, @@ -268,9 +274,9 @@ pub async fn validate_contract_user_functionality(db_addr: Address) { .unwrap(); // Add the third user - let res2 = add_client_to_registered_list( + let res2 = add_users_to_registered_list( &contact, - user_3, + vec![user_3], db_addr, miner_private_key, None, @@ -282,9 +288,9 @@ pub async fn validate_contract_user_functionality(db_addr: Address) { .await .unwrap(); - let res3 = add_client_to_registered_list( + let res3 = add_users_to_registered_list( &contact, - user_4, + vec![user_4], db_addr, miner_private_key, None, @@ -296,9 +302,9 @@ pub async fn validate_contract_user_functionality(db_addr: Address) { .await .unwrap(); - let res4 = add_client_to_registered_list( + let res4 = add_users_to_registered_list( &contact, - user_5, + vec![user_5], db_addr, miner_private_key, None, @@ -310,9 +316,9 @@ pub async fn validate_contract_user_functionality(db_addr: Address) { .await .unwrap(); - let res5 = add_client_to_registered_list( + let res5 = add_users_to_registered_list( &contact, - user_6, + vec![user_6], db_addr, miner_private_key, None, diff --git a/integration_tests/src/db_migration_test.rs b/integration_tests/src/db_migration_test.rs index 6e9665142..b14185846 100644 --- a/integration_tests/src/db_migration_test.rs +++ b/integration_tests/src/db_migration_test.rs @@ -6,19 +6,19 @@ use std::{ use clarity::{Address, PrivateKey}; use diesel::{PgConnection, RunQueryDsl}; use rita_client_registration::{ - client_db::get_all_regsitered_clients, register_client_batch_loop::register_client_batch_loop, + client_db::{check_and_add_user_admin, get_all_regsitered_clients}, + register_client_batch_loop::register_client_batch_loop, }; use rita_common::usage_tracker::tests::test::random_identity; use rita_db_migration::{ - db_migration_user_admin, get_database_connection, models::Client, - schema::clients::dsl::clients, start_db_migration, + get_database_connection, models::Client, schema::clients::dsl::clients, start_db_migration, }; use web30::client::Web3; use crate::{ - payments_eth::{get_eth_miner_key, WEB3_TIMEOUT}, + payments_eth::{TRASACTION_TIMEOUT, WEB3_TIMEOUT}, setup_utils::database::start_postgres, - utils::{deploy_contracts, get_eth_node, MINER_PRIVATE_KEY}, + utils::{deploy_contracts, get_eth_node, REGISTRATION_SERVER_KEY}, }; pub const DB_URI: &str = "postgres://postgres@localhost/test"; @@ -45,28 +45,31 @@ pub async fn run_db_migration_test() { info!("Run migration code"); - let miner_private_key: PrivateKey = get_eth_miner_key(); + let reg_server_key: PrivateKey = REGISTRATION_SERVER_KEY.parse().unwrap(); // Start registration loop info!("Registering user admin"); // This request needs to be made with the state admin's key - db_migration_user_admin( - get_eth_node(), - MINER_PRIVATE_KEY.parse().unwrap(), - miner_private_key.to_address(), + check_and_add_user_admin( + &Web3::new(&get_eth_node(), WEB3_TIMEOUT), althea_db_addr, + reg_server_key.to_address(), + reg_server_key, + Some(TRASACTION_TIMEOUT), + vec![], ) - .await; + .await + .expect("Failed to add user admin!"); thread::sleep(Duration::from_secs(5)); info!("Starting registration loop"); - register_client_batch_loop(get_eth_node(), althea_db_addr, miner_private_key); + register_client_batch_loop(get_eth_node(), althea_db_addr, reg_server_key); info!("Running user migration"); match start_db_migration( DB_URI.to_string(), get_eth_node(), - miner_private_key.to_address(), + reg_server_key.to_address(), althea_db_addr, ) .await @@ -78,7 +81,7 @@ pub async fn run_db_migration_test() { info!("Waiting for register loop to migrate all clients"); thread::sleep(Duration::from_secs(10)); - validate_db_migration(num_clients, althea_db_addr, miner_private_key).await; + validate_db_migration(num_clients, althea_db_addr, reg_server_key).await; } fn add_dummy_clients_to_db(num_of_entries: usize, conn: &PgConnection) { @@ -119,9 +122,9 @@ fn random_db_client() -> Client { async fn validate_db_migration( num_clients: usize, althea_db_addr: Address, - miner_private_key: PrivateKey, + reg_server_key: PrivateKey, ) { - let miner_pub_key = miner_private_key.to_address(); + let miner_pub_key = reg_server_key.to_address(); let contact = Web3::new(&get_eth_node(), WEB3_TIMEOUT); let start = Instant::now(); diff --git a/integration_tests/src/payments_eth.rs b/integration_tests/src/payments_eth.rs index 9b7efa17e..b0f37758e 100644 --- a/integration_tests/src/payments_eth.rs +++ b/integration_tests/src/payments_eth.rs @@ -34,6 +34,7 @@ pub fn eth_chain_id() -> Uint256 { } pub const WEB3_TIMEOUT: Duration = Duration::from_secs(1); +pub const TRASACTION_TIMEOUT: Duration = Duration::from_secs(30); pub const ONE_ETH: u128 = 1_000_000_000_000_000_000; /// Runs a five node fixed network map test scenario diff --git a/integration_tests/src/registration_server.rs b/integration_tests/src/registration_server.rs index f1cea6c1a..f74158071 100644 --- a/integration_tests/src/registration_server.rs +++ b/integration_tests/src/registration_server.rs @@ -8,7 +8,7 @@ use actix_web::{ use althea_types::ExitClientIdentity; use clarity::{Address, PrivateKey}; use rita_client_registration::{ - client_db::add_user_admin, handle_sms_registration, register_client_batch_loop, + client_db::check_and_add_user_admin, handle_sms_registration, register_client_batch_loop, }; use web30::client::Web3; @@ -18,17 +18,17 @@ use crate::{ }; use crate::{ registration_server::register_client_batch_loop::register_client_batch_loop, - utils::MINER_PRIVATE_KEY, + utils::REGISTRATION_SERVER_KEY, }; pub const REGISTRATION_PORT_SERVER: u16 = 40400; pub async fn start_registration_server(db_addr: Address) { - let miner_private_key: PrivateKey = MINER_PRIVATE_KEY.parse().unwrap(); + let miner_private_key: PrivateKey = REGISTRATION_SERVER_KEY.parse().unwrap(); let miner_pub_key = miner_private_key.to_address(); let contact = Web3::new(&get_eth_node(), WEB3_TIMEOUT); - add_user_admin( + check_and_add_user_admin( &contact, db_addr, miner_pub_key, diff --git a/integration_tests/src/utils.rs b/integration_tests/src/utils.rs index 014d769f8..c261d4be2 100644 --- a/integration_tests/src/utils.rs +++ b/integration_tests/src/utils.rs @@ -30,7 +30,7 @@ use nix::{ sys::stat::Mode, }; use phonenumber::PhoneNumber; -use rita_client_registration::client_db::{add_exit_admin, add_exit_to_exit_list}; +use rita_client_registration::client_db::{add_exit_admin, add_exits_to_registration_list}; use rita_common::{ debt_keeper::GetDebtsResult, payment_validator::{ALTHEA_CHAIN_PREFIX, ALTHEA_CONTACT_TIMEOUT}, @@ -74,7 +74,7 @@ pub const MIN_GLOBAL_FEE_AMOUNT: u128 = 10; pub const TOTAL_TIMEOUT: Duration = Duration::from_secs(300); pub const DEBT_ACCURACY_THRES: u8 = 15; pub const ETH_NODE: &str = "http://localhost:8545"; -pub const MINER_PRIVATE_KEY: &str = +pub const REGISTRATION_SERVER_KEY: &str = "0x34d97aaf58b1a81d3ed3068a870d8093c6341cf5d1ef7e6efa03fe7f7fc2c3a8"; lazy_static! { @@ -170,7 +170,7 @@ pub async fn deploy_contracts() -> Address { .args([ "ts-node", "/althea_rs/solidity/contract-deployer.ts", - &format!("--eth-privkey={}", MINER_PRIVATE_KEY), + &format!("--eth-privkey={}", REGISTRATION_SERVER_KEY), &format!("--eth-node={}", ETH_NODE), ]) .output() @@ -1106,7 +1106,7 @@ pub async fn populate_routers_eth(rita_identities: InstanceData) { pub async fn add_exits_contract_exit_list(db_addr: Address, rita_identities: InstanceData) { let web3 = Web3::new("http://localhost:8545", WEB3_TIMEOUT); - let miner_private_key: clarity::PrivateKey = MINER_PRIVATE_KEY.parse().unwrap(); + let miner_private_key: clarity::PrivateKey = REGISTRATION_SERVER_KEY.parse().unwrap(); let miner_pub_key = miner_private_key.to_address(); add_exit_admin( @@ -1143,9 +1143,9 @@ pub async fn add_exits_contract_exit_list(db_addr: Address, rita_identities: Ins }; info!("Adding exit {:?} to contract exit list", exit_id); - add_exit_to_exit_list( + add_exits_to_registration_list( &web3, - exit_id, + vec![exit_id], db_addr, miner_private_key, None, diff --git a/rita_bin/Cargo.toml b/rita_bin/Cargo.toml index 2d81d5fbc..f0e5dd73a 100644 --- a/rita_bin/Cargo.toml +++ b/rita_bin/Cargo.toml @@ -53,7 +53,6 @@ rita_extender = { path = "../rita_extender", default-features = false } flate2 = { version = "1.0", features = [ "rust_backend", ], default-features = false } -reqwest = { version = "0.11", features = ["blocking", "json"] } jemallocator = { version = "0.5", optional = true } # we don't call or us OpenSSL directly in this codebase, but by adding # this dependency with this feature we can enforce that openssl is compiled @@ -63,6 +62,8 @@ jemallocator = { version = "0.5", optional = true } openssl = { version = "0.10", features = ["vendored"] } ipnetwork = "0.20" actix-rt = "2" +clarity = {workspace = true} +web30 = {workspace = true} [features] jemalloc = ["jemallocator"] diff --git a/rita_bin/src/database_migration.rs b/rita_bin/src/database_migration.rs index 16eef19b7..4c581512d 100644 --- a/rita_bin/src/database_migration.rs +++ b/rita_bin/src/database_migration.rs @@ -8,10 +8,11 @@ use std::{thread, time::Duration}; +use clarity::PrivateKey; use docopt::Docopt; use log::{error, info}; use rita_client_registration::register_client_batch_loop::register_client_batch_loop; -use rita_db_migration::{db_migration_user_admin, start_db_migration}; +use rita_db_migration::start_db_migration; use serde::Deserialize; #[derive(Debug, Deserialize)] @@ -39,37 +40,37 @@ async fn main() { .flag_address .parse() .expect("Please provide a valid eth contract addr"); - let web3_url = args.flag_web3url; - let private_key = args + let private_key: PrivateKey = args .flag_privatekey .parse() .expect("Please provide a valid eth private key with funds"); info!("About to add user admin"); - db_migration_user_admin( - web3_url.clone(), - private_key, - private_key.to_address(), - contract_addr, - ) - .await; thread::sleep(Duration::from_secs(5)); info!("About to add start registration loop"); // Start registration loop - register_client_batch_loop(web3_url.clone(), contract_addr, private_key); + register_client_batch_loop(args.flag_web3url.clone(), contract_addr, private_key); thread::sleep(Duration::from_secs(3)); info!("About to start db migration loop"); - match start_db_migration(db_url, web3_url, private_key.to_address(), contract_addr).await { - Ok(_) => info!("Successfully migrated all clients!"), + match start_db_migration( + db_url, + args.flag_web3url, + private_key.to_address(), + contract_addr, + ) + .await + { + Ok(_) => info!( + "Successfully queued all clients for migration! Close the program once this is done" + ), Err(e) => error!("Failed to migrate clients with {}", e), } - info!("Sleeping for 30 mins during migration"); - thread::sleep(Duration::from_secs(60 * 30)); + thread::sleep(Duration::from_secs(60000)); } pub fn get_arg_usage() -> String { diff --git a/rita_bin/src/exit.rs b/rita_bin/src/exit.rs index 10d887875..0b65af74c 100644 --- a/rita_bin/src/exit.rs +++ b/rita_bin/src/exit.rs @@ -73,7 +73,9 @@ fn main() { // and populate the memory cache of settings used throughout the program let settings = { let settings_file = args.flag_config; - let settings = RitaExitSettingsStruct::new_watched(&settings_file).unwrap(); + let settings = RitaExitSettingsStruct::new_watched(settings_file.clone()).unwrap(); + + settings::set_flag_config(settings_file.clone()); let settings = clu::exit_init(settings); settings::set_rita_exit(settings.clone()); diff --git a/rita_client_registration/Cargo.toml b/rita_client_registration/Cargo.toml index ddaaa32b2..3ee58c368 100644 --- a/rita_client_registration/Cargo.toml +++ b/rita_client_registration/Cargo.toml @@ -18,3 +18,6 @@ tokio = { version = "1.21", features = ["macros", "time"] } actix = "0.13" futures = { version = "0.3", features = ["compat"] } num-traits = "0.2" + +[dev-dependencies] +rand = "0.8" \ No newline at end of file diff --git a/rita_client_registration/src/client_db.rs b/rita_client_registration/src/client_db.rs index 75a69b96b..ea0f21cfc 100644 --- a/rita_client_registration/src/client_db.rs +++ b/rita_client_registration/src/client_db.rs @@ -1,9 +1,7 @@ -use std::{ - collections::HashSet, - net::{IpAddr, Ipv6Addr}, - time::Duration, - vec, -}; +//! This file includes functions for interacting with the AltheaDB.sol contract in the solidity folder +//! the purpose of this contract is to act as a registration database for users and exits, so that both +//! exit and client routers can read it to coordinate user setup and two way key exchange with the blockchain +//! as the trusted party use althea_types::{ExitIdentity, Identity, Regions, SystemChain, WgKey}; use clarity::{ @@ -11,22 +9,35 @@ use clarity::{ utils::bytes_to_hex_str, Address, PrivateKey, Uint256, }; +use std::{ + collections::HashSet, + net::{IpAddr, Ipv6Addr}, + time::Duration, + vec, +}; +use tokio::time::timeout as future_timeout; use web30::{ client::Web3, jsonrpc::error::Web3Error, types::{SendTxOption, TransactionRequest}, }; -use tokio::time::timeout as future_timeout; - +/// The EVM integer size pub const WORD_SIZE: usize = 32; +/// This function takes a flat byte vector `input` and divides it into chunks of a specified +/// word size (`WORD_SIZE`). Each chunk is then converted into a separate vector of bytes, +/// resulting in a vector of EVM words. +fn to_evm_words(input: Vec) -> Vec> { + input.chunks(WORD_SIZE).map(|i| i.to_vec()).collect() +} + pub async fn get_all_regsitered_clients( web30: &Web3, requester_address: Address, contract: Address, ) -> Result, Web3Error> { - let payload = encode_call("get_all_registered_users()", &[])?; + let payload = encode_call("getAllRegisteredUsers()", &[])?; let res = web30 .simulate_transaction( TransactionRequest::quick_tx(requester_address, contract, payload), @@ -44,7 +55,7 @@ pub async fn get_registered_client_using_wgkey( web30: &Web3, ) -> Result { let payload = encode_call( - "get_registered_client_with_wg_key(uint256)", + "getRegisteredClientWithWgKey(uint256)", &[AbiToken::Uint(key.into())], )?; let res = web30 @@ -55,95 +66,107 @@ pub async fn get_registered_client_using_wgkey( .await?; // Parse resulting bytes - parse_identity_abi(res.chunks(WORD_SIZE).collect()) + parse_identity_abi(res.chunks(WORD_SIZE).map(|i| i.to_vec()).collect()) } -pub async fn add_client_to_registered_list( +/// Function for bulk adding exits to the exits list, while the contract also provides addRegisteredClient() this function uses +/// addRegisteredClientsBulk() exclusively, simply pass a single client in if required. +pub async fn add_users_to_registered_list( web30: &Web3, - user: Identity, + users: Vec, contract: Address, sender_private_key: PrivateKey, wait_timeout: Option, options: Vec, ) -> Result { - if let IpAddr::V6(mesh_ip_v6) = user.mesh_ip { - let tx = web30 - .prepare_transaction( - contract, - encode_call( - "add_registered_user((uint128,uint256,address))", - &[AbiToken::Struct(vec![ - AbiToken::Uint(u128::from(mesh_ip_v6).into()), - AbiToken::Uint(user.wg_public_key.into()), - AbiToken::Address(user.eth_address), - ])], - )?, - 0u32.into(), - sender_private_key, - options, - ) - .await?; + let mut encoded_clients = Vec::new(); + for user in users { + if let IpAddr::V6(mesh_ip_v6) = user.mesh_ip { + encoded_clients.push(AbiToken::Struct(vec![ + AbiToken::Uint(u128::from(mesh_ip_v6).into()), + AbiToken::Uint(user.wg_public_key.into()), + AbiToken::Address(user.eth_address), + ])) + } else { + return Err(Web3Error::BadInput(format!( + "Why is mesh ip a v4? {}", + user.mesh_ip + ))); + } + } - let tx_hash = web30.send_prepared_transaction(tx).await?; + let tx = web30 + .prepare_transaction( + contract, + encode_call( + "addRegisteredUsersBulk((uint128,uint256,address)[])", + &[AbiToken::Dynamic(encoded_clients)], + )?, + 0u32.into(), + sender_private_key, + options, + ) + .await?; - if let Some(timeout) = wait_timeout { - future_timeout(timeout, web30.wait_for_transaction(tx_hash, timeout, None)).await??; - } + let tx_hash = web30.send_prepared_transaction(tx).await?; - Ok(tx_hash) - } else { - error!("Why is mesh ip setup as a V4? {}", user.mesh_ip); - Err(Web3Error::BadInput(format!( - "Why is mesh ip a v4? {}", - user.mesh_ip - ))) + if let Some(timeout) = wait_timeout { + future_timeout(timeout, web30.wait_for_transaction(tx_hash, timeout, None)).await??; } + + Ok(tx_hash) } -pub async fn add_exit_to_exit_list( +/// Function for bulk adding exits to the exits list, while the contract also provides addRegisteredExit() this function uses +/// addRegisteredExitsBulk() exclusively, simply pass a single exit in if required. +pub async fn add_exits_to_registration_list( web30: &Web3, - exit: ExitIdentity, + exits: Vec, contract: Address, sender_private_key: PrivateKey, wait_timeout: Option, options: Vec, ) -> Result { - if let IpAddr::V6(mesh_ip_v6) = exit.mesh_ip { - let tx = web30 - .prepare_transaction( - contract, - encode_call( - "add_registered_exit((uint128,uint256,address,uint16,uint16,uint256[],uint256[]))", - &[AbiToken::Struct(vec![ - AbiToken::Uint(u128::from(mesh_ip_v6).into()), - AbiToken::Uint(exit.wg_key.into()), - AbiToken::Address(exit.eth_addr), - AbiToken::Uint(exit.registration_port.into()), - AbiToken::Uint(exit.wg_exit_listen_port.into()), - allowed_regions_abi_array(exit.allowed_regions).into(), - payment_types_abi_array(exit.payment_types).into(), - ])], - )?, - 0u32.into(), - sender_private_key, - options, - ) - .await?; + let mut encoded_exits = Vec::new(); + for exit in exits { + if let IpAddr::V6(mesh_ip_v6) = exit.mesh_ip { + encoded_exits.push(AbiToken::Struct(vec![ + AbiToken::Uint(u128::from(mesh_ip_v6).into()), + AbiToken::Uint(exit.wg_key.into()), + AbiToken::Address(exit.eth_addr), + AbiToken::Uint(exit.registration_port.into()), + AbiToken::Uint(exit.wg_exit_listen_port.into()), + allowed_regions_abi_array(exit.allowed_regions).into(), + payment_types_abi_array(exit.payment_types).into(), + ])) + } else { + return Err(Web3Error::BadInput(format!( + "Why is mesh ip a v4? {}", + exit.mesh_ip + ))); + } + } - let tx_hash = web30.send_prepared_transaction(tx).await?; + let tx = web30 + .prepare_transaction( + contract, + encode_call( + "addRegisteredExitsBulk((uint128,uint256,address,uint16,uint16,uint256[],uint256[])[])", + &[AbiToken::Dynamic(encoded_exits)], + )?, + 0u32.into(), + sender_private_key, + options, + ) + .await?; - if let Some(timeout) = wait_timeout { - future_timeout(timeout, web30.wait_for_transaction(tx_hash, timeout, None)).await??; - } + let tx_hash = web30.send_prepared_transaction(tx).await?; - Ok(tx_hash) - } else { - error!("Why is mesh ip setup as a V4? {}", exit.mesh_ip); - Err(Web3Error::BadInput(format!( - "Why is mesh ip a v4? {}", - exit.mesh_ip - ))) + if let Some(timeout) = wait_timeout { + future_timeout(timeout, web30.wait_for_transaction(tx_hash, timeout, None)).await??; } + + Ok(tx_hash) } fn allowed_regions_abi_array(allowed_regions: HashSet) -> Vec { @@ -164,17 +187,16 @@ fn payment_types_abi_array(payment_types: HashSet) -> Vec ret } -/// A user admin has permissions to add and remove users from the registered list -pub async fn add_user_admin( +/// Checks if a given adress is an user admin, that is one of the addresses that is allowed to +/// add and remove users from the contract +pub async fn check_user_admin( web30: &Web3, contract: Address, user_admin: Address, our_private_key: PrivateKey, - wait_timeout: Option, - options: Vec, -) -> Result<(), Web3Error> { +) -> Result { // Check if we are already a user admin - let payload = encode_call("is_user_admin(address)", &[AbiToken::Address(user_admin)])?; + let payload = encode_call("isUserAdmin(address)", &[AbiToken::Address(user_admin)])?; let res = web30 .simulate_transaction( TransactionRequest::quick_tx(our_private_key.to_address(), contract, payload), @@ -185,11 +207,24 @@ pub async fn add_user_admin( let is_admin = !res.is_empty() && Uint256::from_be_bytes(res.chunks(WORD_SIZE).collect::>()[0]) == 1u8.into(); - if !is_admin { + Ok(is_admin) +} + +/// A user admin has permissions to add and remove users from the registered list +/// this function adds them only if required +pub async fn check_and_add_user_admin( + web30: &Web3, + contract: Address, + user_admin: Address, + our_private_key: PrivateKey, + wait_timeout: Option, + options: Vec, +) -> Result<(), Web3Error> { + if !check_user_admin(web30, contract, user_admin, our_private_key).await? { let tx = web30 .prepare_transaction( contract, - encode_call("add_user_admin(address)", &[AbiToken::Address(user_admin)])?, + encode_call("addUserAdmin(address)", &[AbiToken::Address(user_admin)])?, 0u32.into(), our_private_key, options, @@ -205,17 +240,15 @@ pub async fn add_user_admin( Ok(()) } -/// An exit admin has permissions to add and remove exits from the exit list. This is what is returned -/// to clients to register to exits -pub async fn add_exit_admin( +/// Checks if a given address is an exit admin, that is one of the addresses that is allowed to +/// add and remove exits from the contract +pub async fn check_exit_admin( web30: &Web3, contract: Address, exit_admin: Address, our_private_key: PrivateKey, - wait_timeout: Option, - options: Vec, -) -> Result<(), Web3Error> { - let payload = encode_call("is_exit_admin(address)", &[AbiToken::Address(exit_admin)])?; +) -> Result { + let payload = encode_call("isExitAdmin(address)", &[AbiToken::Address(exit_admin)])?; let res = web30 .simulate_transaction( TransactionRequest::quick_tx(our_private_key.to_address(), contract, payload), @@ -226,11 +259,24 @@ pub async fn add_exit_admin( let is_admin = Uint256::from_be_bytes(res.chunks(WORD_SIZE).collect::>()[0]) == 1u8.into(); - if !is_admin { + Ok(is_admin) +} + +/// An exit admin has permissions to add and remove exits from the exit list. This is what is returned +/// to clients to register to exits +pub async fn add_exit_admin( + web30: &Web3, + contract: Address, + exit_admin: Address, + our_private_key: PrivateKey, + wait_timeout: Option, + options: Vec, +) -> Result<(), Web3Error> { + if !check_exit_admin(web30, contract, exit_admin, our_private_key).await? { let tx = web30 .prepare_transaction( contract, - encode_call("add_exit_admin(address)", &[AbiToken::Address(exit_admin)])?, + encode_call("addExitAdmin(address)", &[AbiToken::Address(exit_admin)])?, 0u32.into(), our_private_key, options, @@ -246,12 +292,13 @@ pub async fn add_exit_admin( Ok(()) } -pub async fn get_client_exit_list( +/// Gets the list of exits from the smart contract +pub async fn get_exits_list( web30: &Web3, requester_address: Address, contract: Address, ) -> Result, Web3Error> { - let payload = encode_call("get_all_registered_exits()", &[])?; + let payload = encode_call("getAllRegisteredExits()", &[])?; let res = web30 .simulate_transaction( TransactionRequest::quick_tx(requester_address, contract, payload), @@ -263,7 +310,7 @@ pub async fn get_client_exit_list( parse_exit_identity_array_abi(res) } -pub fn parse_identity_abi(byte_chunks: Vec<&[u8]>) -> Result { +pub fn parse_identity_abi(byte_chunks: Vec>) -> Result { /* Expected Input: 00000000000000000000000000000000c5860e75c42cec1fe1d838a78de785fb // Mesh ip as u128 c5860e75c42cec1fe1d838a78de785fbb687e85cbd5073a089b5395397423ccc // wgkey as u256 @@ -366,7 +413,7 @@ pub fn parse_identity_array_abi(bytes: Vec) -> Result, Web3Err */ let mut ret = vec![]; - let byte_chunks: Vec<&[u8]> = bytes.chunks(WORD_SIZE).collect(); + let byte_chunks = to_evm_words(bytes); // An empty list, the first word has a type identifier, the second is empty if byte_chunks.len() == 2 { @@ -410,11 +457,12 @@ pub fn parse_identity_array_abi(bytes: Vec) -> Result, Web3Err ))) } }; + let bytes_to_pass: Vec> = bytes_to_pass.iter().map(|i| i.to_vec()).collect(); // Increment index for next iteration index += 3; - ret.push(match parse_identity_abi((*bytes_to_pass).to_vec()) { + ret.push(match parse_identity_abi(bytes_to_pass) { Ok(a) => a, Err(e) => { error!( @@ -483,9 +531,14 @@ pub fn parse_exit_identity_array_abi(bytes: Vec) -> Result 0000000000000000000000000000000000000000000000000000000000000001 // Num entries in payment 0000000000000000000000000000000000000000000000000000000000000003 // Payment entry */ + let byte_chunks = to_evm_words(bytes); + + // An empty list, the first word has a type identifier, the second is empty + if byte_chunks.len() == 2 { + return Ok(vec![]); + } // A valid array with 1 entry will have atleast 11 lines - let byte_chunks: Vec<_> = bytes.chunks(WORD_SIZE).collect(); if byte_chunks.len() < 11 { return Err(Web3Error::BadInput(format!( "Empty or invalid array: {byte_chunks:?}" @@ -506,7 +559,12 @@ pub fn parse_exit_identity_array_abi(bytes: Vec) -> Result // pass in each entry byte chunk to individual entry parser let index = 2; for i in index..index + num_entries { - let next_index_pos: Uint256 = Uint256::from_be_bytes(byte_chunks[i]) / WORD_SIZE.into(); + if i >= byte_chunks.len() { + let msg = "Encoded array length longer than data".to_string(); + error!("{}", msg); + return Err(Web3Error::BadInput(msg)); + } + let next_index_pos: Uint256 = Uint256::from_be_bytes(&byte_chunks[i]) / WORD_SIZE.into(); let next_index_pos: usize = usize::from_be_bytes( match next_index_pos.to_be_bytes()[24..WORD_SIZE].try_into() { Ok(a) => a, @@ -521,7 +579,7 @@ pub fn parse_exit_identity_array_abi(bytes: Vec) -> Result ); match parse_exit_identity_abi(match byte_chunks.get(index + next_index_pos..) { - Some(a) => (*a).to_vec(), + Some(a) => a.iter().map(|i| i.to_vec()).collect(), None => { error!( "Invalid indexing? trying to get {}, with byte chunks {:?}", @@ -542,7 +600,7 @@ pub fn parse_exit_identity_array_abi(bytes: Vec) -> Result } // Parses a single entry of an abi encoded ExitIdentity -pub fn parse_exit_identity_abi(byte_chunks: Vec<&[u8]>) -> Result { +pub fn parse_exit_identity_abi(byte_chunks: Vec>) -> Result { /*Expected input: 000000000000000000000000000000007bbab1ac348ee5be29ac57e2c3e052a1 // Second array entry mesh ip 7bbab1ac348ee5be29ac57e2c3e052a164bc756beb5063743399fd08fdb6c5bb @@ -568,7 +626,7 @@ pub fn parse_exit_identity_abi(byte_chunks: Vec<&[u8]>) -> Result a, @@ -582,7 +640,7 @@ pub fn parse_exit_identity_abi(byte_chunks: Vec<&[u8]>) -> Result a, @@ -596,7 +654,7 @@ pub fn parse_exit_identity_abi(byte_chunks: Vec<&[u8]>) -> Result a, @@ -610,7 +668,7 @@ pub fn parse_exit_identity_abi(byte_chunks: Vec<&[u8]>) -> Result a, @@ -698,69 +756,169 @@ pub fn parse_exit_identity_abi(byte_chunks: Vec<&[u8]>) -> Result Vec> { + let outer_range = Uniform::from(1..200_000); + let inner_range = Uniform::from(1..10_000); + let outer_size: usize = outer_range.sample(rng); + let mut fuzz_bytes = Vec::with_capacity(outer_size); + for _ in 0..outer_size { + let inner_size: usize = inner_range.sample(rng); + let mut inner_bytes = Vec::with_capacity(inner_size); + + for _ in 0..inner_size { + inner_bytes.push(rng.gen()); + } + + fuzz_bytes.push(inner_bytes); + } + fuzz_bytes + } + + fn get_fuzz_bytes_flat(rng: &mut ThreadRng) -> Vec { + let range = Uniform::from(1..200_000); + let size: usize = range.sample(rng); + let event_bytes: Vec = (0..size) + .map(|_| { + let val: u8 = rng.gen(); + val + }) + .collect(); + event_bytes + } + + #[test] + fn fuzz_pase_identity_abi() { + let start = Instant::now(); + let mut rng = thread_rng(); + while Instant::now() - start < FUZZ_TIME { + let bytes = get_fuzz_bytes(&mut rng); + + let res = parse_identity_abi(bytes); + match res { + Ok(_) => println!("Got valid output, this should happen very rarely!"), + Err(_e) => {} + } + } + } + + #[test] + fn fuzz_pase_identity_array_abi() { + let start = Instant::now(); + let mut rng = thread_rng(); + while Instant::now() - start < FUZZ_TIME { + let bytes = get_fuzz_bytes_flat(&mut rng); + + let res = parse_identity_array_abi(bytes); + match res { + Ok(_) => println!("Got valid output, this should happen very rarely!"), + Err(_e) => {} + } + } + } + + #[test] + fn fuzz_pase_exit_identity_array() { + let start = Instant::now(); + let mut rng = thread_rng(); + while Instant::now() - start < FUZZ_TIME { + let bytes = get_fuzz_bytes(&mut rng); + + let res = parse_exit_identity_abi(bytes); + match res { + Ok(_) => println!("Got valid output, this should happen very rarely!"), + Err(_e) => {} + } + } + } + + #[test] + fn fuzz_pase_exit_identity_array_abi() { + let start = Instant::now(); + let mut rng = thread_rng(); + while Instant::now() - start < FUZZ_TIME { + let bytes = get_fuzz_bytes_flat(&mut rng); + + let res = parse_exit_identity_array_abi(bytes); + match res { + Ok(_) => println!("Got valid output, this should happen very rarely!"), + Err(_e) => {} + } + } + } + + #[test] + fn test_parse_abi() { + use clarity::utils::hex_str_to_bytes; + // test parsing an abi struct with various input types + let id = Identity { + mesh_ip: "e0b1:bf22:64ae:8e91:cc4e:5a1b:a0ef:8495".parse().unwrap(), + eth_address: "0x090502B2fd4dE198554511C0a6fd4da5D41E7C49" + .parse() + .unwrap(), + wg_public_key: "4LG/ImSujpHMTloboO+ElV7wgn2LRsUnZzoeZGMFO2Q=" + .parse() + .unwrap(), + nickname: None, + }; + let bytes = "\ 00000000000000000000000000000000e0b1bf2264ae8e91cc4e5a1ba0ef8495\ e0b1bf2264ae8e91cc4e5a1ba0ef84955ef0827d8b46c527673a1e6463053b64\ 000000000000000000000000090502b2fd4de198554511c0a6fd4da5d41e7c49"; - let bytes = hex_str_to_bytes(bytes).unwrap(); - assert_eq!( - parse_identity_abi(bytes.chunks(WORD_SIZE).collect()).unwrap(), - id - ); + let bytes = hex_str_to_bytes(bytes).unwrap(); + assert_eq!(parse_identity_abi(to_evm_words(bytes)).unwrap(), id); - // invalid input - let bytes = "\ + // invalid input + let bytes = "\ 00000000000000000000000000000000e0b1bf2264ae8e91cc4e5a1ba0ef8495\ 00000000000000000000000000000000e0b1bf2264ae8e91cc4e5a1ba0ef8495\ e0b1bf2264ae8e91cc4e5a1ba0ef84955ef0827d8b46c527673a1e6463053b64\ 000000000000000000000000090502b2fd4de198554511c0a6fd4da5d41e7c49"; - let bytes = hex_str_to_bytes(bytes).unwrap(); - assert!(parse_identity_abi(bytes.chunks(WORD_SIZE).collect()).is_err()); + let bytes = hex_str_to_bytes(bytes).unwrap(); + assert!(parse_identity_abi(to_evm_words(bytes)).is_err()); - // invalid input - let bytes = "\ + // invalid input + let bytes = "\ 0000000000000000000000000000000000000000000000000000000000000000\ 00000000000000000000000000000000e0b1bf2264ae8e91cc4e5a1ba0ef8495\ e0b1bf2264ae8e91cc4e5a1ba0ef84955ef0827d8b46c527673a1e6463053b64"; - let bytes = hex_str_to_bytes(bytes).unwrap(); - assert!(parse_identity_abi(bytes.chunks(WORD_SIZE).collect()).is_err()); + let bytes = hex_str_to_bytes(bytes).unwrap(); + assert!(parse_identity_abi(to_evm_words(bytes)).is_err()); - // invalid input - let bytes = "\ + // invalid input + let bytes = "\ 00000000000000000000000000000000e0b1bf2264ae8e91cc4e5a1ba0ef8495\ e0b1bf2264ae8e91cc4e5a1ba0ef84955ef0827d8b46c527673a1e6463053b64"; - let bytes = hex_str_to_bytes(bytes).unwrap(); - assert!(parse_identity_abi(bytes.chunks(WORD_SIZE).collect()).is_err()); -} + let bytes = hex_str_to_bytes(bytes).unwrap(); + assert!(parse_identity_abi(to_evm_words(bytes)).is_err()); + } -#[test] -fn test_parse_abi_array() { - use clarity::utils::hex_str_to_bytes; - // empty string - let bytes = ""; - let bytes = hex_str_to_bytes(bytes).unwrap(); - assert!(parse_identity_array_abi(bytes).is_err()); + #[test] + fn test_parse_abi_array() { + use clarity::utils::hex_str_to_bytes; + // empty string + let bytes = ""; + let bytes = hex_str_to_bytes(bytes).unwrap(); + assert!(parse_identity_array_abi(bytes).is_err()); - // valid entry - let bytes = "\ + // valid entry + let bytes = "\ 0000000000000000000000000000000000000000000000000000000000000020\ 0000000000000000000000000000000000000000000000000000000000000003\ 00000000000000000000000000000000e0b1bf2264ae8e91cc4e5a1ba0ef8495\ @@ -773,12 +931,12 @@ fn test_parse_abi_array() { 3ef17d634ede32665e35816eda438a84aeeccf32f53327538f66d6ce859b2d23\ 000000000000000000000000d9474fa480aca506f14c439a738e6362bb66f654"; - let bytes = hex_str_to_bytes(bytes).unwrap(); - assert!(parse_identity_array_abi(bytes.clone()).is_ok()); - assert!(parse_identity_array_abi(bytes).unwrap().len() == 3); + let bytes = hex_str_to_bytes(bytes).unwrap(); + assert!(parse_identity_array_abi(bytes.clone()).is_ok()); + assert!(parse_identity_array_abi(bytes).unwrap().len() == 3); - // Second entry invalid - let bytes = "\ + // Second entry invalid + let bytes = "\ 0000000000000000000000000000000000000000000000000000000000000020\ 0000000000000000000000000000000000000000000000000000000000000003\ 00000000000000000000000000000000e0b1bf2264ae8e91cc4e5a1ba0ef8495\ @@ -791,25 +949,25 @@ fn test_parse_abi_array() { 3ef17d634ede32665e35816eda438a84aeeccf32f53327538f66d6ce859b2d23\ 000000000000000000000000d9474fa480aca506f14c439a738e6362bb66f654"; - let bytes = hex_str_to_bytes(bytes).unwrap(); - assert!(parse_identity_array_abi(bytes.clone()).is_ok()); - assert!(parse_identity_array_abi(bytes).unwrap().len() == 2); + let bytes = hex_str_to_bytes(bytes).unwrap(); + assert!(parse_identity_array_abi(bytes.clone()).is_ok()); + assert!(parse_identity_array_abi(bytes).unwrap().len() == 2); - // No valid entries - let bytes = "\ + // No valid entries + let bytes = "\ 0000000000000000000000000000000000000000000000000000000000000020\ 0000000000000000000000000000000000000000000000000000000000000002\ 0000000000000000000000000000000000000000000000000000000000000000\ "; - let bytes = hex_str_to_bytes(bytes).unwrap(); - assert!(parse_identity_array_abi(bytes.clone()).is_err()); -} + let bytes = hex_str_to_bytes(bytes).unwrap(); + assert!(parse_identity_array_abi(bytes.clone()).is_err()); + } -#[test] -fn test_parse_exit_id_abi() { - use clarity::utils::hex_str_to_bytes; + #[test] + fn test_parse_exit_id_abi() { + use clarity::utils::hex_str_to_bytes; - let bytes = "\ + let bytes = "\ 000000000000000000000000000000007bbab1ac348ee5be29ac57e2c3e052a1\ 7bbab1ac348ee5be29ac57e2c3e052a164bc756beb5063743399fd08fdb6c5bb\ 000000000000000000000000a970fab4bff2530005fdb65eeb4fe88d228aa9f8\ @@ -821,15 +979,15 @@ fn test_parse_exit_id_abi() { 0000000000000000000000000000000000000000000000000000000000000006\ 0000000000000000000000000000000000000000000000000000000000000001\ 0000000000000000000000000000000000000000000000000000000000000003"; - let bytes = hex_str_to_bytes(bytes).unwrap(); + let bytes = hex_str_to_bytes(bytes).unwrap(); - let res = parse_exit_identity_abi(bytes.chunks(WORD_SIZE).collect()).unwrap(); - assert!(res.allowed_regions.contains(&Regions::Columbia)); - assert!(res.payment_types.contains(&SystemChain::Rinkeby)); - assert_eq!(res.allowed_regions.len(), 1); - assert_eq!(res.payment_types.len(), 1); + let res = parse_exit_identity_abi(to_evm_words(bytes)).unwrap(); + assert!(res.allowed_regions.contains(&Regions::Columbia)); + assert!(res.payment_types.contains(&SystemChain::Rinkeby)); + assert_eq!(res.allowed_regions.len(), 1); + assert_eq!(res.payment_types.len(), 1); - let bytes = "\ + let bytes = "\ 00000000000000000000000000000000d5ce8b4de8234789da53bddd707db3d5\ d5ce8b4de8234789da53bddd707db3d589e00b5fce9d9b5f68cc7f3550d8944f\ 000000000000000000000000351634dbb20142a7f5ab996b96f71795e35e93f3\ @@ -844,14 +1002,14 @@ fn test_parse_exit_id_abi() { 0000000000000000000000000000000000000000000000000000000000000001\ 0000000000000000000000000000000000000000000000000000000000000002\ 0000000000000000000000000000000000000000000000000000000000000004"; - let bytes = hex_str_to_bytes(bytes).unwrap(); + let bytes = hex_str_to_bytes(bytes).unwrap(); - let res = parse_exit_identity_abi(bytes.chunks(WORD_SIZE).collect()).unwrap(); - assert_eq!(res.allowed_regions.len(), 2); - assert_eq!(res.payment_types.len(), 3); + let res = parse_exit_identity_abi(to_evm_words(bytes)).unwrap(); + assert_eq!(res.allowed_regions.len(), 2); + assert_eq!(res.payment_types.len(), 3); - // Valid input with a bunch of cruft at the end - let bytes = "\ + // Valid input with a bunch of cruft at the end + let bytes = "\ 00000000000000000000000000000000d5ce8b4de8234789da53bddd707db3d5\ d5ce8b4de8234789da53bddd707db3d589e00b5fce9d9b5f68cc7f3550d8944f\ 000000000000000000000000351634dbb20142a7f5ab996b96f71795e35e93f3\ @@ -869,17 +1027,17 @@ fn test_parse_exit_id_abi() { 0000000000000000000000000000000000000000000000000000000000000002\ 0000000000000000000000000000000000000000000000000000000000000002\ 0000000000000000000000000000000000000000000000000000000000000002"; - let bytes = hex_str_to_bytes(bytes).unwrap(); + let bytes = hex_str_to_bytes(bytes).unwrap(); - let res = parse_exit_identity_abi(bytes.chunks(WORD_SIZE).collect()).unwrap(); - print!("{:?}", res); -} + let res = parse_exit_identity_abi(to_evm_words(bytes)).unwrap(); + print!("{:?}", res); + } -#[test] -fn test_exit_array_abi() { - use clarity::utils::hex_str_to_bytes; + #[test] + fn test_exit_array_abi() { + use clarity::utils::hex_str_to_bytes; - let bytes = "\ + let bytes = "\ 0000000000000000000000000000000000000000000000000000000000000020\ 0000000000000000000000000000000000000000000000000000000000000002\ 0000000000000000000000000000000000000000000000000000000000000040\ @@ -907,8 +1065,9 @@ fn test_exit_array_abi() { 0000000000000000000000000000000000000000000000000000000000000006\ 0000000000000000000000000000000000000000000000000000000000000001\ 0000000000000000000000000000000000000000000000000000000000000003"; - let bytes = hex_str_to_bytes(bytes).unwrap(); + let bytes = hex_str_to_bytes(bytes).unwrap(); - let res = parse_exit_identity_array_abi(bytes).unwrap(); - println!("{:?}", res); + let res = parse_exit_identity_array_abi(bytes).unwrap(); + println!("{:?}", res); + } } diff --git a/rita_client_registration/src/lib.rs b/rita_client_registration/src/lib.rs index ce2ce192d..9b5490087 100644 --- a/rita_client_registration/src/lib.rs +++ b/rita_client_registration/src/lib.rs @@ -1,4 +1,7 @@ -#![deny(unused_crate_dependencies)] +use althea_types::{ExitClientIdentity, Identity, WgKey}; +use awc::error::SendRequestError; +use phonenumber::PhoneNumber; +use serde::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, error::Error, @@ -7,11 +10,6 @@ use std::{ time::Duration, }; -use althea_types::{ExitClientIdentity, Identity, WgKey}; -use awc::error::SendRequestError; -use phonenumber::PhoneNumber; -use serde::{Deserialize, Serialize}; - #[macro_use] extern crate log; #[macro_use] @@ -23,7 +21,7 @@ pub mod register_client_batch_loop; lazy_static! { /// A map that stores number of texts sent to a client during registration static ref TEXTS_SENT: Arc>> = Arc::new(RwLock::new(HashMap::new())); - static ref TX_BATCH: Arc>> = Arc::new(RwLock::new(HashSet::new())); + static ref REGISTER_QUEUE: Arc>> = Arc::new(RwLock::new(HashSet::new())); } const REGISTRATION_LOOP_SPEED: Duration = Duration::from_secs(10); @@ -85,16 +83,16 @@ fn get_texts_sent(key: WgKey) -> u8 { *TEXTS_SENT.read().unwrap().get(&key).unwrap_or(&0u8) } -pub fn add_client_to_reg_batch(id: Identity) { - TX_BATCH.write().unwrap().insert(id); +pub fn add_client_to_reg_queue(id: Identity) { + REGISTER_QUEUE.write().unwrap().insert(id); } -fn remove_client_from_reg_batch(id: Identity) { - TX_BATCH.write().unwrap().remove(&id); +fn remove_client_from_reg_queue(id: Identity) { + REGISTER_QUEUE.write().unwrap().remove(&id); } -fn get_reg_batch() -> Vec { - TX_BATCH.read().unwrap().clone().into_iter().collect() +fn get_reg_queue() -> Vec { + REGISTER_QUEUE.read().unwrap().clone().into_iter().collect() } #[derive(Serialize)] @@ -162,7 +160,7 @@ pub async fn handle_sms_registration( client.global.wg_public_key ); - add_client_to_reg_batch(client.global); + add_client_to_reg_queue(client.global); reset_texts_sent(client.global.wg_public_key); ExitSignupReturn::RegistrationOk } else { @@ -205,7 +203,7 @@ pub async fn handle_sms_registration( "Phone registration complete for {}", client.global.wg_public_key ); - add_client_to_reg_batch(client.global); + add_client_to_reg_queue(client.global); reset_texts_sent(client.global.wg_public_key); ExitSignupReturn::RegistrationOk } else { diff --git a/rita_client_registration/src/register_client_batch_loop.rs b/rita_client_registration/src/register_client_batch_loop.rs index 6b7563d0c..833901108 100644 --- a/rita_client_registration/src/register_client_batch_loop.rs +++ b/rita_client_registration/src/register_client_batch_loop.rs @@ -1,31 +1,30 @@ +use crate::{ + client_db::{add_users_to_registered_list, get_all_regsitered_clients}, + get_reg_queue, remove_client_from_reg_queue, REGISTRATION_LOOP_SPEED, TX_TIMEOUT, WEB3_TIMEOUT, +}; +use actix::System; +use althea_types::Identity; +use clarity::{Address, PrivateKey}; use std::{ - net::IpAddr, + collections::HashSet, thread, time::{Duration, Instant}, }; +use web30::{client::Web3, types::SendTxOption}; -use num_traits::cast::ToPrimitive; - -use actix::System; -use clarity::{ - abi::{encode_call, AbiToken}, - Address, PrivateKey, Transaction, Uint256, -}; -use futures::future::{join4, join_all}; -use web30::{ - client::Web3, - jsonrpc::error::Web3Error, - types::{TransactionRequest, TransactionResponse}, -}; +pub const MAX_BATCH_SIZE: usize = 75; -use crate::{ - get_reg_batch, remove_client_from_reg_batch, REGISTRATION_LOOP_SPEED, TX_TIMEOUT, WEB3_TIMEOUT, -}; - -pub const MAX_BATCH_SIZE: usize = 100; +/// Utility function used to easily perform O(1) lookups against the identities list +fn get_clients_hashset(input: Vec) -> HashSet { + let mut output = HashSet::new(); + for i in input { + output.insert(i); + } + output +} -/// This loop pull a queue of routers to be registered, batches them and setting up their -/// nonces, and sends a registrations request for all of them +/// This function starts a separate thread that monitors the registraiton batch lazy static variable and every REGISTRATION_LOOP_SPEED seconds +/// sends a batch register tx to the smart contract pub fn register_client_batch_loop( web3_url: String, contract_addr: Address, @@ -36,22 +35,75 @@ pub fn register_client_batch_loop( // this will always be an error, so it's really just a loop statement // with some fancy destructuring while let Err(e) = { - let web3 = web3_url.clone(); + let web3_url = web3_url.clone(); thread::spawn(move || { + let web3_url = web3_url.clone(); // Our Exit state variabl let runner = System::new(); runner.block_on(async move { loop { let start = Instant::now(); - info!("Registration Loop tick"); + // there is no one in the queue + let list = get_reg_queue(); + if list.is_empty() { + thread::sleep(WEB3_TIMEOUT); + continue + } + + let web3 = Web3::new(&web3_url, WEB3_TIMEOUT); + // get a copy of all existing clients, we do this in order to handle a potential future edgecase where more than one registration server + // is operating at a time and the same user attempts to register to more than one before the transaction can be sent. Without this check + // once a already registered user is in the queue all future transactions would fail and the server would no longer operate correctly + let all_clients = match get_all_regsitered_clients(&web3, our_private_key.to_address(), contract_addr).await { + Ok(all_clients) => all_clients, + Err(e) => { + error!("Failed to get list of already registered clients {:?}, retrying", e); + continue; + }, + }; + let all_clients = get_clients_hashset(all_clients); + + let mut clients_to_register = Vec::new(); + for client in list { + if !all_clients.contains(&client) { + clients_to_register.push(client); + if clients_to_register.len() > MAX_BATCH_SIZE { + break; + } + } + } + // there is no one once we filter already registered users + if clients_to_register.is_empty() { + thread::sleep(WEB3_TIMEOUT); + continue + } - register_client_batch_internal( - web3.clone(), + info!("Prepped user batch sending register tx"); + match add_users_to_registered_list( + &web3, + clients_to_register.clone(), contract_addr, our_private_key, + Some(TX_TIMEOUT), + vec![SendTxOption::GasPriorityFee(1000000000u128.into()), SendTxOption::GasMaxFee(4000000000u128.into())], ) - .await; + .await + { + Ok(_) => { + info!( + "Successfully registered {} clients!", + clients_to_register.len() + ); + // remove all the successfully registered clients from the queue + for client in clients_to_register { + remove_client_from_reg_queue(client); + } + } + Err(e) => { + error!("Failed to register clients with {:?}, will try again!", e) + } + } info!("Registration loop elapsed in = {:?}", start.elapsed()); if start.elapsed() < REGISTRATION_LOOP_SPEED { @@ -67,10 +119,7 @@ pub fn register_client_batch_loop( }) .join() } { - error!( - "Rita client Exit Manager loop thread paniced! Respawning {:?}", - e - ); + error!("Registration loop thread panicked! Respawning {:?}", e); if Instant::now() - last_restart < Duration::from_secs(60) { error!("Restarting too quickly, leaving it to auto rescue!"); let sys = System::current(); @@ -80,223 +129,3 @@ pub fn register_client_batch_loop( } }); } - -async fn get_base_fee_per_gas(contact: &Web3) -> Result, Web3Error> { - match contact.eth_get_latest_block().await { - Ok(eth_block) => Ok(eth_block.base_fee_per_gas), - Err(e) => Err(e), - } -} - -pub fn set_tx_data(tx: &mut Transaction, encoding: Vec) { - match tx { - Transaction::Legacy { .. } | Transaction::Eip2930 { .. } => {} - Transaction::Eip1559 { data, .. } => *data = encoding, - } -} - -pub fn set_tx_nonce(tx: &mut Transaction, n: Uint256) { - match tx { - Transaction::Legacy { .. } | Transaction::Eip2930 { .. } => {} - Transaction::Eip1559 { nonce, .. } => *nonce = n, - } -} - -pub async fn register_client_batch_internal( - web3: String, - contract_addr: Address, - our_private_key: PrivateKey, -) { - info!("Register batch clients tick"); - let reg_clients = get_reg_batch(); - let contact = Web3::new(&web3, WEB3_TIMEOUT); - - // request tx params - let chain_id_fut = contact.net_version(); - let nonce_fut = contact.eth_get_transaction_count(our_private_key.to_address()); - let our_balance_fut = contact.eth_get_balance(our_private_key.to_address()); - let base_fee_per_gas_fut = get_base_fee_per_gas(&contact); - let our_balance; - let mut nonce; - let chain_id; - let base_fee_per_gas; - - let (our_balance_fut, nonce_fut, base_fee_per_gas_fut, chain_id_fut) = join4( - our_balance_fut, - nonce_fut, - base_fee_per_gas_fut, - chain_id_fut, - ) - .await; - - match ( - our_balance_fut, - nonce_fut, - base_fee_per_gas_fut, - chain_id_fut, - ) { - (Ok(a), Ok(b), Ok(c), Ok(d)) => { - our_balance = a; - nonce = b; - base_fee_per_gas = c; - chain_id = d; - info!("Received all tx params successfully!"); - } - error => { - error!( - "Atleast one of the four requested params failed: {:?}", - error - ); - error!("Cant register routers, panicing!"); - let sys = System::current(); - sys.stop(); - panic!( - "{}", - format!("Unable to get params to register routers:\n {:?}", error) - ); - } - } - - let mut max_fee_per_gas = match base_fee_per_gas { - Some(bf) => bf * 2u8.into(), - None => { - // No point in keep this loop running if it cant register any routers - panic!("Pre London, cant get base fee"); - } - }; - - let base_fee_per_gas = base_fee_per_gas.unwrap(); - - // Create a tx template to use for our reg batch - let mut prepared_tx = Transaction::Eip1559 { - chain_id: chain_id.into(), - nonce, - max_priority_fee_per_gas: 1u8.into(), - max_fee_per_gas, - // populated later - gas_limit: 0u8.into(), - to: contract_addr, - value: 0u32.into(), - // populated later, using some dummy for now - data: encode_call( - "add_registered_user((uint128,uint256,address))", - &[AbiToken::Struct(vec![ - AbiToken::Uint(0u8.into()), - AbiToken::Uint(0u8.into()), - AbiToken::Address(our_private_key.to_address()), - ])], - ) - .expect("Why does this fail?"), - signature: None, - access_list: vec![], - }; - - let mut gas_limit = contact - .eth_estimate_gas(TransactionRequest::from_transaction( - &prepared_tx, - our_private_key.to_address(), - )) - .await - .expect("Cannot get gas estimate to setup gas limit"); - - // multiply limit by gasLimitMultiplier - let gas_limit_128 = gas_limit.to_u128(); - if let Some(v) = gas_limit_128 { - gas_limit = ((v as f32 * 5.0) as u128).into() - } else { - gas_limit *= 5_u128.into() - } - prepared_tx.set_gas_limit(gas_limit); - - // this is an edge case where we are about to send a transaction that can't possibly be valid - if max_fee_per_gas * gas_limit > our_balance { - if base_fee_per_gas * gas_limit > our_balance { - let err = Web3Error::InsufficientGas { - balance: our_balance, - base_gas: base_fee_per_gas, - gas_required: gas_limit, - }; - error!("{:?}", err); - panic!("{:?}", err); - } - // this will give some value >= base_fee_per_gas * gas_limit - // in post-london and some non zero value in pre-london - max_fee_per_gas = our_balance / gas_limit; - } - prepared_tx.set_max_fee_per_gas(max_fee_per_gas); - - info!("Starting client batching"); - // Start batching tx for each client - let mut batch: Vec = vec![]; - trace!("Reg clients are: {:?}", reg_clients); - // we limit batch size to 100 clients at a time - let mut batch_len = 0; - for id in reg_clients { - if let IpAddr::V6(mesh_ip_v6) = id.mesh_ip { - let mut prepared_tx_copy = prepared_tx.clone(); - set_tx_data( - &mut prepared_tx_copy, - match encode_call( - "add_registered_user((uint128,uint256,address))", - &[AbiToken::Struct(vec![ - AbiToken::Uint(u128::from(mesh_ip_v6).into()), - AbiToken::Uint(id.wg_public_key.into()), - AbiToken::Address(id.eth_address), - ])], - ) { - Ok(a) => a, - Err(e) => { - error!("REGISTRATION ERROR: Why cant we encode this call? {}", e); - continue; - } - }, - ); - set_tx_nonce(&mut prepared_tx_copy, nonce); - let prepared_tx_copy: Transaction = prepared_tx_copy.sign(&our_private_key, None); - - match contact.send_prepared_transaction(prepared_tx_copy).await { - Ok(tx_id) => { - //increment nonce for next tx - nonce += 1u64.into(); - batch_len += 1; - remove_client_from_reg_batch(id); - info!( - "BATCH CLIENT {}: {} with txid {}", - batch_len, id.mesh_ip, tx_id - ); - batch.push(tx_id); - if batch_len >= MAX_BATCH_SIZE { - break; - } - } - Err(e) => { - error!("Failed registration for {} with {}", id.wg_public_key, e); - } - } - } else { - error!("{} Doesnt have a v6 mesh ip??", id); - } - } - - // Join on txs - let res = wait_for_txids(batch, &contact).await; - for e in res { - match e { - Err(e) => error!("Failed tx with {}", e), - _ => info!("Tx is ok!"), - } - } -} - -/// utility function that waits for a large number of txids to enter a block -async fn wait_for_txids( - txids: Vec, - web3: &Web3, -) -> Vec> { - let mut wait_for_txid = Vec::new(); - for txid in txids { - let wait = web3.wait_for_transaction(txid, TX_TIMEOUT, None); - wait_for_txid.push(wait); - } - join_all(wait_for_txid).await -} diff --git a/rita_common/src/network_endpoints/mod.rs b/rita_common/src/network_endpoints/mod.rs index cf60a40db..2a176d5d8 100644 --- a/rita_common/src/network_endpoints/mod.rs +++ b/rita_common/src/network_endpoints/mod.rs @@ -58,7 +58,7 @@ pub async fn make_payments_v2(item: Json>) -> HttpResponse { } pub async fn hello_response(item: Json, req: HttpRequest) -> HttpResponse { - info!("In Hello response handler!!"); + trace!("In Hello response handler!!"); let their_id = item.into_inner(); let err_mesg = "Malformed hello tcp packet!"; @@ -67,7 +67,7 @@ pub async fn hello_response(item: Json, req: HttpRequest) -> Http None => return HttpResponse::build(StatusCode::from_u16(400u16).unwrap()).json(err_mesg), }; - info!("Got Hello from {:?}", req.peer_addr()); + trace!("Got Hello from {:?}", req.peer_addr()); trace!("opening tunnel in hello_response for {:?}", their_id); let peer = Peer { diff --git a/rita_common/src/tunnel_manager/id_callback.rs b/rita_common/src/tunnel_manager/id_callback.rs index d15b2a86e..64ef291ed 100644 --- a/rita_common/src/tunnel_manager/id_callback.rs +++ b/rita_common/src/tunnel_manager/id_callback.rs @@ -30,7 +30,7 @@ impl IdentityCallback { /// we now must attach to their tunnel entry. If we also return a bool for if the tunnel already /// exists pub fn tm_identity_callback(msg: IdentityCallback) -> Result<(Tunnel, bool), RitaCommonError> { - info!("Tm identity callback with msg: {:?}", msg); + trace!("Tm identity callback with msg: {:?}", msg); let tm_pin = &mut *TUNNEL_MANAGER.write().unwrap(); let tunnel_manager = get_tunnel_manager_write_ref(tm_pin); tunnel_manager.open_tunnel(msg.local_identity, msg.peer) diff --git a/rita_db_migration/src/lib.rs b/rita_db_migration/src/lib.rs index 670cbd0ac..f57943d2a 100644 --- a/rita_db_migration/src/lib.rs +++ b/rita_db_migration/src/lib.rs @@ -13,15 +13,12 @@ use std::{collections::HashSet, time::Duration}; use crate::schema::clients::dsl::clients; use althea_types::Identity; -use clarity::{Address, PrivateKey}; +use clarity::Address; use diesel::{r2d2::ConnectionManager, PgConnection, RunQueryDsl}; use error::RitaDBMigrationError; use models::Client; use r2d2::PooledConnection; -use rita_client_registration::{ - add_client_to_reg_batch, - client_db::{add_user_admin, get_all_regsitered_clients}, -}; +use rita_client_registration::{add_client_to_reg_queue, client_db::get_all_regsitered_clients}; use web30::client::Web3; const WEB3_TIMEOUT: Duration = Duration::from_secs(60); @@ -105,7 +102,7 @@ async fn add_clients_to_reg_queue( if !existing_users.contains(&id) { info!("Adding user {}", id.mesh_ip); - add_client_to_reg_batch(id); + add_client_to_reg_queue(id); } else { warn!("User {} already exists!", id.mesh_ip); } @@ -131,25 +128,3 @@ pub fn get_database_connection( } } } - -pub async fn db_migration_user_admin( - web3_url: String, - state_admin_key: PrivateKey, - user_to_add: Address, - db_addr: Address, -) { - let contact = Web3::new(&web3_url, WEB3_TIMEOUT); - - // We need to be a user admin to add users - let res = add_user_admin( - &contact, - db_addr, - user_to_add, - state_admin_key, - None, - vec![], - ) - .await; - - res.expect("Unable to register a user admin to migrate clients"); -} diff --git a/rita_exit/src/lib.rs b/rita_exit/src/lib.rs index f5bc67d3a..4e0cd306e 100644 --- a/rita_exit/src/lib.rs +++ b/rita_exit/src/lib.rs @@ -35,12 +35,12 @@ use rita_common::dashboard::wallet::*; use rita_common::dashboard::wg_key::*; use rita_common::middleware; use rita_common::network_endpoints::version; +use std::path::PathBuf; use std::thread; #[derive(Debug, Deserialize, Default)] pub struct Args { - pub flag_config: String, - pub flag_future: bool, + pub flag_config: PathBuf, } pub fn get_exit_usage(version: &str, git_hash: &str) -> String { @@ -48,7 +48,6 @@ pub fn get_exit_usage(version: &str, git_hash: &str) -> String { "Usage: rita_exit --config= Options: -c, --config= Name of config file - --future Enable B side of A/B releases About: Version {READABLE_VERSION} - {version} git hash {git_hash}" diff --git a/rita_exit/src/network_endpoints/mod.rs b/rita_exit/src/network_endpoints/mod.rs index bb18e7177..3379d97a2 100644 --- a/rita_exit/src/network_endpoints/mod.rs +++ b/rita_exit/src/network_endpoints/mod.rs @@ -22,7 +22,7 @@ use althea_types::{ use althea_types::{EncryptedExitList, Identity}; use althea_types::{ExitList, WgKey}; use num256::Int256; -use rita_client_registration::client_db::get_client_exit_list; +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::payment_validator::calculate_unverified_payments; @@ -318,7 +318,7 @@ pub async fn get_exit_list(request: Json) -> HttpRe let contract_addr = rita_exit.exit_network.registered_users_contract_addr; let ret: ExitList = ExitList { - exit_list: match get_client_exit_list(&contact, our_addr, contract_addr).await { + exit_list: match get_exits_list(&contact, our_addr, contract_addr).await { Ok(a) => { let exit_regions = rita_exit.network.allowed_countries; let accepted_payments = rita_exit.network.payment_chains; @@ -396,7 +396,7 @@ pub async fn get_exit_list_v2(request: Json) -> Htt let contract_addr = rita_exit.exit_network.registered_users_contract_addr; let ret: ExitListV2 = ExitListV2 { - exit_list: match get_client_exit_list(&contact, our_addr, contract_addr).await { + exit_list: match get_exits_list(&contact, our_addr, contract_addr).await { Ok(a) => a, Err(e) => { error!( diff --git a/settings/src/exit.rs b/settings/src/exit.rs index 0c9d38c52..651b8c86c 100644 --- a/settings/src/exit.rs +++ b/settings/src/exit.rs @@ -7,7 +7,7 @@ use clarity::Address; use ipnetwork::IpNetwork; use std::collections::HashSet; use std::net::Ipv4Addr; -use std::path::Path; +use std::path::{Path, PathBuf}; /// This is the network settings specific to rita_exit #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] @@ -241,9 +241,11 @@ impl RitaExitSettingsStruct { Ok(ret) } - pub fn new_watched(file_name: &str) -> Result { - if !Path::new(file_name).exists() { - return Err(SettingsError::FileNotFoundError(file_name.to_string())); + pub fn new_watched(file_name: PathBuf) -> Result { + if !Path::new(&file_name).exists() { + return Err(SettingsError::FileNotFoundError( + file_name.as_os_str().to_string_lossy().to_string(), + )); } let config_toml = std::fs::read_to_string(file_name)?; diff --git a/settings/src/lib.rs b/settings/src/lib.rs index 9ad8733fa..c6c783bb5 100644 --- a/settings/src/lib.rs +++ b/settings/src/lib.rs @@ -144,13 +144,19 @@ pub fn write_config() -> Result<(), SettingsError> { Some(Settings::Adaptor(adapt)) => adapt.adaptor.write_config(), Some(Settings::Client(settings)) => { let filename = FLAG_CONFIG.read().unwrap(); - let filename = filename.get(&netns).unwrap(); - settings.write(filename.clone()) + let filename = filename.get(&netns); + if let Some(filename) = filename { + settings.write(filename.clone())? + } + Ok(()) } Some(Settings::Exit(settings)) => { let filename = FLAG_CONFIG.read().unwrap(); - let filename = filename.get(&netns).unwrap(); - settings.write(filename.clone()) + let filename = filename.get(&netns); + if let Some(filename) = filename { + settings.write(filename.clone())? + } + Ok(()) } None => panic!("expected settings but got none"), } diff --git a/solidity/contract-deployer.ts b/solidity/contract-deployer.ts index d5a0ae7a4..8733b059b 100644 --- a/solidity/contract-deployer.ts +++ b/solidity/contract-deployer.ts @@ -46,51 +46,19 @@ async function deploy() { await sleep(1000); } - console.log("Test mode, deploying ERC20 contracts"); - - // this handles several possible locations for the ERC20 artifacts - // var erc20_a_path: string - // var erc20_b_path: string - // var erc20_c_path: string - // var erc721_a_path: string var althea_db_path: string - // const main_location_a = "/althea_rs/solidity/artifacts/contracts/TestERC20A.sol/TestERC20A.json" - // const main_location_b = "/althea_rs/solidity/artifacts/contracts/TestERC20B.sol/TestERC20B.json" - // const main_location_c = "/althea_rs/solidity/artifacts/contracts/TestERC20C.sol/TestERC20C.json" - // const main_location_721_a = "/althea_rs/solidity/artifacts/contracts/TestERC721A.sol/TestERC721A.json" const main_location_altheadb = "/althea_rs/solidity/artifacts/contracts/AltheaDB.sol/AltheaDB.json" - - // const alt_location_1_a = "/solidity/TestERC20A.json" - // const alt_location_1_b = "/solidity/TestERC20B.json" - // const alt_location_1_c = "/solidity/TestERC20C.json" - // const alt_location_1_721a = "/solidity/TestERC721A.json" const alt_location_1_altheadb = "/solidity/AltheaDB.json" - // const alt_location_2_a = "TestERC20A.json" - // const alt_location_2_b = "TestERC20B.json" - // const alt_location_2_c = "TestERC20C.json" - // const alt_location_2_721a = "TestERC721A.json" const alt_location_2_altheadb = "AltheaDB.json" if (fs.existsSync(main_location_altheadb)) { - // erc20_a_path = main_location_a - // erc20_b_path = main_location_b - // erc20_c_path = main_location_c - // erc721_a_path = main_location_721_a althea_db_path = main_location_altheadb } else if (fs.existsSync(alt_location_1_altheadb)) { - // erc20_a_path = alt_location_1_a - // erc20_b_path = alt_location_1_b - // erc20_c_path = alt_location_1_c - // erc721_a_path = alt_location_1_721a althea_db_path = alt_location_1_altheadb } else if (fs.existsSync(alt_location_2_altheadb)) { - // erc20_a_path = alt_location_2_a - // erc20_b_path = alt_location_2_b - // erc20_c_path = alt_location_2_c - // erc721_a_path = alt_location_2_721a althea_db_path = alt_location_2_altheadb } else { console.log("Test mode was enabled but the ERC20 contracts can't be found!") @@ -105,37 +73,6 @@ async function deploy() { // TypeScript type declaration file in typechain/ for your contract, the goal being to deploy the contract // and cast it to the generated Class in that .d.ts file - // Read ABI (blockchain API) and bytecode from disk - // const { abi, bytecode } = getContractArtifacts(erc20_a_path); - // // Create a contract deployer factory - // const erc20Factory = new ethers.ContractFactory(abi, bytecode, wallet); - // // Deploy the contract and cast to the typechain generated class - // const testERC20 = (await erc20Factory.deploy(overrides)) as TestERC20A; - // await testERC20.deployed(); // Wait - // const erc20TestAddress = testERC20.address; - // console.log("ERC20 deployed at Address - ", erc20TestAddress); - // Now testERC20 is ready to use, e.g.testERC20.transfer(from, to, amount) - - // const { abi: abi1, bytecode: bytecode1 } = getContractArtifacts(erc20_b_path); - // const erc20Factory1 = new ethers.ContractFactory(abi1, bytecode1, wallet); - // const testERC201 = (await erc20Factory1.deploy(overrides)) as TestERC20B; - // await testERC201.deployed(); - // const erc20TestAddress1 = testERC201.address; - // console.log("ERC20 deployed at Address - ", erc20TestAddress1); - - // const { abi: abi2, bytecode: bytecode2 } = getContractArtifacts(erc20_c_path); - // const erc20Factory2 = new ethers.ContractFactory(abi2, bytecode2, wallet); - // const testERC202 = (await erc20Factory2.deploy(overrides)) as TestERC20C; - // await testERC202.deployed(); - // const erc20TestAddress2 = testERC202.address; - // console.log("ERC20 deployed at Address - ", erc20TestAddress2); - - // const { abi: abi3, bytecode: bytecode3 } = getContractArtifacts(erc721_a_path); - // const erc721Factory1 = new ethers.ContractFactory(abi3, bytecode3, wallet); - // const testERC721 = (await erc721Factory1.deploy(overrides)) as TestERC721A; - // await testERC721.deployed(); - // const erc721TestAddress = testERC721.address; - // console.log("ERC721 deployed at Address - ", erc721TestAddress); const { abi: abi4, bytecode: bytecode4 } = getContractArtifacts(althea_db_path); const altheadbFactory1 = new ethers.ContractFactory(abi4, bytecode4, wallet); diff --git a/solidity/contracts/AltheaDB.sol b/solidity/contracts/AltheaDB.sol index 0d4f5660d..a9f238872 100644 --- a/solidity/contracts/AltheaDB.sol +++ b/solidity/contracts/AltheaDB.sol @@ -69,7 +69,7 @@ contract AltheaDB { // region codes component. This is mostly used for comparison and duplicate checking as we want // to ignore the region codes when adding and removing an exit to avoid having identical exits // with different region codes. - function exit_id_to_id( + function exitIdToId( ExitIdentity memory input ) public pure returns (Identity memory) { return @@ -80,15 +80,11 @@ contract AltheaDB { }); } - function get_null_identity() public pure returns (Identity memory) { + function getNullIdentity() public pure returns (Identity memory) { return Identity({mesh_ip: 0, wg_key: 0, eth_addr: address(0)}); } - function get_null_exit_identity() - public - pure - returns (ExitIdentity memory) - { + function getNullExitIdentity() public pure returns (ExitIdentity memory) { uint256[] memory empty_array; return ExitIdentity({ @@ -102,13 +98,13 @@ contract AltheaDB { }); } - function is_null_identity( + function isNullIdentity( Identity calldata input ) public pure returns (bool) { - return identities_are_equal(input, get_null_identity()); + return identitiesAreEqual(input, getNullIdentity()); } - function identities_are_equal( + function identitiesAreEqual( Identity memory a, Identity memory b ) public pure returns (bool) { @@ -124,7 +120,7 @@ contract AltheaDB { return true; } - function is_user_admin(address potential_admin) public view returns (bool) { + function isUserAdmin(address potential_admin) public view returns (bool) { for (uint256 i = 0; i < state_UserAdmins.length; i++) { if (potential_admin == state_UserAdmins[i]) { return true; @@ -133,7 +129,7 @@ contract AltheaDB { return false; } - function is_exit_admin(address potential_admin) public view returns (bool) { + function isExitAdmin(address potential_admin) public view returns (bool) { for (uint256 i = 0; i < state_ExitAdmins.length; i++) { if (potential_admin == state_ExitAdmins[i]) { return true; @@ -143,7 +139,7 @@ contract AltheaDB { } /// Deletes an entry of the provided array - function delete_array_entry(uint index, Identity[] storage array) private { + function deleteArrayEntry(uint index, Identity[] storage array) private { require(index < array.length); // copy the last element into the index that we want to delete // in the case that we want to delete the last element, just skip this @@ -156,7 +152,7 @@ contract AltheaDB { } /// Deletes an entry of the provided array - function delete_array_entry( + function deleteArrayEntry( uint index, ExitIdentity[] storage array ) private { @@ -172,7 +168,7 @@ contract AltheaDB { } /// Deletes an entry of the provided array - function delete_array_entry(uint index, address[] storage array) private { + function deleteArrayEntry(uint index, address[] storage array) private { require(index < array.length); // copy the last element into the index that we want to delete // in the case that we want to delete the last element, just skip this @@ -184,33 +180,31 @@ contract AltheaDB { array.pop(); } - function get_index_of_id( + function getIndexOfId( Identity memory id, Identity[] memory array ) private pure returns (uint256) { for (uint256 i = 0; i < array.length; i++) { - if (identities_are_equal(array[i], id)) { + if (identitiesAreEqual(array[i], id)) { return i; } } revert IdentityNotFound(); } - function get_index_of_id( + function getIndexOfId( ExitIdentity memory id, ExitIdentity[] memory array ) private pure returns (uint256) { for (uint256 i = 0; i < array.length; i++) { - if ( - identities_are_equal(exit_id_to_id(array[i]), exit_id_to_id(id)) - ) { + if (identitiesAreEqual(exitIdToId(array[i]), exitIdToId(id))) { return i; } } revert IdentityNotFound(); } - function get_index_of_admin( + function getIndexOfAdmin( address admin, address[] memory array ) private pure returns (uint256) { @@ -224,7 +218,7 @@ contract AltheaDB { /// Checks both the exit and the client lists for any entry with any /// sort of duplicate ID component - function check_for_any_duplicates( + function checkForAnyDuplicates( Identity memory entry ) public view returns (bool) { if (state_registeredIps[entry.mesh_ip] == true) { @@ -245,11 +239,11 @@ contract AltheaDB { // start user and exit management functions // Add a new registered user - function add_registered_user(Identity calldata entry) public { - if (is_user_admin(msg.sender)) { + function addRegisteredUser(Identity calldata entry) public { + if (isUserAdmin(msg.sender)) { // if any client or exit currently registered has overlapping data, do not allow the // registration to continue - if (check_for_any_duplicates(entry)) { + if (checkForAnyDuplicates(entry)) { revert DuplicateUser(); } @@ -264,11 +258,18 @@ contract AltheaDB { } } + // Utility function that registers users in bulk within a single transaction + function addRegisteredUsersBulk(Identity[] calldata users) public { + for (uint256 i = 0; i < users.length; i++) { + addRegisteredUser(users[i]); + } + } + // Remove a new registered user - function remove_registered_user(Identity calldata entry) public { - if (is_user_admin(msg.sender)) { - uint256 index = get_index_of_id(entry, state_registeredUsers); - delete_array_entry(index, state_registeredUsers); + function removeRegisteredUser(Identity calldata entry) public { + if (isUserAdmin(msg.sender)) { + uint256 index = getIndexOfId(entry, state_registeredUsers); + deleteArrayEntry(index, state_registeredUsers); state_registeredAddr[entry.eth_addr] = false; state_registeredIps[entry.mesh_ip] = false; @@ -280,12 +281,19 @@ contract AltheaDB { } } + // Utility function that removes exits in bulk within a single transaction + function removeRegisteredUsersBulk(Identity[] calldata users) public { + for (uint256 i = 0; i < users.length; i++) { + removeRegisteredUser(users[i]); + } + } + // Add a new registered exit - function add_registered_exit(ExitIdentity calldata entry) public { - if (is_exit_admin(msg.sender)) { + function addRegisteredExit(ExitIdentity calldata entry) public { + if (isExitAdmin(msg.sender)) { // if any client or exit currently registered has overlapping data, do not allow the // registration to continue - if (check_for_any_duplicates(exit_id_to_id(entry))) { + if (checkForAnyDuplicates(exitIdToId(entry))) { revert DuplicateUser(); } @@ -300,11 +308,18 @@ contract AltheaDB { } } + // Utility function that registers exits in bulk within a single transaction + function addRegisteredExitsBulk(ExitIdentity[] calldata exits) public { + for (uint256 i = 0; i < exits.length; i++) { + addRegisteredExit(exits[i]); + } + } + // Remove a new registered exit - function remove_registered_exit(ExitIdentity calldata entry) public { - if (is_exit_admin(msg.sender)) { - uint256 index = get_index_of_id(entry, state_registeredExits); - delete_array_entry(index, state_registeredExits); + function removeRegisteredExit(ExitIdentity calldata entry) public { + if (isExitAdmin(msg.sender)) { + uint256 index = getIndexOfId(entry, state_registeredExits); + deleteArrayEntry(index, state_registeredExits); state_registeredAddr[entry.eth_addr] = false; state_registeredIps[entry.mesh_ip] = false; @@ -316,17 +331,20 @@ contract AltheaDB { } } + // Utility function that removes exits in bulk within a single transaction + function removeRegisteredExitsBulk(ExitIdentity[] calldata exits) public { + for (uint256 i = 0; i < exits.length; i++) { + removeRegisteredExit(exits[i]); + } + } + // start user query functions - function get_all_registered_users() - public - view - returns (Identity[] memory) - { + function getAllRegisteredUsers() public view returns (Identity[] memory) { return state_registeredUsers; } - function get_all_registered_exits() + function getAllRegisteredExits() public view returns (ExitIdentity[] memory) @@ -334,7 +352,7 @@ contract AltheaDB { return state_registeredExits; } - function get_registered_client_with_wg_key( + function getRegisteredClientWithWgKey( uint256 wg_key ) public view returns (Identity memory) { for (uint256 i = 0; i < state_registeredUsers.length; i++) { @@ -342,10 +360,10 @@ contract AltheaDB { return state_registeredUsers[i]; } } - return get_null_identity(); + return getNullIdentity(); } - function get_registered_client_with_mesh_ip( + function getRegisteredClientWithMeshIp( uint128 mesh_ip ) public view returns (Identity memory) { for (uint256 i = 0; i < state_registeredUsers.length; i++) { @@ -353,10 +371,10 @@ contract AltheaDB { return state_registeredUsers[i]; } } - return get_null_identity(); + return getNullIdentity(); } - function get_registered_client_with_eth_addr( + function getRegisteredClientWithEthAddr( address eth_addr ) public view returns (Identity memory) { for (uint256 i = 0; i < state_registeredUsers.length; i++) { @@ -364,10 +382,10 @@ contract AltheaDB { return state_registeredUsers[i]; } } - return get_null_identity(); + return getNullIdentity(); } - function get_registered_exit_with_wg_key( + function getRegisteredExitWithWgKey( uint256 wg_key ) public view returns (ExitIdentity memory) { for (uint256 i = 0; i < state_registeredExits.length; i++) { @@ -375,10 +393,10 @@ contract AltheaDB { return state_registeredExits[i]; } } - return get_null_exit_identity(); + return getNullExitIdentity(); } - function get_registered_exit_with_mesh_ip( + function getRegisteredExitWithMeshIp( uint128 mesh_ip ) public view returns (ExitIdentity memory) { for (uint256 i = 0; i < state_registeredExits.length; i++) { @@ -386,10 +404,10 @@ contract AltheaDB { return state_registeredExits[i]; } } - return get_null_exit_identity(); + return getNullExitIdentity(); } - function get_registered_exit_with_eth_addr( + function getRegisteredExitWithEthAddr( address eth_addr ) public view returns (ExitIdentity memory) { for (uint256 i = 0; i < state_registeredExits.length; i++) { @@ -397,15 +415,15 @@ contract AltheaDB { return state_registeredExits[i]; } } - return get_null_exit_identity(); + return getNullExitIdentity(); } // start admin management functions // Add a new user admin - function add_user_admin(address entry) public { + function addUserAdmin(address entry) public { if (state_admin == msg.sender) { - if (is_user_admin(entry)) { + if (isUserAdmin(entry)) { revert DuplicateAdmin(); } @@ -417,10 +435,10 @@ contract AltheaDB { } // Remove a user admin - function remove_user_admin(address entry) public { + function removeUserAdmin(address entry) public { if (state_admin == msg.sender) { - uint256 index = get_index_of_admin(entry, state_UserAdmins); - delete_array_entry(index, state_UserAdmins); + uint256 index = getIndexOfAdmin(entry, state_UserAdmins); + deleteArrayEntry(index, state_UserAdmins); emit UserAdminAddedEvent(entry); } else { revert UnathorizedCaller(); @@ -428,9 +446,9 @@ contract AltheaDB { } // Add a new exit admin - function add_exit_admin(address entry) public { + function addExitAdmin(address entry) public { if (state_admin == msg.sender) { - if (is_exit_admin(entry)) { + if (isExitAdmin(entry)) { revert DuplicateAdmin(); } @@ -442,10 +460,10 @@ contract AltheaDB { } // Remove a exit admin - function remove_exit_admin(address entry) public { + function removeExitAdmin(address entry) public { if (state_admin == msg.sender) { - uint256 index = get_index_of_admin(entry, state_ExitAdmins); - delete_array_entry(index, state_ExitAdmins); + uint256 index = getIndexOfAdmin(entry, state_ExitAdmins); + deleteArrayEntry(index, state_ExitAdmins); emit UserAdminAddedEvent(entry); } else { revert UnathorizedCaller(); diff --git a/solidity/contracts/TestERC20A.sol b/solidity/contracts/TestERC20A.sol deleted file mode 100644 index f38e52fbe..000000000 --- a/solidity/contracts/TestERC20A.sol +++ /dev/null @@ -1,20 +0,0 @@ -//SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.21; // Force solidity compliance -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -// One of three testing coins named "Bitcoin MAX" with symbol "MAX", the constructor mints tokens -// and sends them to the input addresses, see hardhat.config.ts for the private keys -contract TestERC20A is ERC20 { - constructor() ERC20("Bitcoin MAX", "MAX") { - _mint(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 100000000000000000000000000); - _mint(0xc783df8a850f42e7F7e57013759C285caa701eB6, 10000); - _mint(0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4, 10000); - _mint(0xE5904695748fe4A84b40b3fc79De2277660BD1D3, 10000); - _mint(0x92561F28Ec438Ee9831D00D1D59fbDC981b762b2, 10000); - _mint(0x2fFd013AaA7B5a7DA93336C2251075202b33FB2B, 10000); - // this is the EtherBase address for our testnet miner in - // tests/assets/ETHGenesis.json so it wil have both a lot - // of ETH and a lot of erc20 tokens to test with - _mint(0xBf660843528035a5A4921534E156a27e64B231fE, 100000000000000000000000000); - } -} diff --git a/solidity/contracts/TestERC20B.sol b/solidity/contracts/TestERC20B.sol deleted file mode 100644 index 62c013327..000000000 --- a/solidity/contracts/TestERC20B.sol +++ /dev/null @@ -1,20 +0,0 @@ -//SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.21; // Force solidity compliance -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -// One of three testing coins named "2 Ethereum" with symbol "E2H", the constructor mints tokens -// and sends them to the input addresses, see hardhat.config.ts for the private keys -contract TestERC20B is ERC20 { - constructor() ERC20("2 Ethereum", "E2H") { - _mint(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 100000000000000000000000000); - _mint(0xc783df8a850f42e7F7e57013759C285caa701eB6, 10000); - _mint(0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4, 10000); - _mint(0xE5904695748fe4A84b40b3fc79De2277660BD1D3, 10000); - _mint(0x92561F28Ec438Ee9831D00D1D59fbDC981b762b2, 10000); - _mint(0x2fFd013AaA7B5a7DA93336C2251075202b33FB2B, 10000); - // this is the EtherBase address for our testnet miner in - // tests/assets/ETHGenesis.json so it wil have both a lot - // of ETH and a lot of erc20 tokens to test with - _mint(0xBf660843528035a5A4921534E156a27e64B231fE, 100000000000000000000000000); - } -} diff --git a/solidity/contracts/TestERC20C.sol b/solidity/contracts/TestERC20C.sol deleted file mode 100644 index 734a6d612..000000000 --- a/solidity/contracts/TestERC20C.sol +++ /dev/null @@ -1,20 +0,0 @@ -//SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.21; // Force solidity compliance -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -// One of three testing coins named "Byecoin" with symbol "BYE", the constructor mints tokens -// and sends them to the input addresses, see hardhat.config.ts for the private keys -contract TestERC20C is ERC20 { - constructor() ERC20("Byecoin", "BYE") { - _mint(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 100000000000000000000000000); - _mint(0xc783df8a850f42e7F7e57013759C285caa701eB6, 10000); - _mint(0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4, 10000); - _mint(0xE5904695748fe4A84b40b3fc79De2277660BD1D3, 10000); - _mint(0x92561F28Ec438Ee9831D00D1D59fbDC981b762b2, 10000); - _mint(0x2fFd013AaA7B5a7DA93336C2251075202b33FB2B, 10000); - // this is the EtherBase address for our testnet miner in - // tests/assets/ETHGenesis.json so it wil have both a lot - // of ETH and a lot of erc20 tokens to test with - _mint(0xBf660843528035a5A4921534E156a27e64B231fE, 100000000000000000000000000); - } -} diff --git a/solidity/contracts/TestERC721A.sol b/solidity/contracts/TestERC721A.sol deleted file mode 100644 index 067779386..000000000 --- a/solidity/contracts/TestERC721A.sol +++ /dev/null @@ -1,23 +0,0 @@ -//SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.21; // Force solidity compliance -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - -// Generate NFTs with token ids 1-10 and 190-195 -contract TestERC721A is ERC721 { - constructor() ERC721("NFT PUNK", "NFTPUNK") { - uint i=0; - // mint group 1 of nfts starting at token id 1, to 10 - for (i = 1; i <= 10; i += 1) { - _mint(0xc783df8a850f42e7F7e57013759C285caa701eB6, i); - } - // mint group 2 of nfts starting at token id 190, to 199 - for (i = 190; i < 200; i += 1) { - _mint(0xc783df8a850f42e7F7e57013759C285caa701eB6, i); - } - // mint group 3 of nfts token id 200, 201, 202 - _mint(0xBf660843528035a5A4921534E156a27e64B231fE, 200); - _mint(0xBf660843528035a5A4921534E156a27e64B231fE, 201); - _mint(0xBf660843528035a5A4921534E156a27e64B231fE, 202); - _mint(0xBf660843528035a5A4921534E156a27e64B231fE, 203); - } -} diff --git a/solidity/test-utils/index.ts b/solidity/test-utils/index.ts index c5650f3f2..2fa7357aa 100644 --- a/solidity/test-utils/index.ts +++ b/solidity/test-utils/index.ts @@ -7,17 +7,8 @@ import { Signer } from "ethers"; export async function deployContracts(signer?: Signer | undefined) { - const TestERC20A = await ethers.getContractFactory("TestERC20A", signer); - const testERC20A = (await TestERC20A.deploy()) as TestERC20A; - - const TestERC20B = await ethers.getContractFactory("TestERC20B", signer); - const testERC20B = (await TestERC20B.deploy()) as TestERC20B; - - const TestERC20C = await ethers.getContractFactory("TestERC20C", signer); - const testERC20C = (await TestERC20C.deploy()) as TestERC20C; - const AltheaDB = await ethers.getContractFactory("AltheaDB", signer); const althea_db = (await AltheaDB.deploy(signer?.getAddress())) as AltheaDB; - return { testERC20A, testERC20B, testERC20C, althea_db }; + return { althea_db }; } \ No newline at end of file diff --git a/solidity/test/altheaDBTests.ts b/solidity/test/altheaDBTests.ts index a0f9ea4e6..903fc1217 100644 --- a/solidity/test/altheaDBTests.ts +++ b/solidity/test/altheaDBTests.ts @@ -70,64 +70,64 @@ async function addUser(opts: { }; const { althea_db } = await deployContracts(sender); if (opts.with_admin) { - await althea_db.add_user_admin(await sender?.getAddress()); + await althea_db.addUserAdmin(await sender?.getAddress()); if (opts.dup_admin) { - await althea_db.add_user_admin(await sender?.getAddress()); + await althea_db.addUserAdmin(await sender?.getAddress()); } } // add a bunch of admins to make sure we delete the right one - await althea_db.add_user_admin(await signers[1].getAddress()); - await althea_db.add_user_admin(await signers[2].getAddress()); - await althea_db.add_user_admin(await signers[3].getAddress()); + await althea_db.addUserAdmin(await signers[1].getAddress()); + await althea_db.addUserAdmin(await signers[2].getAddress()); + await althea_db.addUserAdmin(await signers[3].getAddress()); - await althea_db.add_registered_user(user1) - expectId(await althea_db.get_registered_client_with_eth_addr(user1.eth_addr), user1) - expectId(await althea_db.get_registered_client_with_wg_key(user1.wg_key), user1) - expectId(await althea_db.get_registered_client_with_mesh_ip(user1.mesh_ip), user1) - await althea_db.add_registered_user(user2) - expectId(await althea_db.get_registered_client_with_eth_addr(user2.eth_addr), user2) - expectId(await althea_db.get_registered_client_with_wg_key(user2.wg_key), user2) - expectId(await althea_db.get_registered_client_with_mesh_ip(user2.mesh_ip), user2) - await althea_db.add_registered_user(user3) - expectId(await althea_db.get_registered_client_with_eth_addr(user3.eth_addr), user3) - expectId(await althea_db.get_registered_client_with_wg_key(user3.wg_key), user3) - expectId(await althea_db.get_registered_client_with_mesh_ip(user3.mesh_ip), user3) - await althea_db.add_registered_user(user4) - expectId(await althea_db.get_registered_client_with_eth_addr(user4.eth_addr), user4) - expectId(await althea_db.get_registered_client_with_wg_key(user4.wg_key), user4) - expectId(await althea_db.get_registered_client_with_mesh_ip(user4.mesh_ip), user4) - await althea_db.add_registered_user(user5) - expectId(await althea_db.get_registered_client_with_eth_addr(user5.eth_addr), user5) - expectId(await althea_db.get_registered_client_with_wg_key(user5.wg_key), user5) - expectId(await althea_db.get_registered_client_with_mesh_ip(user5.mesh_ip), user5) - expect((await althea_db.get_all_registered_users()).length).to.equal(5) + await althea_db.addRegisteredUser(user1) + expectId(await althea_db.getRegisteredClientWithEthAddr(user1.eth_addr), user1) + expectId(await althea_db.getRegisteredClientWithWgKey(user1.wg_key), user1) + expectId(await althea_db.getRegisteredClientWithMeshIp(user1.mesh_ip), user1) + await althea_db.addRegisteredUser(user2) + expectId(await althea_db.getRegisteredClientWithEthAddr(user2.eth_addr), user2) + expectId(await althea_db.getRegisteredClientWithWgKey(user2.wg_key), user2) + expectId(await althea_db.getRegisteredClientWithMeshIp(user2.mesh_ip), user2) + await althea_db.addRegisteredUser(user3) + expectId(await althea_db.getRegisteredClientWithEthAddr(user3.eth_addr), user3) + expectId(await althea_db.getRegisteredClientWithWgKey(user3.wg_key), user3) + expectId(await althea_db.getRegisteredClientWithMeshIp(user3.mesh_ip), user3) + await althea_db.addRegisteredUser(user4) + expectId(await althea_db.getRegisteredClientWithEthAddr(user4.eth_addr), user4) + expectId(await althea_db.getRegisteredClientWithWgKey(user4.wg_key), user4) + expectId(await althea_db.getRegisteredClientWithMeshIp(user4.mesh_ip), user4) + await althea_db.addRegisteredUser(user5) + expectId(await althea_db.getRegisteredClientWithEthAddr(user5.eth_addr), user5) + expectId(await althea_db.getRegisteredClientWithWgKey(user5.wg_key), user5) + expectId(await althea_db.getRegisteredClientWithMeshIp(user5.mesh_ip), user5) + expect((await althea_db.getAllRegisteredUsers()).length).to.equal(5) if (opts.try_duplicate) { - await althea_db.add_registered_user(user1) + await althea_db.addRegisteredUser(user1) } if (opts.try_partial_dup) { - await althea_db.add_registered_user(partialDup) + await althea_db.addRegisteredUser(partialDup) } if (opts.remove_admin) { - await althea_db.remove_user_admin(await sender?.getAddress()); + await althea_db.removeUserAdmin(await sender?.getAddress()); // make sure the other admins are still there - assert(await althea_db.is_user_admin(await signers[1].getAddress())); + assert(await althea_db.isUserAdmin(await signers[1].getAddress())); } if (opts.cross_dup) { - await althea_db.add_exit_admin(await sender?.getAddress()); - await althea_db.add_registered_exit(crossDup) + await althea_db.addExitAdmin(await sender?.getAddress()); + await althea_db.addRegisteredExit(crossDup) } - await althea_db.remove_registered_user(user1) - expectId(await althea_db.get_registered_client_with_eth_addr(user1.eth_addr), nullUser) - expectId(await althea_db.get_registered_client_with_wg_key(user1.wg_key), nullUser) - expectId(await althea_db.get_registered_client_with_mesh_ip(user1.mesh_ip), nullUser) - expectId(await althea_db.get_registered_client_with_eth_addr(user2.eth_addr), user2) - expectId(await althea_db.get_registered_client_with_wg_key(user2.wg_key), user2) - expectId(await althea_db.get_registered_client_with_mesh_ip(user2.mesh_ip), user2) + await althea_db.removeRegisteredUser(user1) + expectId(await althea_db.getRegisteredClientWithEthAddr(user1.eth_addr), nullUser) + expectId(await althea_db.getRegisteredClientWithWgKey(user1.wg_key), nullUser) + expectId(await althea_db.getRegisteredClientWithMeshIp(user1.mesh_ip), nullUser) + expectId(await althea_db.getRegisteredClientWithEthAddr(user2.eth_addr), user2) + expectId(await althea_db.getRegisteredClientWithWgKey(user2.wg_key), user2) + expectId(await althea_db.getRegisteredClientWithMeshIp(user2.mesh_ip), user2) } async function addExit(opts: { @@ -192,49 +192,49 @@ async function addExit(opts: { }; const { althea_db } = await deployContracts(sender); if (opts.with_admin) { - await althea_db.add_exit_admin(await sender?.getAddress()); + await althea_db.addExitAdmin(await sender?.getAddress()); if (opts.dup_admin) { - await althea_db.add_exit_admin(await sender?.getAddress()); + await althea_db.addExitAdmin(await sender?.getAddress()); } } - await althea_db.add_exit_admin(await signers[1].getAddress()); - await althea_db.add_exit_admin(await signers[2].getAddress()); - await althea_db.add_exit_admin(await signers[3].getAddress()); + await althea_db.addExitAdmin(await signers[1].getAddress()); + await althea_db.addExitAdmin(await signers[2].getAddress()); + await althea_db.addExitAdmin(await signers[3].getAddress()); - await althea_db.add_registered_exit(user1) - expectId(await althea_db.get_registered_exit_with_eth_addr(user1.eth_addr), user1) - expectId(await althea_db.get_registered_exit_with_wg_key(user1.wg_key), user1) - expectId(await althea_db.get_registered_exit_with_mesh_ip(user1.mesh_ip), user1) - await althea_db.add_registered_exit(user2) - expectId(await althea_db.get_registered_exit_with_eth_addr(user2.eth_addr), user2) - expectId(await althea_db.get_registered_exit_with_wg_key(user2.wg_key), user2) - expectId(await althea_db.get_registered_exit_with_mesh_ip(user2.mesh_ip), user2) + await althea_db.addRegisteredExit(user1) + expectId(await althea_db.getRegisteredExitWithEthAddr(user1.eth_addr), user1) + expectId(await althea_db.getRegisteredExitWithWgKey(user1.wg_key), user1) + expectId(await althea_db.getRegisteredExitWithMeshIp(user1.mesh_ip), user1) + await althea_db.addRegisteredExit(user2) + expectId(await althea_db.getRegisteredExitWithEthAddr(user2.eth_addr), user2) + expectId(await althea_db.getRegisteredExitWithWgKey(user2.wg_key), user2) + expectId(await althea_db.getRegisteredExitWithMeshIp(user2.mesh_ip), user2) if (opts.try_duplicate) { - await althea_db.add_registered_exit(user1) + await althea_db.addRegisteredExit(user1) } if (opts.try_partial_dup) { - await althea_db.add_registered_exit(partialDup) + await althea_db.addRegisteredExit(partialDup) } if (opts.remove_admin) { - await althea_db.remove_exit_admin(await sender?.getAddress()); + await althea_db.removeExitAdmin(await sender?.getAddress()); // make sure the other admins are still there - assert(await althea_db.is_exit_admin(await signers[1].getAddress())); + assert(await althea_db.isExitAdmin(await signers[1].getAddress())); } if (opts.cross_dup) { - await althea_db.add_user_admin(await sender?.getAddress()); - await althea_db.add_registered_user(crossDup) + await althea_db.addUserAdmin(await sender?.getAddress()); + await althea_db.addRegisteredUser(crossDup) } - await althea_db.remove_registered_exit(user1ButDifferentRegions) - expectId(await althea_db.get_registered_exit_with_eth_addr(user1.eth_addr), nullUser) - expectId(await althea_db.get_registered_exit_with_wg_key(user1.wg_key), nullUser) - expectId(await althea_db.get_registered_exit_with_mesh_ip(user1.mesh_ip), nullUser) - expectId(await althea_db.get_registered_exit_with_eth_addr(user2.eth_addr), user2) - expectId(await althea_db.get_registered_exit_with_wg_key(user2.wg_key), user2) - expectId(await althea_db.get_registered_exit_with_mesh_ip(user2.mesh_ip), user2) + await althea_db.removeRegisteredExit(user1ButDifferentRegions) + expectId(await althea_db.getRegisteredExitWithEthAddr(user1.eth_addr), nullUser) + expectId(await althea_db.getRegisteredExitWithWgKey(user1.wg_key), nullUser) + expectId(await althea_db.getRegisteredExitWithMeshIp(user1.mesh_ip), nullUser) + expectId(await althea_db.getRegisteredExitWithEthAddr(user2.eth_addr), user2) + expectId(await althea_db.getRegisteredExitWithWgKey(user2.wg_key), user2) + expectId(await althea_db.getRegisteredExitWithMeshIp(user2.mesh_ip), user2) } diff --git a/solidity/test/erc20Transfer.ts b/solidity/test/erc20Transfer.ts deleted file mode 100644 index 499951c50..000000000 --- a/solidity/test/erc20Transfer.ts +++ /dev/null @@ -1,51 +0,0 @@ -import chai from "chai"; -import { ethers } from "hardhat"; -import { solidity } from "ethereum-waffle"; - -import { deployContracts } from "../test-utils"; - -chai.use(solidity); -const { expect } = chai; - -// This is a sample test to demonstrate how tests could be written for new contracts -// -// Important test details: -// Contract interactions happen via hardhat-ethers: https://hardhat.org/hardhat-runner/plugins/nomiclabs-hardhat-ethers -// Chai is used to make assertions https://www.chaijs.com/api/bdd/ -// Ethereum-waffle is used to extend chai and add ethereum matchers: https://ethereum-waffle.readthedocs.io/en/latest/matchers.html -// -// This test has a complicated context: The actual chain running is HardHat, a EVM testing & development layer with nice features -// like block auto-mining, account hijacking, start from an historical block height, and balance setup. This differs significantly -// from Althea-L1 since it's not a fully representative environment. In particular, anything that relies on the CosmosSDK side of -// of the blockchain (e.g. Liquid Infrastructure) will not work and must be tested in an integration test -async function runTest(opts: {}) { - // hardhat.config.ts set up several Signers with the native token (aka aalthea), these users are also granted ERC20s in the contract - // constructors, see contracts/ for examples - const signers = await ethers.getSigners(); - const sender = signers[0]; - const receiver = signers[1]; - console.log("sender", sender.address, "receiver", receiver.address); - - // Deploy several ERC20 tokens - const { testERC20A, testERC20B, testERC20C, althea_db } = await deployContracts(sender); - - const amount = 100; - // Expect is a chai test that monitors side effects and makes them available with ethereum-waffle features like .to.emit() - // So this next call will execute testERC20A.transfer (defined in the openzeppelin ERC20 source contract here: - // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L110-L122) - // and expect a "Transfer" Event to be emitted and have the parameters sender, receiver, and amount in that event. - // - // When using these expect.to.whatever() functions be careful the syntax is tricky so make sure await - // is placed inside of expect like so: - expect( - await testERC20A.transfer(receiver.address, 100) - ).to - .emit(testERC20A, 'Transfer') - .withArgs(sender.address, receiver.address, amount); -} - -describe("ERC20Transfer tests", function () { - it("emits Transfer events correctly", async function () { - await runTest({}) - }); -}); \ No newline at end of file