use std::collections::BTreeMap;
use std::fmt;
use std::sync::Arc;
use evm_fork_cache::cache::EvmSnapshot;
use super::{
AdapterInstanceId, AdapterKey, AdapterRegistry, AmmChangeSet, AmmOwnershipIndex, AmmRuntimeId,
AmmStatePoint, AmmStateVersion, PoolInstanceId, PoolKey, PoolRegistration, PoolStateRevision,
};
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AdapterRegistrySnapshotError {
RegistryPoolMissingOwnership(PoolKey),
OwnershipPoolMissingRegistry(PoolInstanceId),
RegistryAdapterMissingOwnership(AdapterKey),
OwnershipAdapterMissingRegistry(AdapterInstanceId),
RegistryPoolMissingAdapter(PoolKey),
OwnershipPoolMissingAdapter(PoolInstanceId),
PoolAdapterMismatch {
pool: Box<PoolInstanceId>,
registry: Box<AdapterKey>,
ownership: Box<AdapterInstanceId>,
},
}
impl fmt::Display for AdapterRegistrySnapshotError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "adapter registry snapshot rejected: {self:?}")
}
}
impl std::error::Error for AdapterRegistrySnapshotError {}
pub struct AdapterRegistrySnapshot {
registry: Arc<AdapterRegistry>,
active_pools: BTreeMap<PoolKey, PoolInstanceId>,
active_adapters: BTreeMap<AdapterKey, AdapterInstanceId>,
}
impl AdapterRegistrySnapshot {
pub fn try_new(
registry: &AdapterRegistry,
ownership: &AmmOwnershipIndex,
) -> Result<Self, AdapterRegistrySnapshotError> {
let mut registry_pools: Vec<_> = registry.pools().collect();
registry_pools.sort_by(|left, right| left.key.cmp(&right.key));
for pool in ®istry_pools {
if ownership.active_pool(&pool.key).is_none() {
return Err(AdapterRegistrySnapshotError::RegistryPoolMissingOwnership(
pool.key.clone(),
));
}
}
for instance in ownership.pools() {
if registry.pool(instance.key()).is_none() {
return Err(AdapterRegistrySnapshotError::OwnershipPoolMissingRegistry(
instance.clone(),
));
}
}
for pool in registry_pools {
let Some(adapter) = registry.adapter(pool.protocol()) else {
return Err(AdapterRegistrySnapshotError::RegistryPoolMissingAdapter(
pool.key.clone(),
));
};
let registry_adapter = AdapterKey::new(adapter.protocol(), adapter.protocols());
let Some(instance) = ownership.active_pool(&pool.key) else {
return Err(AdapterRegistrySnapshotError::RegistryPoolMissingOwnership(
pool.key.clone(),
));
};
let Some(owned_adapter) = ownership.adapter_for_pool(instance) else {
return Err(AdapterRegistrySnapshotError::OwnershipPoolMissingAdapter(
instance.clone(),
));
};
if owned_adapter.key() != ®istry_adapter
|| ownership.active_adapter(®istry_adapter) != Some(owned_adapter)
{
return Err(AdapterRegistrySnapshotError::PoolAdapterMismatch {
pool: Box::new(instance.clone()),
registry: Box::new(registry_adapter),
ownership: Box::new(owned_adapter.clone()),
});
}
}
let registry_adapters: BTreeMap<AdapterKey, ()> = registry
.adapters()
.map(|adapter| (AdapterKey::new(adapter.protocol(), adapter.protocols()), ()))
.collect();
for key in registry_adapters.keys() {
if ownership.active_adapter(key).is_none() {
return Err(
AdapterRegistrySnapshotError::RegistryAdapterMissingOwnership(key.clone()),
);
}
}
for instance in ownership.adapters() {
if !registry_adapters.contains_key(instance.key()) {
return Err(
AdapterRegistrySnapshotError::OwnershipAdapterMissingRegistry(instance.clone()),
);
}
}
let active_pools = ownership
.pools()
.map(|instance| (instance.key().clone(), instance.clone()))
.collect();
let active_adapters = ownership
.adapters()
.map(|instance| (instance.key().clone(), instance.clone()))
.collect();
Ok(Self {
registry: Arc::new(registry.clone()),
active_pools,
active_adapters,
})
}
pub fn registry(&self) -> &AdapterRegistry {
&self.registry
}
pub fn pool_instance(&self, key: &PoolKey) -> Option<&PoolInstanceId> {
self.active_pools.get(key)
}
pub fn pool(&self, instance: &PoolInstanceId) -> Option<&PoolRegistration> {
(self.active_pools.get(instance.key()) == Some(instance))
.then(|| self.registry.pool(instance.key()))
.flatten()
}
pub fn adapter_instance(&self, key: &AdapterKey) -> Option<&AdapterInstanceId> {
self.active_adapters.get(key)
}
pub fn adapter(&self, instance: &AdapterInstanceId) -> Option<&Arc<dyn super::AmmAdapter>> {
(self.active_adapters.get(instance.key()) == Some(instance))
.then(|| instance.key().protocols().first().copied())
.flatten()
.and_then(|protocol| self.registry.adapter(protocol))
}
pub fn pool_count(&self) -> usize {
self.active_pools.len()
}
pub fn adapter_count(&self) -> usize {
self.active_adapters.len()
}
pub fn pools(&self) -> impl Iterator<Item = (&PoolKey, &PoolInstanceId)> {
self.active_pools.iter()
}
pub fn adapters(&self) -> impl Iterator<Item = (&AdapterKey, &AdapterInstanceId)> {
self.active_adapters.iter()
}
}
pub type PoolRevisionMap = BTreeMap<PoolInstanceId, PoolStateRevision>;
pub struct AmmStateSnapshot {
runtime_id: AmmRuntimeId,
version: AmmStateVersion,
point: AmmStatePoint,
interest_revision: u64,
cache: Arc<EvmSnapshot>,
registry: Arc<AdapterRegistrySnapshot>,
pool_revisions: Arc<PoolRevisionMap>,
}
impl AmmStateSnapshot {
#[cfg_attr(not(feature = "live-runtime"), allow(dead_code))]
pub(crate) fn new(
runtime_id: AmmRuntimeId,
version: AmmStateVersion,
point: AmmStatePoint,
interest_revision: u64,
cache: Arc<EvmSnapshot>,
registry: Arc<AdapterRegistrySnapshot>,
pool_revisions: Arc<PoolRevisionMap>,
) -> Self {
Self {
runtime_id,
version,
point,
interest_revision,
cache,
registry,
pool_revisions,
}
}
pub const fn runtime_id(&self) -> AmmRuntimeId {
self.runtime_id
}
pub const fn version(&self) -> AmmStateVersion {
self.version
}
pub const fn point(&self) -> AmmStatePoint {
self.point
}
pub const fn interest_revision(&self) -> u64 {
self.interest_revision
}
pub fn cache(&self) -> &EvmSnapshot {
&self.cache
}
pub fn cache_snapshot(&self) -> Arc<EvmSnapshot> {
Arc::clone(&self.cache)
}
pub fn registry(&self) -> &AdapterRegistrySnapshot {
&self.registry
}
pub fn registry_snapshot(&self) -> Arc<AdapterRegistrySnapshot> {
Arc::clone(&self.registry)
}
pub fn pool_revision(&self, pool: &PoolInstanceId) -> Option<PoolStateRevision> {
self.pool_revisions.get(pool).copied()
}
pub fn pool_revisions(&self) -> &PoolRevisionMap {
&self.pool_revisions
}
pub fn pool_revisions_snapshot(&self) -> Arc<PoolRevisionMap> {
Arc::clone(&self.pool_revisions)
}
}
pub struct AmmStateCommit {
snapshot: Arc<AmmStateSnapshot>,
changes: Arc<AmmChangeSet>,
}
impl AmmStateCommit {
#[cfg_attr(not(feature = "live-runtime"), allow(dead_code))]
pub(crate) fn new(snapshot: Arc<AmmStateSnapshot>, changes: Arc<AmmChangeSet>) -> Self {
debug_assert_eq!(snapshot.version(), changes.version());
debug_assert_eq!(snapshot.point(), changes.point());
Self { snapshot, changes }
}
pub fn snapshot(&self) -> &Arc<AmmStateSnapshot> {
&self.snapshot
}
pub fn changes(&self) -> &Arc<AmmChangeSet> {
&self.changes
}
}