use solana_sdk::pubkey::Pubkey;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use dashmap::DashMap;
use std::collections::BTreeSet;
const MAX_SLOTS: usize = 1000;
const CLEANUP_BATCH_SIZE: usize = 100;
#[derive(Default)]
struct SlotAddresses {
dev_addresses: BTreeSet<Pubkey>,
bonk_dev_addresses: BTreeSet<Pubkey>,
}
pub struct GlobalState {
slot_data: DashMap<u64, SlotAddresses>,
slot_count: AtomicUsize,
generation: AtomicU64,
}
impl GlobalState {
pub fn new() -> Self {
Self {
slot_data: DashMap::new(),
slot_count: AtomicUsize::new(0),
generation: AtomicU64::new(0),
}
}
fn maybe_cleanup(&self) {
let current_count = self.slot_count.load(Ordering::Relaxed);
if current_count <= MAX_SLOTS {
return;
}
let gen = self.generation.load(Ordering::Relaxed);
if self.generation.compare_exchange_weak(gen, gen + 1, Ordering::Acquire, Ordering::Relaxed).is_err() {
return; }
let mut slots_to_remove: Vec<u64> = self.slot_data.iter()
.map(|entry| *entry.key())
.collect();
if slots_to_remove.len() <= MAX_SLOTS {
return; }
slots_to_remove.sort_unstable();
slots_to_remove.truncate(CLEANUP_BATCH_SIZE);
for slot in slots_to_remove {
self.slot_data.remove(&slot);
self.slot_count.fetch_sub(1, Ordering::Relaxed);
}
}
pub fn add_dev_address(&self, slot: u64, address: Pubkey) {
self.maybe_cleanup();
self.slot_data.entry(slot)
.and_modify(|addresses| {
addresses.dev_addresses.insert(address);
})
.or_insert_with(|| {
self.slot_count.fetch_add(1, Ordering::Relaxed);
let mut slot_addr = SlotAddresses::default();
slot_addr.dev_addresses.insert(address);
slot_addr
});
}
pub fn add_bonk_dev_address(&self, slot: u64, address: Pubkey) {
self.maybe_cleanup();
self.slot_data.entry(slot)
.and_modify(|addresses| {
addresses.bonk_dev_addresses.insert(address);
})
.or_insert_with(|| {
self.slot_count.fetch_add(1, Ordering::Relaxed);
let mut slot_addr = SlotAddresses::default();
slot_addr.bonk_dev_addresses.insert(address);
slot_addr
});
}
pub fn is_dev_address_in_slot(&self, slot: u64, address: &Pubkey) -> bool {
self.slot_data.get(&slot)
.map(|entry| entry.dev_addresses.contains(address))
.unwrap_or(false)
}
pub fn is_bonk_dev_address_in_slot(&self, slot: u64, address: &Pubkey) -> bool {
self.slot_data.get(&slot)
.map(|entry| entry.bonk_dev_addresses.contains(address))
.unwrap_or(false)
}
pub fn is_dev_address(&self, address: &Pubkey) -> bool {
self.slot_data.iter().any(|entry| entry.dev_addresses.contains(address))
}
pub fn is_bonk_dev_address(&self, address: &Pubkey) -> bool {
self.slot_data.iter().any(|entry| entry.bonk_dev_addresses.contains(address))
}
pub fn get_dev_addresses(&self) -> Vec<Pubkey> {
let mut all_addresses = BTreeSet::new();
for entry in self.slot_data.iter() {
for addr in &entry.dev_addresses {
all_addresses.insert(*addr);
}
}
all_addresses.into_iter().collect()
}
pub fn get_bonk_dev_addresses(&self) -> Vec<Pubkey> {
let mut all_addresses = BTreeSet::new();
for entry in self.slot_data.iter() {
for addr in &entry.bonk_dev_addresses {
all_addresses.insert(*addr);
}
}
all_addresses.into_iter().collect()
}
pub fn get_dev_addresses_for_slot(&self, slot: u64) -> Vec<Pubkey> {
self.slot_data.get(&slot)
.map(|entry| entry.dev_addresses.iter().copied().collect())
.unwrap_or_default()
}
pub fn get_bonk_dev_addresses_for_slot(&self, slot: u64) -> Vec<Pubkey> {
self.slot_data.get(&slot)
.map(|entry| entry.bonk_dev_addresses.iter().copied().collect())
.unwrap_or_default()
}
pub fn get_slot_count(&self) -> usize {
self.slot_count.load(Ordering::Relaxed)
}
pub fn clear_all_data(&self) {
self.slot_data.clear();
self.slot_count.store(0, Ordering::Relaxed);
self.generation.store(0, Ordering::Relaxed);
}
}
impl Default for GlobalState {
fn default() -> Self {
Self::new()
}
}
static GLOBAL_STATE: once_cell::sync::Lazy<GlobalState> =
once_cell::sync::Lazy::new(GlobalState::new);
pub fn get_global_state() -> &'static GlobalState {
&GLOBAL_STATE
}
pub fn add_dev_address(slot: u64, address: Pubkey) {
get_global_state().add_dev_address(slot, address);
}
pub fn is_dev_address(address: &Pubkey) -> bool {
get_global_state().is_dev_address(address)
}
pub fn add_bonk_dev_address(slot: u64, address: Pubkey) {
get_global_state().add_bonk_dev_address(slot, address);
}
pub fn is_bonk_dev_address(address: &Pubkey) -> bool {
get_global_state().is_bonk_dev_address(address)
}
pub fn get_dev_addresses() -> Vec<Pubkey> {
get_global_state().get_dev_addresses()
}
pub fn get_bonk_dev_addresses() -> Vec<Pubkey> {
get_global_state().get_bonk_dev_addresses()
}
pub fn get_dev_addresses_for_slot(slot: u64) -> Vec<Pubkey> {
get_global_state().get_dev_addresses_for_slot(slot)
}
pub fn get_bonk_dev_addresses_for_slot(slot: u64) -> Vec<Pubkey> {
get_global_state().get_bonk_dev_addresses_for_slot(slot)
}
pub fn get_slot_count() -> usize {
get_global_state().get_slot_count()
}
pub fn is_dev_address_in_slot(slot: u64, address: &Pubkey) -> bool {
get_global_state().is_dev_address_in_slot(slot, address)
}
pub fn is_bonk_dev_address_in_slot(slot: u64, address: &Pubkey) -> bool {
get_global_state().is_bonk_dev_address_in_slot(slot, address)
}