Node icon indicating copy to clipboard operation
Node copied to clipboard

Design And Build Malefactor Banning for the Database

Open dnwiebe opened this issue 6 years ago • 1 comments

Malefactors should be bannable by IP address, Node public key, and earning wallet.

Bans by IP should be temporary (probably hours) in case the IP is transferred to another user who isn't a malefactor.

Bans by public key and wallet should be theoretically permanent, but truly permanent bans raise the specter of a denial-of-service attack by filling up the database with malefactor bans so that each hop is significantly delayed while the Node looks through its list of bans to see whether the purveyor of the current CORES package is among them. Therefore even public-key and wallet bans should probably be time-limited, although more generously than IP bans.

Create a database table or tables to persist malefactor bans from run to run (don't forget the database migration and CURRENT_SCHEMA_VERSION bump), and provide a facilities in the code for A) easily creating new malefactor bans and B) caching malefactor bans in memory to avoid a database dip every time a CORES package is received.

Don't write code yet to check for or respond to malefactor bans.

dnwiebe avatar Nov 03 '19 14:11 dnwiebe

Some example pseudocode:

#[derive (Clone)]
struct MalefactorCache {
	ip_addrs: Arc<Mutex<HashSet<IpAddr>>>,
	public_keys: Arc<Mutex<HashSet<PublicKey>>>,
	earning_wallets: Arc<Mutex<HashSet<Wallet>>>,
	consuming_wallets: Arc<Mutex<HashSet<Wallet>>>,
	dao: Arc<Mutex<MalefactorDao>>,
}

impl MalefactorCache {
	pub fn new(conn: DatabaseConnection) -> Self {
		let dao = MalefactorDao::new (conn);

		// Make HashSets and populate them from dao
		Self {
			ip_addrs: Arc::new(Mutex::new(ip_addrs)),
			public_keys: ...,
			earning_wallets: ...,
			consuming_wallets: ...,
			dao: Arc::new(Mutex::new(dao))
		}
	}

	pub fn check_ip_addr(&self, ip_addr: IpAddr) -> boolean {
		self.ip_addrs.lock().expect("Aaaagh!").contains(&ip_addr)
	}
	// ...other three...

	pub fn add_malefactor(&mut self, 
		ip_addr_opt: Option<IpAddr>, 
		public_key_opt: Option<PublicKey>,
		...,
		reason: MalefactorOffense,
		db_password: &str
	) {
		if let Some(ip_addr) = ip_addr_opt {
			self.ip_addrs.lock().expect("Aaagh!").push (ip_addr);
		}
		// ...other three
		let dao = self.dao.lock().expect("Aaagh!");
		dao.time_out_malefactors();
		dao.add_malefactor(ip_addr_opt, public_key_opt, ...);
	}

	pub fn reencrypt_data(&mut self, old_password: Option<String>, new_password: String) {
		let dao = self.dao.lock().expect("Aaagh!");
		BEGIN TRANSACTION
		ALTER TABLE MALEFACTORS RENAME TO MALEFACTORS_OLD;
		CREATE TABLE MALEFACTORS (...table schema...); --maybe get this from DbInitializer somehow
		let stream SELECT <columns> FROM MALEFACTORS_OLD;
		stream.iter().for_each(|old_row| {
			let new_row = reencrypt(old_row, old_password, new_password);
			INSERT INTO MALEFACTORS <new_row>
		});
		DROP TABLE MALEFACTORS_OLD;
		COMMIT_TRANSACTION
	}
}


// ActorSystemFactory
fn create_actors() {
	// ...
	conn = DatabaseConnection::new(...whatever...);
	let malefactor_cache = MalefactorCache::new(conn);
	let neighborhood_config = NeighborhoodConfig {
		//...
		malefactor_cache: malefactor_cache.clone(),
		//...
	}
	let neighborhood = Neighborhood::new(neighborhood_config);
	let neighborhood_addr = neighborhood.start();
	// ...
}

dnwiebe avatar Apr 19 '24 11:04 dnwiebe