use std::collections::{HashSet, HashMap};
use std::sync::Arc;
use std::time::{Instant, Duration};
use parking_lot::RwLock;
use vapory_types::H256;
use tetsy_kvdb::KeyValueDB;
use types::transaction::SignedTransaction;
use crate::private_transactions::VerifiedPrivateTransaction;
use crate::private_state_db::PrivateStateDB;
use crate::log::Logging;
const MAX_REQUEST_SESSION_DURATION: u64 = 120 * 1000;
pub enum RequestType {
Verification(Arc<VerifiedPrivateTransaction>),
Creation(SignedTransaction),
}
#[derive(Clone, PartialEq)]
enum RequestState {
Syncing,
Ready,
}
struct StateRequest {
request_type: RequestType,
request_hashes: HashSet<H256>,
state: RequestState,
}
pub struct PrivateStateStorage {
private_state_db: Arc<PrivateStateDB>,
requests: RwLock<Vec<StateRequest>>,
syncing_hashes: RwLock<HashMap<H256, Instant>>,
}
impl PrivateStateStorage {
pub fn new(db: Arc<dyn KeyValueDB>) -> Self {
PrivateStateStorage {
private_state_db: Arc::new(PrivateStateDB::new(db)),
requests: RwLock::new(Vec::new()),
syncing_hashes: RwLock::default(),
}
}
pub fn requests_ready(&self) -> bool {
let requests = self.requests.read();
requests.iter().find(|r| r.state == RequestState::Ready).is_some()
}
pub fn state_sync_completed(&self, synced_state_hash: &H256) {
let mut syncing_hashes = self.syncing_hashes.write();
syncing_hashes.remove(synced_state_hash);
self.mark_hash_ready(synced_state_hash);
}
pub fn private_state_db(&self) -> Arc<PrivateStateDB> {
self.private_state_db.clone()
}
pub fn add_request(&self, request_type: RequestType, request_hashes: HashSet<H256>) -> Vec<H256> {
let request = StateRequest {
request_type: request_type,
request_hashes: request_hashes.clone(),
state: RequestState::Syncing,
};
let mut requests = self.requests.write();
requests.push(request);
let mut new_hashes = Vec::new();
for hash in request_hashes {
let mut hashes = self.syncing_hashes.write();
if hashes.insert(hash, Instant::now() + Duration::from_millis(MAX_REQUEST_SESSION_DURATION)).is_none() {
new_hashes.push(hash);
}
}
new_hashes
}
pub fn drain_ready_requests(&self) -> Vec<RequestType> {
let mut requests_queue = self.requests.write();
let mut drained = Vec::new();
let mut i = 0;
while i != requests_queue.len() {
if requests_queue[i].state == RequestState::Ready {
let request = requests_queue.remove(i);
drained.push(request.request_type);
} else {
i += 1;
}
}
drained
}
pub fn tick(&self, logging: &Option<Logging>) {
let mut syncing_hashes = self.syncing_hashes.write();
let current_time = Instant::now();
syncing_hashes
.iter()
.filter(|&(_, expiration_time)| *expiration_time >= current_time)
.for_each(|(hash, _)| self.mark_hash_stale(&hash, logging));
syncing_hashes.retain(|_, expiration_time| *expiration_time < current_time);
}
fn mark_hash_ready(&self, ready_hash: &H256) {
let mut requests = self.requests.write();
for request in requests.iter_mut() {
request.request_hashes.remove(ready_hash);
if request.request_hashes.is_empty() && request.state == RequestState::Syncing {
request.state = RequestState::Ready;
}
}
}
fn mark_hash_stale(&self, stale_hash: &H256, logging: &Option<Logging>) {
let mut requests = self.requests.write();
requests.retain(|request| {
let mut delete_request = false;
if request.request_hashes.contains(stale_hash) {
let tx_hash;
match &request.request_type {
RequestType::Verification(transaction) => {
tx_hash = transaction.transaction_hash;
}
RequestType::Creation(transaction) => {
tx_hash = transaction.hash();
if let Some(ref logging) = logging {
logging.private_state_sync_failed(&tx_hash);
}
}
}
trace!(target: "privatetx", "Private state request for {:?} staled due to timeout", &tx_hash);
delete_request = true;
}
!delete_request
});
}
}