newton-chainio 0.5.2

newton prover chainio
//! Cache-backed policy contract read helpers.
//!
//! These functions combine `PolicyContractCache` lookup with RPC fallback
//! using existing chainio free functions. Services call these to get
//! consensus-safe cached reads with automatic population on miss.
//!
//! All helpers use Moka's `try_get_with` to coalesce concurrent lookups for
//! the same key — only one caller issues the RPC, the rest await the result.

use alloy::primitives::{Address, B256};
use newton_core::cache::{PolicyContractCache, PolicySnapshot};

use crate::{
    error::ChainIoError,
    policy_client::{get_policy_address_for_client, get_policy_config_by_id},
    version::{get_policy_data_version, get_policy_version},
};

/// Resolve a policy snapshot from cache, falling back to RPC on miss.
///
/// Caller must provide `policy_id` (typically from `getPolicyId(client)` which
/// the operator already fetches to build TaskResponse).
pub async fn resolve_policy_snapshot(
    cache: &PolicyContractCache,
    policy_client: Address,
    policy_id: B256,
    chain_id: u64,
    rpc_url: &str,
) -> Result<PolicySnapshot, ChainIoError> {
    let rpc = rpc_url.to_string();
    cache
        .get_or_try_insert_snapshot(chain_id, policy_id, async {
            let policy_address = get_policy_address_for_client(policy_client, rpc.clone()).await?;
            let config = get_policy_config_by_id(policy_address, policy_id, &rpc).await?;
            Ok::<_, ChainIoError>(PolicySnapshot {
                policy_address,
                policy_id,
                policy_params: config.policyParams,
                expire_after: config.expireAfter,
            })
        })
        .await
        .map_err(|e| ChainIoError::SignerError(eyre::eyre!("{}", e)))
}

/// Resolve a policy version from cache, falling back to RPC on miss.
///
/// Returns `"0.0.0"` for pre-versioning implementations (SemVerMixin not present).
pub async fn resolve_policy_version(
    cache: &PolicyContractCache,
    policy_address: Address,
    chain_id: u64,
    rpc_url: &str,
) -> Result<String, ChainIoError> {
    let rpc = rpc_url.to_string();
    cache
        .get_or_try_insert_policy_version(chain_id, policy_address, async {
            get_policy_version(policy_address, &rpc)
                .await
                .map_err(ChainIoError::SignerError)
        })
        .await
        .map_err(|e| ChainIoError::SignerError(eyre::eyre!("{}", e)))
}

/// Resolve a policy data version from cache, falling back to RPC on miss.
///
/// Returns `"0.0.0"` for pre-versioning implementations.
pub async fn resolve_policy_data_version(
    cache: &PolicyContractCache,
    policy_data_address: Address,
    chain_id: u64,
    rpc_url: &str,
) -> Result<String, ChainIoError> {
    let rpc = rpc_url.to_string();
    cache
        .get_or_try_insert_policy_data_version(chain_id, policy_data_address, async {
            get_policy_data_version(policy_data_address, &rpc)
                .await
                .map_err(ChainIoError::SignerError)
        })
        .await
        .map_err(|e| ChainIoError::SignerError(eyre::eyre!("{}", e)))
}