use crate::{
chain_api::{get_api, get_rpc, EntropyConfig},
launch::Configuration,
signing_client::ListenerState,
};
use entropy_kvdb::kv_manager::KvManager;
use entropy_shared::X25519PublicKey;
use serde::{Deserialize, Serialize};
use sp_core::{crypto::AccountId32, sr25519, Pair};
use std::{
collections::HashMap,
sync::{Arc, RwLock},
};
use subxt::{
backend::legacy::LegacyRpcMethods, tx::PairSigner, utils::AccountId32 as SubxtAccountId32,
OnlineClient,
};
use thiserror::Error;
use x25519_dalek::StaticSecret;
#[derive(Clone, PartialEq, Eq)]
pub enum TssState {
NoChainConnection,
ReadOnlyChainConnection,
Ready,
}
impl TssState {
fn new() -> Self {
TssState::NoChainConnection
}
fn is_ready(&self) -> bool {
self == &TssState::Ready
}
fn can_read_from_chain(&self) -> bool {
self != &TssState::NoChainConnection
}
}
#[derive(Debug, Deserialize, Serialize)]
pub enum BlockNumberFields {
LatestBlock,
NewUser,
Reshare,
Attest,
ProactiveRefresh,
}
#[derive(Default, Clone)]
pub struct BlockNumbers {
pub latest_block: Arc<RwLock<u32>>,
pub new_user: Arc<RwLock<u32>>,
pub reshare: Arc<RwLock<u32>>,
pub attest: Arc<RwLock<u32>>,
pub proactive_refresh: Arc<RwLock<u32>>,
}
#[derive(Clone)]
pub struct Cache {
pub listener_state: ListenerState,
pub tss_state: Arc<RwLock<TssState>>,
pub request_limit: Arc<RwLock<HashMap<String, u32>>>,
pub encryption_key_backup_provider: Arc<RwLock<HashMap<AccountId32, [u8; 32]>>>,
pub attestation_nonces: Arc<RwLock<HashMap<X25519PublicKey, [u8; 32]>>>,
pub block_numbers: Arc<BlockNumbers>,
}
impl Default for Cache {
fn default() -> Self {
Self::new()
}
}
impl Cache {
pub fn new() -> Self {
Self {
listener_state: ListenerState::default(),
tss_state: Arc::new(RwLock::new(TssState::new())),
request_limit: Default::default(),
encryption_key_backup_provider: Default::default(),
attestation_nonces: Default::default(),
block_numbers: Default::default(),
}
}
pub fn is_ready(&self) -> bool {
match self.tss_state.read() {
Ok(state) => state.is_ready(),
_ => false,
}
}
pub fn can_read_from_chain(&self) -> bool {
match self.tss_state.read() {
Ok(state) => state.can_read_from_chain(),
_ => false,
}
}
pub fn connected_to_chain_node(&self) -> Result<(), AppStateError> {
let mut tss_state =
self.tss_state.write().map_err(|e| AppStateError::PosionError(e.to_string()))?;
if *tss_state == TssState::NoChainConnection {
*tss_state = TssState::ReadOnlyChainConnection;
}
Ok(())
}
pub fn make_ready(&self) -> Result<(), AppStateError> {
let mut tss_state =
self.tss_state.write().map_err(|e| AppStateError::PosionError(e.to_string()))?;
*tss_state = TssState::Ready;
Ok(())
}
pub fn write_to_request_limit(&self, key: String, value: u32) -> Result<(), AppStateError> {
self.clear_poisioned_request_limit();
let mut request_limit =
self.request_limit.write().map_err(|e| AppStateError::PosionError(e.to_string()))?;
request_limit.insert(key, value);
Ok(())
}
pub fn exists_in_request_limit(&self, key: &String) -> Result<bool, AppStateError> {
self.clear_poisioned_request_limit();
let request_limit =
self.request_limit.read().map_err(|e| AppStateError::PosionError(e.to_string()))?;
Ok(request_limit.contains_key(key))
}
pub fn remove_from_request_limit(&self, key: &String) -> Result<(), AppStateError> {
self.clear_poisioned_request_limit();
let mut request_limit =
self.request_limit.write().map_err(|e| AppStateError::PosionError(e.to_string()))?;
request_limit.remove(key);
Ok(())
}
pub fn read_from_request_limit(&self, key: &String) -> Result<Option<u32>, AppStateError> {
self.clear_poisioned_request_limit();
let request_limit =
self.request_limit.read().map_err(|e| AppStateError::PosionError(e.to_string()))?;
Ok(request_limit.get(key).cloned())
}
pub fn clear_request_limit(&self) -> Result<(), AppStateError> {
self.clear_poisioned_request_limit();
let mut request_limit =
self.request_limit.write().map_err(|e| AppStateError::PosionError(e.to_string()))?;
request_limit.clear();
Ok(())
}
pub fn clear_poisioned_request_limit(&self) {
if self.request_limit.is_poisoned() {
self.request_limit.clear_poison()
}
}
pub fn read_write_to_block_numbers(
&self,
key: BlockNumberFields,
value: u32,
) -> Result<u32, AppStateError> {
let block_number_target = self.get_block_number_target(&key);
self.clear_poisioned_block_numbers(&block_number_target);
let mut block_number =
block_number_target.write().map_err(|e| AppStateError::PosionError(e.to_string()))?;
let current_number = *block_number;
*block_number = value;
Ok(current_number)
}
pub fn write_to_block_numbers(
&self,
key: BlockNumberFields,
value: u32,
) -> Result<(), AppStateError> {
let block_number_target = self.get_block_number_target(&key);
self.clear_poisioned_block_numbers(&block_number_target);
let mut block_number =
block_number_target.write().map_err(|e| AppStateError::PosionError(e.to_string()))?;
*block_number = value;
Ok(())
}
pub fn read_from_block_numbers(&self, key: &BlockNumberFields) -> Result<u32, AppStateError> {
let block_number_target = self.get_block_number_target(key);
self.clear_poisioned_block_numbers(&block_number_target);
let block_number =
block_number_target.read().map_err(|e| AppStateError::PosionError(e.to_string()))?;
Ok(*block_number)
}
pub fn clear_poisioned_block_numbers(&self, lock: &Arc<RwLock<u32>>) {
if lock.is_poisoned() {
lock.clear_poison()
}
}
pub fn get_block_number_target(&self, key: &BlockNumberFields) -> Arc<RwLock<u32>> {
match key {
BlockNumberFields::LatestBlock => self.block_numbers.latest_block.clone(),
BlockNumberFields::NewUser => self.block_numbers.new_user.clone(),
BlockNumberFields::Reshare => self.block_numbers.reshare.clone(),
BlockNumberFields::Attest => self.block_numbers.attest.clone(),
BlockNumberFields::ProactiveRefresh => self.block_numbers.proactive_refresh.clone(),
}
}
pub fn unsubscribed_peers(
&self,
session_id: &entropy_protocol::SessionId,
) -> Result<Vec<subxt::utils::AccountId32>, AppStateError> {
self.listener_state.unsubscribed_peers(session_id).map_err(|_| {
AppStateError::SessionError(format!(
"Unable to get unsubscribed peers for `SessionId` {:?}",
session_id,
))
})
}
}
#[derive(Clone)]
pub struct AppState {
pub pair: sr25519::Pair,
pub x25519_secret: StaticSecret,
pub configuration: Configuration,
pub kv_store: KvManager,
pub cache: Cache,
}
impl AppState {
pub fn new(
configuration: Configuration,
kv_store: KvManager,
pair: sr25519::Pair,
x25519_secret: StaticSecret,
) -> Self {
Self { pair, x25519_secret, configuration, kv_store, cache: Cache::default() }
}
pub async fn get_api_rpc(
&self,
) -> Result<(OnlineClient<EntropyConfig>, LegacyRpcMethods<EntropyConfig>), AppStateError> {
Ok((
get_api(&self.configuration.endpoint).await?,
get_rpc(&self.configuration.endpoint).await?,
))
}
pub fn signer(&self) -> PairSigner<EntropyConfig, sr25519::Pair> {
PairSigner::<EntropyConfig, sr25519::Pair>::new(self.pair.clone())
}
pub fn account_id(&self) -> AccountId32 {
AccountId32::new(self.pair.public().0)
}
pub fn subxt_account_id(&self) -> SubxtAccountId32 {
SubxtAccountId32(self.pair.public().0)
}
pub fn x25519_public_key(&self) -> [u8; 32] {
x25519_dalek::PublicKey::from(&self.x25519_secret).to_bytes()
}
}
#[derive(Error, Debug)]
pub enum AppStateError {
#[error("Posion Mutex error: {0}")]
PosionError(String),
#[error("Session Error: {0}")]
SessionError(String),
#[error("Subxt: {0}")]
Subxt(#[from] subxt::Error),
}