use std::collections::BTreeMap;
use std::fmt;
use std::sync::Arc;
#[cfg(feature = "live-runtime")]
use std::sync::atomic::{AtomicU64, Ordering};
use alloy_primitives::{Address, B256};
use super::{PoolKey, PoolStatus, ProtocolId};
#[cfg(feature = "live-runtime")]
static NEXT_AMM_RUNTIME_ID: AtomicU64 = AtomicU64::new(1);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct AmmRuntimeId(u64);
impl AmmRuntimeId {
#[cfg(feature = "live-runtime")]
pub(crate) fn allocate() -> Self {
let value = NEXT_AMM_RUNTIME_ID
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |value| {
value.checked_add(1)
})
.expect("AMM runtime identity exhausted");
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RuntimeSequenceOverflow {
sequence: &'static str,
}
impl RuntimeSequenceOverflow {
pub const fn new(sequence: &'static str) -> Self {
Self { sequence }
}
pub const fn sequence(self) -> &'static str {
self.sequence
}
}
impl fmt::Display for RuntimeSequenceOverflow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} sequence exhausted", self.sequence)
}
}
impl std::error::Error for RuntimeSequenceOverflow {}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PoolGeneration(u64);
impl PoolGeneration {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
pub fn checked_next(self) -> Result<Self, RuntimeSequenceOverflow> {
self.0
.checked_add(1)
.map(Self)
.ok_or_else(|| RuntimeSequenceOverflow::new("PoolGeneration"))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PoolInstanceId {
key: PoolKey,
generation: PoolGeneration,
}
impl PoolInstanceId {
pub const fn new(key: PoolKey, generation: PoolGeneration) -> Self {
Self { key, generation }
}
pub const fn key(&self) -> &PoolKey {
&self.key
}
pub const fn generation(&self) -> PoolGeneration {
self.generation
}
pub fn into_parts(self) -> (PoolKey, PoolGeneration) {
(self.key, self.generation)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct AdapterKey {
protocols: Vec<ProtocolId>,
}
impl AdapterKey {
pub fn new(primary: ProtocolId, additional: impl IntoIterator<Item = ProtocolId>) -> Self {
let mut protocols = vec![primary];
protocols.extend(additional);
protocols.sort_unstable();
protocols.dedup();
Self { protocols }
}
pub fn protocols(&self) -> &[ProtocolId] {
&self.protocols
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct AdapterGeneration(u64);
impl AdapterGeneration {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
pub fn checked_next(self) -> Result<Self, RuntimeSequenceOverflow> {
self.0
.checked_add(1)
.map(Self)
.ok_or_else(|| RuntimeSequenceOverflow::new("AdapterGeneration"))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct AdapterInstanceId {
key: AdapterKey,
generation: AdapterGeneration,
}
impl AdapterInstanceId {
pub const fn new(key: AdapterKey, generation: AdapterGeneration) -> Self {
Self { key, generation }
}
pub const fn key(&self) -> &AdapterKey {
&self.key
}
pub const fn generation(&self) -> AdapterGeneration {
self.generation
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DiscoveryOwnerKey(String);
impl DiscoveryOwnerKey {
pub fn new(key: impl Into<String>) -> Self {
Self(key.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DiscoveryGeneration(u64);
impl DiscoveryGeneration {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
pub fn checked_next(self) -> Result<Self, RuntimeSequenceOverflow> {
self.0
.checked_add(1)
.map(Self)
.ok_or_else(|| RuntimeSequenceOverflow::new("DiscoveryGeneration"))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DiscoveryOwnerId {
key: DiscoveryOwnerKey,
generation: DiscoveryGeneration,
}
impl DiscoveryOwnerId {
pub const fn new(key: DiscoveryOwnerKey, generation: DiscoveryGeneration) -> Self {
Self { key, generation }
}
pub const fn key(&self) -> &DiscoveryOwnerKey {
&self.key
}
pub const fn generation(&self) -> DiscoveryGeneration {
self.generation
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum RuntimeOwnerId {
Pool(PoolInstanceId),
Adapter(AdapterInstanceId),
Discovery(DiscoveryOwnerId),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct WorkId(u64);
impl WorkId {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
pub fn checked_next(self) -> Result<Self, RuntimeSequenceOverflow> {
self.0
.checked_add(1)
.map(Self)
.ok_or_else(|| RuntimeSequenceOverflow::new("WorkId"))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum StatePosition {
PostBlock,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct AmmStatePoint {
chain_id: u64,
block_number: u64,
block_hash: B256,
position: StatePosition,
}
impl AmmStatePoint {
pub const fn post_block(chain_id: u64, block_number: u64, block_hash: B256) -> Self {
Self {
chain_id,
block_number,
block_hash,
position: StatePosition::PostBlock,
}
}
pub const fn chain_id(self) -> u64 {
self.chain_id
}
pub const fn block_number(self) -> u64 {
self.block_number
}
pub const fn block_hash(self) -> B256 {
self.block_hash
}
pub const fn position(self) -> StatePosition {
self.position
}
pub fn first_unapplied_block(self) -> Result<u64, RuntimeSequenceOverflow> {
match self.position {
StatePosition::PostBlock => self
.block_number
.checked_add(1)
.ok_or_else(|| RuntimeSequenceOverflow::new("AmmStatePoint.block_number")),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct AmmStateVersion(u64);
impl AmmStateVersion {
pub const fn initial() -> Self {
Self(0)
}
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
pub fn checked_next(self) -> Result<Self, RuntimeSequenceOverflow> {
self.0
.checked_add(1)
.map(Self)
.ok_or_else(|| RuntimeSequenceOverflow::new("AmmStateVersion"))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PoolStateRevision(u64);
impl PoolStateRevision {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
pub fn checked_next(self) -> Result<Self, RuntimeSequenceOverflow> {
self.0
.checked_add(1)
.map(Self)
.ok_or_else(|| RuntimeSequenceOverflow::new("PoolStateRevision"))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PoolStateRef {
pool: PoolInstanceId,
revision: PoolStateRevision,
point: AmmStatePoint,
}
impl PoolStateRef {
pub const fn new(
pool: PoolInstanceId,
revision: PoolStateRevision,
point: AmmStatePoint,
) -> Self {
Self {
pool,
revision,
point,
}
}
pub const fn pool(&self) -> &PoolInstanceId {
&self.pool
}
pub const fn revision(&self) -> PoolStateRevision {
self.revision
}
pub const fn point(&self) -> AmmStatePoint {
self.point
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum PoolRuntimeState {
Discovered,
Queued,
Hydrating,
CatchingUp,
Searchable,
Live,
Degraded,
Removing,
Removed,
Failed,
}
impl PoolRuntimeState {
pub const fn required_pool_status(self) -> Option<PoolStatus> {
match self {
Self::Discovered | Self::Queued => Some(PoolStatus::Pending),
Self::Hydrating | Self::CatchingUp => Some(PoolStatus::Cold),
Self::Searchable | Self::Live => Some(PoolStatus::Ready),
Self::Degraded => Some(PoolStatus::Degraded),
Self::Removing | Self::Removed | Self::Failed => None,
}
}
pub const fn can_transition_to(self, next: Self) -> bool {
if matches!(next, Self::Removing) {
return !matches!(self, Self::Removing | Self::Removed);
}
matches!(
(self, next),
(Self::Discovered, Self::Queued)
| (Self::Queued, Self::Hydrating | Self::Failed)
| (Self::Hydrating, Self::CatchingUp | Self::Failed)
| (Self::CatchingUp, Self::Searchable | Self::Failed)
| (
Self::Searchable,
Self::Live | Self::CatchingUp | Self::Degraded
)
| (Self::Live, Self::CatchingUp | Self::Degraded)
| (Self::Degraded, Self::CatchingUp | Self::Live)
| (Self::Failed, Self::Queued)
| (Self::Removing, Self::Removed)
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct InvalidPoolRuntimeTransition {
from: PoolRuntimeState,
to: PoolRuntimeState,
}
impl InvalidPoolRuntimeTransition {
pub const fn new(from: PoolRuntimeState, to: PoolRuntimeState) -> Self {
Self { from, to }
}
pub const fn from(self) -> PoolRuntimeState {
self.from
}
pub const fn to(self) -> PoolRuntimeState {
self.to
}
}
impl fmt::Display for InvalidPoolRuntimeTransition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"invalid pool runtime transition: {:?} -> {:?}",
self.from, self.to
)
}
}
impl std::error::Error for InvalidPoolRuntimeTransition {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PoolLifecycle {
pool: PoolInstanceId,
state: PoolRuntimeState,
}
impl PoolLifecycle {
pub const fn new(pool: PoolInstanceId, state: PoolRuntimeState) -> Self {
Self { pool, state }
}
pub const fn pool(&self) -> &PoolInstanceId {
&self.pool
}
pub const fn state(&self) -> PoolRuntimeState {
self.state
}
pub fn transition_to(
&mut self,
next: PoolRuntimeState,
) -> Result<(), InvalidPoolRuntimeTransition> {
if !self.state.can_transition_to(next) {
return Err(InvalidPoolRuntimeTransition::new(self.state, next));
}
self.state = next;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RegistrationSourceKey(String);
impl RegistrationSourceKey {
pub fn new(key: impl Into<String>) -> Self {
Self(key.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum QueryEvidencePolicy {
Finalized,
RevalidateOnReorg,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum RegistrationProvenance {
Stable {
source: RegistrationSourceKey,
},
StateQuery {
owner: DiscoveryOwnerId,
chain_id: u64,
block_number: u64,
block_hash: B256,
policy: QueryEvidencePolicy,
},
FactoryLog {
owner: DiscoveryOwnerId,
factory: Address,
chain_id: u64,
block_number: u64,
block_hash: B256,
transaction_hash: B256,
log_index: u64,
},
}
impl RegistrationProvenance {
pub const fn stable(source: RegistrationSourceKey) -> Self {
Self::Stable { source }
}
pub const fn state_query(
owner: DiscoveryOwnerId,
chain_id: u64,
block_number: u64,
block_hash: B256,
policy: QueryEvidencePolicy,
) -> Self {
Self::StateQuery {
owner,
chain_id,
block_number,
block_hash,
policy,
}
}
#[allow(clippy::too_many_arguments)]
pub const fn factory_log(
owner: DiscoveryOwnerId,
factory: Address,
chain_id: u64,
block_number: u64,
block_hash: B256,
transaction_hash: B256,
log_index: u64,
) -> Self {
Self::FactoryLog {
owner,
factory,
chain_id,
block_number,
block_hash,
transaction_hash,
log_index,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RegistrationReorgAction {
Keep,
Revalidate,
Remove,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RegistrationEvidenceSet {
evidence: Vec<RegistrationProvenance>,
}
impl RegistrationEvidenceSet {
pub fn new(
primary: RegistrationProvenance,
additional: impl IntoIterator<Item = RegistrationProvenance>,
) -> Self {
let mut evidence = vec![primary];
evidence.extend(additional);
evidence.sort_unstable();
evidence.dedup();
Self { evidence }
}
pub fn evidence(&self) -> &[RegistrationProvenance] {
&self.evidence
}
pub fn reorg_action(&self, dropped_hashes: &[B256]) -> RegistrationReorgAction {
let mut requires_revalidation = false;
for evidence in &self.evidence {
match evidence {
RegistrationProvenance::Stable { .. }
| RegistrationProvenance::StateQuery {
policy: QueryEvidencePolicy::Finalized,
..
} => return RegistrationReorgAction::Keep,
RegistrationProvenance::StateQuery {
block_hash,
policy: QueryEvidencePolicy::RevalidateOnReorg,
..
} => {
if dropped_hashes.contains(block_hash) {
requires_revalidation = true;
} else {
return RegistrationReorgAction::Keep;
}
}
RegistrationProvenance::FactoryLog { block_hash, .. } => {
if !dropped_hashes.contains(block_hash) {
return RegistrationReorgAction::Keep;
}
}
}
}
if requires_revalidation {
RegistrationReorgAction::Revalidate
} else {
RegistrationReorgAction::Remove
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum AmmStateQuality {
Coherent,
Degraded,
Untrusted,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum AmmPoolChangeKind {
Added,
Updated,
Degraded,
Recovered,
Removed,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct AmmChangeImpact {
state: bool,
quoteability: bool,
topology: bool,
}
impl AmmChangeImpact {
pub const fn new(state: bool, quoteability: bool, topology: bool) -> Self {
Self {
state,
quoteability,
topology,
}
}
pub const fn state_only() -> Self {
Self::new(true, false, false)
}
pub const fn quoteability() -> Self {
Self::new(false, true, false)
}
pub const fn topology() -> Self {
Self::new(false, false, true)
}
pub const fn all() -> Self {
Self::new(true, true, true)
}
pub const fn state_changed(self) -> bool {
self.state
}
pub const fn quoteability_changed(self) -> bool {
self.quoteability
}
pub const fn topology_changed(self) -> bool {
self.topology
}
pub const fn union(self, other: Self) -> Self {
Self::new(
self.state || other.state,
self.quoteability || other.quoteability,
self.topology || other.topology,
)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AmmPoolChange {
pool: PoolInstanceId,
revision: PoolStateRevision,
kind: AmmPoolChangeKind,
impact: AmmChangeImpact,
}
impl AmmPoolChange {
pub const fn new(
pool: PoolInstanceId,
revision: PoolStateRevision,
kind: AmmPoolChangeKind,
impact: AmmChangeImpact,
) -> Self {
Self {
pool,
revision,
kind,
impact,
}
}
pub const fn pool(&self) -> &PoolInstanceId {
&self.pool
}
pub const fn revision(&self) -> PoolStateRevision {
self.revision
}
pub const fn kind(&self) -> AmmPoolChangeKind {
self.kind
}
pub const fn impact(&self) -> AmmChangeImpact {
self.impact
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum AmmStateIncident {
Reorg {
dropped: Vec<AmmStatePoint>,
},
Gap {
from: u64,
to: u64,
},
CoverageGap {
address: Address,
block: u64,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AmmChangeSetError {
DuplicatePool {
pool: PoolInstanceId,
},
}
impl AmmChangeSetError {
pub const fn duplicate_pool(pool: PoolInstanceId) -> Self {
Self::DuplicatePool { pool }
}
}
impl fmt::Display for AmmChangeSetError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DuplicatePool { pool } => {
write!(f, "duplicate AMM change for pool instance {pool:?}")
}
}
}
}
impl std::error::Error for AmmChangeSetError {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AmmChangeSet {
version: AmmStateVersion,
point: AmmStatePoint,
quality: AmmStateQuality,
pool_changes: Vec<AmmPoolChange>,
incidents: Vec<AmmStateIncident>,
requires_full_refresh: bool,
}
impl AmmChangeSet {
pub fn new(
version: AmmStateVersion,
point: AmmStatePoint,
quality: AmmStateQuality,
pool_changes: impl IntoIterator<Item = AmmPoolChange>,
incidents: impl IntoIterator<Item = AmmStateIncident>,
requires_full_refresh: bool,
) -> Result<Self, AmmChangeSetError> {
let mut pool_changes: Vec<_> = pool_changes.into_iter().collect();
pool_changes.sort_by(|left, right| left.pool.cmp(&right.pool));
for duplicate in pool_changes.windows(2) {
if duplicate[0].pool == duplicate[1].pool {
return Err(AmmChangeSetError::duplicate_pool(duplicate[0].pool.clone()));
}
}
let mut incidents: Vec<_> = incidents
.into_iter()
.map(|incident| match incident {
AmmStateIncident::Reorg { mut dropped } => {
dropped.sort_unstable();
dropped.dedup();
AmmStateIncident::Reorg { dropped }
}
incident => incident,
})
.collect();
incidents.sort_unstable();
incidents.dedup();
Ok(Self {
version,
point,
quality,
pool_changes,
incidents,
requires_full_refresh,
})
}
pub const fn version(&self) -> AmmStateVersion {
self.version
}
pub const fn point(&self) -> AmmStatePoint {
self.point
}
pub const fn quality(&self) -> AmmStateQuality {
self.quality
}
pub fn pool_changes(&self) -> &[AmmPoolChange] {
&self.pool_changes
}
pub fn incidents(&self) -> &[AmmStateIncident] {
&self.incidents
}
pub const fn requires_full_refresh(&self) -> bool {
self.requires_full_refresh
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RuntimeWorkId {
owner: RuntimeOwnerId,
work: WorkId,
}
impl RuntimeWorkId {
pub const fn new(owner: RuntimeOwnerId, work: WorkId) -> Self {
Self { owner, work }
}
pub const fn owner(&self) -> &RuntimeOwnerId {
&self.owner
}
pub const fn work(&self) -> WorkId {
self.work
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum OwnerRuntimeState {
Active,
Removing,
Removed,
Failed,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RuntimeLifecycleMap {
pools: BTreeMap<PoolInstanceId, PoolRuntimeState>,
adapters: BTreeMap<AdapterInstanceId, OwnerRuntimeState>,
discovery: BTreeMap<DiscoveryOwnerId, OwnerRuntimeState>,
}
impl RuntimeLifecycleMap {
pub fn set_pool(&mut self, pool: PoolInstanceId, state: PoolRuntimeState) {
self.pools.insert(pool, state);
}
pub fn set_adapter(&mut self, adapter: AdapterInstanceId, state: OwnerRuntimeState) {
self.adapters.insert(adapter, state);
}
pub fn set_discovery(&mut self, owner: DiscoveryOwnerId, state: OwnerRuntimeState) {
self.discovery.insert(owner, state);
}
pub fn pool(&self, pool: &PoolInstanceId) -> Option<PoolRuntimeState> {
self.pools.get(pool).copied()
}
pub fn adapter(&self, adapter: &AdapterInstanceId) -> Option<OwnerRuntimeState> {
self.adapters.get(adapter).copied()
}
pub fn discovery(&self, owner: &DiscoveryOwnerId) -> Option<OwnerRuntimeState> {
self.discovery.get(owner).copied()
}
pub fn pools(&self) -> impl Iterator<Item = (&PoolInstanceId, PoolRuntimeState)> {
self.pools.iter().map(|(owner, state)| (owner, *state))
}
pub fn adapters(&self) -> impl Iterator<Item = (&AdapterInstanceId, OwnerRuntimeState)> {
self.adapters.iter().map(|(owner, state)| (owner, *state))
}
pub fn discovery_owners(&self) -> impl Iterator<Item = (&DiscoveryOwnerId, OwnerRuntimeState)> {
self.discovery.iter().map(|(owner, state)| (owner, *state))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum AmmWorkClass {
Canonical,
CatchUp,
Repair,
Focused,
Bootstrap,
Deferred,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum AmmWorkKind {
Discovery,
ColdStart,
CatchUp,
Repair,
DeferredWarmup,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AmmWorkProgress {
kind: AmmWorkKind,
completed: u64,
total: Option<u64>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct InvalidWorkProgress {
completed: u64,
total: u64,
}
impl InvalidWorkProgress {
pub const fn new(completed: u64, total: u64) -> Self {
Self { completed, total }
}
pub const fn completed(self) -> u64 {
self.completed
}
pub const fn total(self) -> u64 {
self.total
}
}
impl fmt::Display for InvalidWorkProgress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"completed work {} exceeds known total {}",
self.completed, self.total
)
}
}
impl std::error::Error for InvalidWorkProgress {}
impl AmmWorkProgress {
pub const fn new(
kind: AmmWorkKind,
completed: u64,
total: Option<u64>,
) -> Result<Self, InvalidWorkProgress> {
if let Some(total) = total
&& completed > total
{
return Err(InvalidWorkProgress::new(completed, total));
}
Ok(Self {
kind,
completed,
total,
})
}
pub const fn kind(&self) -> AmmWorkKind {
self.kind
}
pub const fn completed(&self) -> u64 {
self.completed
}
pub const fn total(&self) -> Option<u64> {
self.total
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct QueueDepths {
depths: BTreeMap<AmmWorkClass, usize>,
}
impl QueueDepths {
pub fn set(&mut self, class: AmmWorkClass, depth: usize) {
self.depths.insert(class, depth);
}
pub fn get(&self, class: AmmWorkClass) -> usize {
self.depths.get(&class).copied().unwrap_or_default()
}
pub fn iter(&self) -> impl Iterator<Item = (AmmWorkClass, usize)> + '_ {
self.depths.iter().map(|(class, depth)| (*class, *depth))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum AmmRuntimeHealth {
Healthy,
Degraded,
Untrusted,
ShuttingDown,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AmmRuntimeStatusSnapshot {
sequence: u64,
state_version: AmmStateVersion,
lifecycles: Arc<RuntimeLifecycleMap>,
active_work: Arc<BTreeMap<RuntimeWorkId, AmmWorkProgress>>,
queues: Arc<QueueDepths>,
health: AmmRuntimeHealth,
}
impl AmmRuntimeStatusSnapshot {
pub fn new(
sequence: u64,
state_version: AmmStateVersion,
lifecycles: RuntimeLifecycleMap,
active_work: impl IntoIterator<Item = (RuntimeWorkId, AmmWorkProgress)>,
queues: QueueDepths,
health: AmmRuntimeHealth,
) -> Self {
Self {
sequence,
state_version,
lifecycles: Arc::new(lifecycles),
active_work: Arc::new(active_work.into_iter().collect()),
queues: Arc::new(queues),
health,
}
}
pub const fn sequence(&self) -> u64 {
self.sequence
}
pub const fn state_version(&self) -> AmmStateVersion {
self.state_version
}
pub fn pool_state(&self, pool: &PoolInstanceId) -> Option<PoolRuntimeState> {
self.lifecycles.pool(pool)
}
pub fn adapter_state(&self, adapter: &AdapterInstanceId) -> Option<OwnerRuntimeState> {
self.lifecycles.adapter(adapter)
}
pub fn discovery_state(&self, owner: &DiscoveryOwnerId) -> Option<OwnerRuntimeState> {
self.lifecycles.discovery(owner)
}
pub fn lifecycles(&self) -> &RuntimeLifecycleMap {
&self.lifecycles
}
pub fn active_work(&self, work: &RuntimeWorkId) -> Option<&AmmWorkProgress> {
self.active_work.get(work)
}
pub fn active_work_items(&self) -> impl Iterator<Item = (&RuntimeWorkId, &AmmWorkProgress)> {
self.active_work.iter()
}
pub fn queue_depth(&self, class: AmmWorkClass) -> usize {
self.queues.get(class)
}
pub fn queue_depths(&self) -> &QueueDepths {
&self.queues
}
pub const fn health(&self) -> AmmRuntimeHealth {
self.health
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AmmRuntimeEventKind {
PoolLifecycleTransition {
pool: PoolInstanceId,
from: PoolRuntimeState,
to: PoolRuntimeState,
},
AdapterLifecycleTransition {
adapter: AdapterInstanceId,
from: OwnerRuntimeState,
to: OwnerRuntimeState,
},
DiscoveryLifecycleTransition {
owner: DiscoveryOwnerId,
from: OwnerRuntimeState,
to: OwnerRuntimeState,
},
WorkQueued {
work: RuntimeWorkId,
class: AmmWorkClass,
kind: AmmWorkKind,
},
WorkProgress {
work: RuntimeWorkId,
progress: AmmWorkProgress,
},
WorkCompleted {
work: RuntimeWorkId,
},
WorkCancelled {
work: RuntimeWorkId,
},
WorkFailed {
work: RuntimeWorkId,
message: String,
},
ColdStartRoundStarted {
work: RuntimeWorkId,
round: u64,
total_rounds: Option<u64>,
},
ColdStartRoundCompleted {
work: RuntimeWorkId,
round: u64,
},
RegistrationAccepted {
pool: PoolInstanceId,
},
RegistrationRemoved {
pool: PoolInstanceId,
},
AdapterRegistrationAccepted {
adapter: AdapterInstanceId,
},
AdapterRegistrationRemoved {
adapter: AdapterInstanceId,
},
DiscoveryRegistrationAccepted {
owner: DiscoveryOwnerId,
},
DiscoveryRegistrationRemoved {
owner: DiscoveryOwnerId,
},
StateCommitted {
version: AmmStateVersion,
point: AmmStatePoint,
},
Reorg {
dropped: Vec<AmmStatePoint>,
},
Gap {
from: u64,
to: u64,
},
CoverageGap {
address: Address,
block: u64,
},
HealthChanged {
from: AmmRuntimeHealth,
to: AmmRuntimeHealth,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AmmRuntimeEvent {
sequence: u64,
kind: AmmRuntimeEventKind,
}
impl AmmRuntimeEvent {
pub const fn new(sequence: u64, kind: AmmRuntimeEventKind) -> Self {
Self { sequence, kind }
}
pub fn checked_next(&self, kind: AmmRuntimeEventKind) -> Result<Self, RuntimeSequenceOverflow> {
self.sequence
.checked_add(1)
.map(|sequence| Self { sequence, kind })
.ok_or_else(|| RuntimeSequenceOverflow::new("AmmRuntimeEvent.sequence"))
}
pub const fn sequence(&self) -> u64 {
self.sequence
}
pub const fn kind(&self) -> &AmmRuntimeEventKind {
&self.kind
}
}