iota_light_client/
package_store.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{collections::HashMap, sync::Arc};
6
7use anyhow::Result;
8use async_trait::async_trait;
9use iota_package_resolver::{Package, PackageStore, error::Error as PackageResolverError};
10use move_core_types::account_address::AccountAddress;
11use tokio::sync::Mutex;
12use tracing::{error, info};
13
14use crate::{config::Config, verifier::get_verified_object};
15
16pub struct RemotePackageStore {
17    config: Config,
18    cache: Mutex<HashMap<AccountAddress, Arc<Package>>>,
19}
20
21impl RemotePackageStore {
22    pub fn new(config: Config) -> Self {
23        Self {
24            config,
25            cache: Mutex::new(HashMap::new()),
26        }
27    }
28}
29
30#[async_trait]
31impl PackageStore for RemotePackageStore {
32    /// Read package contents. Fails if `id` is not an object, not a package, or
33    /// is malformed in some way.
34    async fn fetch(&self, id: AccountAddress) -> iota_package_resolver::Result<Arc<Package>> {
35        // Check if we have it in the cache
36        let res: Result<Arc<Package>> = async move {
37            if let Some(package) = self.cache.lock().await.get(&id) {
38                info!("Fetch Package: {id} cache hit");
39                return Ok(package.clone());
40            }
41
42            info!("Fetch Package: {id}");
43
44            let object = get_verified_object(&self.config, id.into()).await?;
45            let package = Arc::new(Package::read_from_object(&object)?);
46
47            // Add to the cache
48            self.cache.lock().await.insert(id, package.clone());
49
50            Ok(package)
51        }
52        .await;
53        res.map_err(|e| {
54            error!("Fetch Package: {id} error: {e:?}");
55            PackageResolverError::PackageNotFound(id)
56        })
57    }
58}