From 4265bef6d5e14e0904f74a5c0af1c42e005ff744 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 10 Apr 2026 19:12:12 +0800 Subject: [PATCH 1/2] refactor: no Arc wrapper around ChainIndex and ZstdFrameCache --- src/blocks/tipset.rs | 10 +++ src/chain/mod.rs | 5 +- src/chain/store/chain_store.rs | 41 +++++----- src/chain/store/index.rs | 22 ++++-- src/chain_sync/bad_block_cache.rs | 12 ++- src/chain_sync/chain_follower.rs | 21 +++--- src/chain_sync/network_context.rs | 9 ++- src/chain_sync/tipset_syncer.rs | 13 ++-- src/cli/subcommands/chain_cmd/list.rs | 3 +- src/daemon/db_util.rs | 5 +- src/daemon/mod.rs | 12 +-- src/db/car/any.rs | 11 +-- src/db/car/forest.rs | 10 +-- src/db/car/many.rs | 7 +- src/db/car/mod.rs | 18 ++++- src/dev/subcommands/state_cmd.rs | 9 ++- src/fil_cns/validation.rs | 7 +- src/interpreter/fvm2.rs | 4 +- src/interpreter/fvm3.rs | 4 +- src/interpreter/fvm4.rs | 4 +- src/interpreter/vm.rs | 2 +- src/message_pool/msgpool/mod.rs | 5 +- src/rpc/methods/chain.rs | 33 +++++--- src/rpc/methods/eth.rs | 21 ++++-- src/rpc/methods/f3.rs | 10 +-- src/rpc/methods/gas.rs | 3 +- src/rpc/methods/state.rs | 9 ++- src/rpc/mod.rs | 2 +- src/state_manager/chain_rand.rs | 15 ++-- src/state_manager/mod.rs | 105 +++++++++++++------------- src/tool/subcommands/archive_cmd.rs | 5 +- src/tool/subcommands/benchmark_cmd.rs | 2 +- src/tool/subcommands/snapshot_cmd.rs | 10 +-- src/utils/cache/lru.rs | 20 ++++- src/utils/mod.rs | 2 + src/utils/shallow_clone.rs | 20 +++++ 36 files changed, 304 insertions(+), 187 deletions(-) create mode 100644 src/utils/shallow_clone.rs diff --git a/src/blocks/tipset.rs b/src/blocks/tipset.rs index 790fee31313b..3b3747103c24 100644 --- a/src/blocks/tipset.rs +++ b/src/blocks/tipset.rs @@ -13,6 +13,7 @@ use crate::{ networks::{calibnet, mainnet}, shim::clock::ChainEpoch, utils::{ + ShallowClone, cid::CidCborExt, db::{CborStoreExt, car_stream::CarBlock}, get_size::nunny_vec_heap_size_helper, @@ -195,6 +196,15 @@ pub struct Tipset { key: Arc>, } +impl ShallowClone for Tipset { + fn shallow_clone(&self) -> Self { + Self { + headers: self.headers.shallow_clone(), + key: self.key.shallow_clone(), + } + } +} + impl From for Tipset { fn from(value: RawBlockHeader) -> Self { Self::from(CachingBlockHeader::from(value)) diff --git a/src/chain/mod.rs b/src/chain/mod.rs index f53751fffd3f..1b70f09ae9a5 100644 --- a/src/chain/mod.rs +++ b/src/chain/mod.rs @@ -15,6 +15,7 @@ use crate::cid_collections::CidHashSet; use crate::db::car::forest::{self, ForestCarFrame, finalize_frame}; use crate::db::{SettingsStore, SettingsStoreExt}; use crate::ipld::stream_chain; +use crate::utils::ShallowClone as _; use crate::utils::db::car_stream::{CarBlock, CarBlockWrite}; use crate::utils::io::{AsyncWriterWithChecksum, Checksum}; use crate::utils::multihash::MultihashCode; @@ -167,8 +168,8 @@ async fn export_to_forest_car( // block size is between 1kb and 2kb. 1024, stream_chain( - Arc::clone(db), - tipset.clone().chain_owned(Arc::clone(db)), + db.shallow_clone(), + tipset.shallow_clone().chain_owned(db.shallow_clone()), stateroot_lookup_limit, ) .with_seen(seen) diff --git a/src/chain/store/chain_store.rs b/src/chain/store/chain_store.rs index d78ce51bb220..19440c46f569 100644 --- a/src/chain/store/chain_store.rs +++ b/src/chain/store/chain_store.rs @@ -11,6 +11,7 @@ use crate::networks::{ChainConfig, Height}; use crate::rpc::eth::{eth_tx_from_signed_eth_message, types::EthHash}; use crate::shim::clock::ChainEpoch; use crate::shim::{executor::Receipt, message::Message, version::NetworkVersion}; +use crate::utils::ShallowClone; use crate::utils::db::{BlockstoreExt, CborStoreExt}; use crate::{ blocks::{CachingBlockHeader, Tipset, TipsetKey, TxMeta}, @@ -76,7 +77,7 @@ pub struct ChainStore { f3_finalized_tipset: Arc>>, /// Used as a cache for tipset `lookbacks`. - chain_index: Arc>>, + chain_index: ChainIndex, /// Tracks blocks for the purpose of forming tipsets. tipset_tracker: TipsetTracker, @@ -144,22 +145,20 @@ where }; let heaviest_tipset = Arc::new(RwLock::new(head)); let f3_finalized_tipset: Arc>> = Default::default(); - let chain_index = Arc::new( - ChainIndex::new(db.clone()).with_is_tipset_finalized(Box::new({ - let chain_finality = chain_config.policy.chain_finality; - let heaviest_tipset = heaviest_tipset.clone(); - let f3_finalized_tipset = f3_finalized_tipset.clone(); - move |ts| { - let finalized = f3_finalized_tipset - .read() - .as_ref() - .map(|ts| ts.epoch()) - .unwrap_or_default() - .max(heaviest_tipset.read().epoch() - chain_finality); - ts.epoch() <= finalized - } - })), - ); + let chain_index = ChainIndex::new(db.clone()).with_is_tipset_finalized(Arc::new({ + let chain_finality = chain_config.policy.chain_finality; + let heaviest_tipset = heaviest_tipset.clone(); + let f3_finalized_tipset = f3_finalized_tipset.clone(); + move |ts| { + let finalized = f3_finalized_tipset + .read() + .as_ref() + .map(|ts| ts.epoch()) + .unwrap_or_default() + .max(heaviest_tipset.read().epoch() - chain_finality); + ts.epoch() <= finalized + } + })); let cs = Self { head_changes_tx: publisher, chain_index, @@ -290,7 +289,7 @@ where } /// Returns the chain index - pub fn chain_index(&self) -> &Arc>> { + pub fn chain_index(&self) -> &ChainIndex { &self.chain_index } @@ -393,7 +392,7 @@ where /// is usually 900. The `heaviest_tipset` is a reference point in the /// blockchain. It must be a child of the look-back tipset. pub fn get_lookback_tipset_for_round( - chain_index: &Arc>>, + chain_index: &ChainIndex, chain_config: &Arc, heaviest_tipset: &Tipset, round: ChainEpoch, @@ -417,8 +416,8 @@ where let beacon = Arc::new(chain_config.get_beacon_schedule(genesis_timestamp)); let ExecutedTipset { state_root, .. } = crate::state_manager::apply_block_messages( genesis_timestamp, - Arc::clone(chain_index), - Arc::clone(chain_config), + chain_index.shallow_clone(), + chain_config.shallow_clone(), beacon, // Using shared WASM engine here as creating new WASM engines is expensive // (takes seconds to minutes). It's only acceptable here because this situation is diff --git a/src/chain/store/index.rs b/src/chain/store/index.rs index aa4cb1f95d5c..3fe8f675fa19 100644 --- a/src/chain/store/index.rs +++ b/src/chain/store/index.rs @@ -1,13 +1,14 @@ // Copyright 2019-2026 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use std::num::NonZeroUsize; +use std::{num::NonZeroUsize, sync::Arc}; use crate::beacon::{BeaconEntry, IGNORE_DRAND}; use crate::blocks::{Tipset, TipsetKey}; use crate::chain::Error; use crate::metrics; use crate::shim::clock::ChainEpoch; +use crate::utils::ShallowClone; use crate::utils::cache::SizeTrackingLruCache; use fvm_ipld_blockstore::Blockstore; use itertools::Itertools; @@ -20,7 +21,7 @@ type TipsetCache = SizeTrackingLruCache; type TipsetHeightCache = SizeTrackingLruCache; -type IsTipsetFinalizedFn = Box bool + Send + Sync>; +type IsTipsetFinalizedFn = Arc bool + Send + Sync>; /// Keeps look-back tipsets in cache at a given interval `skip_length` and can /// be used to look-back at the chain to retrieve an old tipset. @@ -30,11 +31,22 @@ pub struct ChainIndex { /// epoch to tipset key mappings. ts_height_cache: TipsetHeightCache, /// `Blockstore` pointer needed to load tipsets from cold storage. - db: DB, + db: Arc, /// check whether a tipset is finalized is_tipset_finalized: Option, } +impl ShallowClone for ChainIndex { + fn shallow_clone(&self) -> Self { + Self { + ts_cache: self.ts_cache.shallow_clone(), + ts_height_cache: self.ts_height_cache.shallow_clone(), + db: self.db.shallow_clone(), + is_tipset_finalized: self.is_tipset_finalized.clone(), + } + } +} + #[derive(Debug, Clone, Copy)] /// Methods for resolving fetches of null tipsets. /// Imagine epoch 10 is null but epoch 9 and 11 exist. If epoch we request epoch @@ -45,7 +57,7 @@ pub enum ResolveNullTipset { } impl ChainIndex { - pub fn new(db: DB) -> Self { + pub fn new(db: Arc) -> Self { let ts_cache = SizeTrackingLruCache::new_with_metrics("tipset".into(), DEFAULT_TIPSET_CACHE_SIZE); let ts_height_cache: SizeTrackingLruCache = @@ -68,7 +80,7 @@ impl ChainIndex { self } - pub fn db(&self) -> &DB { + pub fn db(&self) -> &Arc { &self.db } diff --git a/src/chain_sync/bad_block_cache.rs b/src/chain_sync/bad_block_cache.rs index 119a1b4c9972..cc487a38df12 100644 --- a/src/chain_sync/bad_block_cache.rs +++ b/src/chain_sync/bad_block_cache.rs @@ -6,7 +6,7 @@ use std::num::NonZeroUsize; use cid::Cid; use nonzero_ext::nonzero; -use crate::utils::{cache::SizeTrackingLruCache, get_size}; +use crate::utils::{ShallowClone, cache::SizeTrackingLruCache, get_size}; /// Default capacity for CID caches (32768 entries). /// That's about 4 MiB. @@ -47,11 +47,19 @@ impl BadBlockCache { /// Thread-safe LRU cache for tracking recently seen gossip block CIDs. /// Used to de-duplicate gossip blocks before expensive message fetching. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SeenBlockCache { cache: SizeTrackingLruCache, } +impl ShallowClone for SeenBlockCache { + fn shallow_clone(&self) -> Self { + Self { + cache: self.cache.shallow_clone(), + } + } +} + impl Default for SeenBlockCache { fn default() -> Self { Self::new(DEFAULT_CID_CACHE_CAPACITY) diff --git a/src/chain_sync/chain_follower.rs b/src/chain_sync/chain_follower.rs index 43c46ea67a55..b1e5360168d3 100644 --- a/src/chain_sync/chain_follower.rs +++ b/src/chain_sync/chain_follower.rs @@ -32,6 +32,7 @@ use crate::{ networks::calculate_expected_epoch, shim::clock::ChainEpoch, state_manager::StateManager, + utils::ShallowClone as _, }; use ahash::{HashMap, HashSet}; use chrono::Utc; @@ -153,12 +154,12 @@ pub async fn chain_follower( // Increment metrics, update peer information, and forward tipsets to the state machine. set.spawn({ - let state_manager = state_manager.clone(); - let state_changed = state_changed.clone(); - let state_machine = state_machine.clone(); - let network = network.clone(); - let bad_block_cache = bad_block_cache.clone(); - let seen_block_cache = seen_block_cache.clone(); + let state_manager = state_manager.shallow_clone(); + let state_changed = state_changed.shallow_clone(); + let state_machine = state_machine.shallow_clone(); + let network = network.shallow_clone(); + let bad_block_cache = bad_block_cache.shallow_clone(); + let seen_block_cache = seen_block_cache.shallow_clone(); async move { while let Ok(event) = network_rx.recv_async().await { inc_gossipsub_event_metrics(&event); @@ -272,10 +273,10 @@ pub async fn chain_follower( let new = tasks_set.insert(task.clone()); if new { let action = task.clone().execute( - network.clone(), - state_manager.clone(), + network.shallow_clone(), + state_manager.shallow_clone(), stateless_mode, - bad_block_cache.clone(), + bad_block_cache.shallow_clone(), ); tokio::spawn({ let tasks = tasks.clone(); @@ -389,7 +390,7 @@ fn update_peer_info( let genesis_cid = *genesis.block_headers().first().cid(); // Spawn and immediately move on to the next event tokio::task::spawn(handle_peer_connected_event( - network.clone(), + network.shallow_clone(), chain_store, *peer_id, genesis_cid, diff --git a/src/chain_sync/network_context.rs b/src/chain_sync/network_context.rs index 682b2861a8c0..f351e55079db 100644 --- a/src/chain_sync/network_context.rs +++ b/src/chain_sync/network_context.rs @@ -22,6 +22,7 @@ use crate::{ rpc::RequestResponseError, }, utils::{ + ShallowClone, misc::{AdaptiveValueProvider, ExponentialAdaptiveValueProvider}, stats::Stats, }, @@ -66,12 +67,12 @@ pub struct SyncNetworkContext { db: Arc, } -impl Clone for SyncNetworkContext { - fn clone(&self) -> Self { +impl ShallowClone for SyncNetworkContext { + fn shallow_clone(&self) -> Self { Self { network_send: self.network_send.clone(), - peer_manager: self.peer_manager.clone(), - db: self.db.clone(), + peer_manager: self.peer_manager.shallow_clone(), + db: self.db.shallow_clone(), } } } diff --git a/src/chain_sync/tipset_syncer.rs b/src/chain_sync/tipset_syncer.rs index 86f1e4604389..8cfd6c233a18 100644 --- a/src/chain_sync/tipset_syncer.rs +++ b/src/chain_sync/tipset_syncer.rs @@ -13,6 +13,7 @@ use crate::shim::{ }; use crate::state_manager::ExecutedTipset; use crate::state_manager::{Error as StateManagerError, StateManager, utils::is_valid_for_sending}; +use crate::utils::ShallowClone as _; use crate::{ blocks::{Block, CachingBlockHeader, Error as ForestBlockError, FullTipset, Tipset}, fil_cns::{self, FilecoinConsensus, FilecoinConsensusError}, @@ -224,17 +225,17 @@ async fn validate_block( // Check block messages validations.spawn(check_block_messages( - state_manager.clone(), - block.clone(), - base_tipset.clone(), + state_manager.shallow_clone(), + block.shallow_clone(), + base_tipset.shallow_clone(), )); // Base fee check validations.spawn_blocking({ let smoke_height = state_manager.chain_config().epoch(Height::Smoke); - let base_tipset = base_tipset.clone(); + let base_tipset = base_tipset.shallow_clone(); let block_store = state_manager.blockstore_owned(); - let block = Arc::clone(&block); + let block = block.shallow_clone(); move || { let base_fee = crate::chain::compute_base_fee(&block_store, &base_tipset, smoke_height) .map_err(|e| { @@ -253,7 +254,7 @@ async fn validate_block( // Parent weight calculation check validations.spawn_blocking({ let block_store = state_manager.blockstore_owned(); - let base_tipset = base_tipset.clone(); + let base_tipset = base_tipset.shallow_clone(); let weight = header.weight.clone(); move || { let calc_weight = fil_cns::weight(&block_store, &base_tipset).map_err(|e| { diff --git a/src/cli/subcommands/chain_cmd/list.rs b/src/cli/subcommands/chain_cmd/list.rs index 6700c3106cc6..b865785cebe0 100644 --- a/src/cli/subcommands/chain_cmd/list.rs +++ b/src/cli/subcommands/chain_cmd/list.rs @@ -17,6 +17,7 @@ use crate::{ }, }, shim::econ::{BLOCK_GAS_LIMIT, TokenAmount}, + utils::ShallowClone as _, }; /// View a segment of the chain @@ -43,7 +44,7 @@ impl ChainListCommand { }; let mut tipsets = Vec::with_capacity(count); loop { - tipsets.push(ts.clone()); + tipsets.push(ts.shallow_clone()); if ts.epoch() == 0 || tipsets.len() >= count { break; } diff --git a/src/daemon/db_util.rs b/src/daemon/db_util.rs index 8f9b2dfdc3e8..1d202ea93395 100644 --- a/src/daemon/db_util.rs +++ b/src/daemon/db_util.rs @@ -10,6 +10,7 @@ use crate::networks::ChainConfig; use crate::rpc::sync::SnapshotProgressTracker; use crate::shim::clock::ChainEpoch; use crate::state_manager::StateManager; +use crate::utils::ShallowClone as _; use crate::utils::db::car_stream::CarStream; use crate::utils::io::EitherMmapOrRandomAccessFile; use crate::utils::net::{DownloadFileOption, download_to}; @@ -390,7 +391,7 @@ where match spec { RangeSpec::To(to_epoch) => { for ts in head_ts - .clone() + .shallow_clone() .chain(&state_manager.chain_store().blockstore()) .take_while(|ts| ts.epoch() >= to_epoch) { @@ -400,7 +401,7 @@ where } RangeSpec::NumTipsets(n_tipsets) => { for ts in head_ts - .clone() + .shallow_clone() .chain(&state_manager.chain_store().blockstore()) .take(n_tipsets) { diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index 99052e92b697..5dec2067c999 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -31,8 +31,8 @@ use crate::rpc::start_rpc; use crate::shim::clock::ChainEpoch; use crate::shim::state_tree::StateTree; use crate::shim::version::NetworkVersion; -use crate::utils; use crate::utils::misc::env::is_env_truthy; +use crate::utils::{self, ShallowClone as _}; use crate::utils::{proofs_api::ensure_proof_params_downloaded, version::FOREST_VERSION_STRING}; use anyhow::{Context as _, bail}; use backon::{ExponentialBuilder, Retryable}; @@ -392,12 +392,12 @@ fn maybe_start_rpc_service( ); } services.spawn({ - let state_manager = ctx.state_manager.clone(); - let bad_blocks = chain_follower.bad_blocks.clone(); - let sync_status = chain_follower.sync_status.clone(); - let sync_network_context = chain_follower.network.clone(); + let state_manager = ctx.state_manager.shallow_clone(); + let bad_blocks = chain_follower.bad_blocks.shallow_clone(); + let sync_status = chain_follower.sync_status.shallow_clone(); + let sync_network_context = chain_follower.network.shallow_clone(); let tipset_send = chain_follower.tipset_sender.clone(); - let keystore = ctx.keystore.clone(); + let keystore = ctx.keystore.shallow_clone(); let snapshot_progress_tracker = ctx.snapshot_progress_tracker.clone(); let nonce_tracker = NonceTracker::new(); let mpool_locker = MpoolLocker::new(); diff --git a/src/db/car/any.rs b/src/db/car/any.rs index 2c3627a04324..76e1e6d9d5cb 100644 --- a/src/db/car/any.rs +++ b/src/db/car/any.rs @@ -16,10 +16,11 @@ use cid::Cid; use fvm_ipld_blockstore::Blockstore; use itertools::Either; use positioned_io::ReadAt; -use std::borrow::Cow; -use std::io::{Error, ErrorKind, Read, Result}; -use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::{ + borrow::Cow, + io::{Error, ErrorKind, Read, Result}, + path::{Path, PathBuf}, +}; #[derive(derive_more::From)] pub enum AnyCar { @@ -102,7 +103,7 @@ impl AnyCar { } /// Set the z-frame cache of the inner CAR reader. - pub fn with_cache(self, cache: Arc, key: CacheKey) -> Self { + pub fn with_cache(self, cache: ZstdFrameCache, key: CacheKey) -> Self { match self { AnyCar::Forest(f) => AnyCar::Forest(f.with_cache(cache, key)), AnyCar::Plain(p) => AnyCar::Plain(p), diff --git a/src/db/car/forest.rs b/src/db/car/forest.rs index 6bbab45d2649..bdd54b1d0bef 100644 --- a/src/db/car/forest.rs +++ b/src/db/car/forest.rs @@ -66,7 +66,7 @@ use nunny::Vec as NonEmpty; use positioned_io::{Cursor, ReadAt, Size as _, SizeCursor}; use std::io::{Seek, SeekFrom}; use std::path::Path; -use std::sync::{Arc, OnceLock}; +use std::sync::OnceLock; use std::task::Poll; use std::{ io, @@ -97,7 +97,7 @@ pub struct ForestCar { cache_key: CacheKey, indexed: index::Reader>>, index_size_bytes: u64, - frame_cache: Arc, + frame_cache: ZstdFrameCache, header: CarV1Header, metadata: OnceLock>, } @@ -119,7 +119,7 @@ impl ForestCar { cache_key: 0, indexed, index_size_bytes, - frame_cache: Arc::new(ZstdFrameCache::default()), + frame_cache: ZstdFrameCache::default(), header, metadata: OnceLock::new(), }) @@ -220,10 +220,10 @@ impl ForestCar { }) } - pub fn with_cache(self, cache: Arc, key: CacheKey) -> Self { + pub fn with_cache(self, frame_cache: ZstdFrameCache, key: CacheKey) -> Self { Self { cache_key: key, - frame_cache: cache, + frame_cache, ..self } } diff --git a/src/db/car/many.rs b/src/db/car/many.rs index 1db57933e6aa..300d3966cbce 100644 --- a/src/db/car/many.rs +++ b/src/db/car/many.rs @@ -17,6 +17,7 @@ use crate::db::{ use crate::libp2p_bitswap::BitswapStoreReadWrite; use crate::rpc::eth::types::EthHash; use crate::shim::clock::ChainEpoch; +use crate::utils::ShallowClone; use crate::utils::io::EitherMmapOrRandomAccessFile; use crate::utils::multihash::prelude::*; use crate::{blocks::Tipset, libp2p_bitswap::BitswapStoreRead}; @@ -65,7 +66,7 @@ impl PartialEq for WithHeaviestEpoch { } pub struct ManyCar { - shared_cache: Arc, + shared_cache: ZstdFrameCache, read_only: Arc>>, writer: WriterT, } @@ -73,7 +74,7 @@ pub struct ManyCar { impl ManyCar { pub fn new(writer: WriterT) -> Self { ManyCar { - shared_cache: Arc::new(ZstdFrameCache::default()), + shared_cache: ZstdFrameCache::default(), read_only: Arc::new(RwLock::new(BinaryHeap::default())), writer, } @@ -108,7 +109,7 @@ impl ManyCar { read_only.push(WithHeaviestEpoch::new( any_car - .with_cache(self.shared_cache.clone(), key) + .with_cache(self.shared_cache.shallow_clone(), key) .into_dyn()?, )?); diff --git a/src/db/car/mod.rs b/src/db/car/mod.rs index 4f41708c2df7..a30f00359296 100644 --- a/src/db/car/mod.rs +++ b/src/db/car/mod.rs @@ -16,12 +16,12 @@ use positioned_io::{ReadAt, Size}; use std::{ num::NonZeroUsize, sync::{ - LazyLock, + Arc, LazyLock, atomic::{AtomicUsize, Ordering}, }, }; -use crate::utils::{cache::SizeTrackingLruCache, get_size::CidWrapper}; +use crate::utils::{ShallowClone, cache::SizeTrackingLruCache, get_size::CidWrapper}; pub trait RandomAccessFileReader: ReadAt + Size + Send + Sync + 'static {} impl RandomAccessFileReader for X {} @@ -56,12 +56,22 @@ pub struct ZstdFrameCache { /// Maximum size in bytes. Pages will be evicted if the total size of the /// cache exceeds this amount. pub max_size: usize, - current_size: AtomicUsize, + current_size: Arc, // use `hashbrown::HashMap` here because its `GetSize` implementation is accurate // (thanks to `hashbrown::HashMap::allocation_size`). lru: SizeTrackingLruCache<(FrameOffset, CacheKey), hashbrown::HashMap>>, } +impl ShallowClone for ZstdFrameCache { + fn shallow_clone(&self) -> Self { + Self { + max_size: self.max_size, + current_size: self.current_size.shallow_clone(), + lru: self.lru.shallow_clone(), + } + } +} + impl Default for ZstdFrameCache { fn default() -> Self { ZstdFrameCache::new(*ZSTD_FRAME_CACHE_DEFAULT_MAX_SIZE) @@ -72,7 +82,7 @@ impl ZstdFrameCache { pub fn new(max_size: usize) -> Self { ZstdFrameCache { max_size, - current_size: AtomicUsize::new(0), + current_size: Arc::new(AtomicUsize::new(0)), lru: SizeTrackingLruCache::unbounded_with_metrics("zstd_frame".into()), } } diff --git a/src/dev/subcommands/state_cmd.rs b/src/dev/subcommands/state_cmd.rs index ed122c77d517..0c463d7d2206 100644 --- a/src/dev/subcommands/state_cmd.rs +++ b/src/dev/subcommands/state_cmd.rs @@ -12,6 +12,7 @@ use crate::{ shim::clock::ChainEpoch, state_manager::{ExecutedTipset, StateManager}, tool::subcommands::api_cmd::generate_test_snapshot, + utils::ShallowClone as _, }; use human_repr::HumanCount as _; use nonzero_ext::nonzero; @@ -157,8 +158,12 @@ impl ReplayComputeCommand { crate::state_manager::utils::state_compute::prepare_state_compute(&chain, &snapshot) .await?; for _ in 0..n.get() { - crate::state_manager::utils::state_compute::state_compute(&sm, ts.clone(), &ts_next) - .await?; + crate::state_manager::utils::state_compute::state_compute( + &sm, + ts.shallow_clone(), + &ts_next, + ) + .await?; } Ok(()) } diff --git a/src/fil_cns/validation.rs b/src/fil_cns/validation.rs index e612bf511349..97bf2155df81 100644 --- a/src/fil_cns/validation.rs +++ b/src/fil_cns/validation.rs @@ -21,6 +21,7 @@ use crate::shim::{ version::NetworkVersion, }; use crate::state_manager::StateManager; +use crate::utils::ShallowClone; use crate::utils::encoding::prover_id_from_u64; use cid::Cid; use fil_actors_shared::filecoin_proofs_api::{PublicReplicaInfo, SectorId, post}; @@ -78,7 +79,7 @@ pub(in crate::fil_cns) async fn validate_block { heaviest_tipset: Tipset, epoch: ChainEpoch, root: Cid, - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, bail: AtomicBool, } @@ -46,7 +46,7 @@ impl ForestExternsV2 { heaviest_tipset: Tipset, epoch: ChainEpoch, root: Cid, - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, ) -> Self { ForestExternsV2 { diff --git a/src/interpreter/fvm3.rs b/src/interpreter/fvm3.rs index 589f182287a7..e9aa1a0635ea 100644 --- a/src/interpreter/fvm3.rs +++ b/src/interpreter/fvm3.rs @@ -39,7 +39,7 @@ pub struct ForestExterns { heaviest_tipset: Tipset, epoch: ChainEpoch, root: Cid, - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, bail: AtomicBool, } @@ -50,7 +50,7 @@ impl ForestExterns { heaviest_tipset: Tipset, epoch: ChainEpoch, root: Cid, - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, ) -> Self { ForestExterns { diff --git a/src/interpreter/fvm4.rs b/src/interpreter/fvm4.rs index 1d741ee92d0a..668d1ca58ff7 100644 --- a/src/interpreter/fvm4.rs +++ b/src/interpreter/fvm4.rs @@ -39,7 +39,7 @@ pub struct ForestExterns { heaviest_tipset: Tipset, epoch: ChainEpoch, root: Cid, - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, bail: AtomicBool, } @@ -50,7 +50,7 @@ impl ForestExterns { heaviest_tipset: Tipset, epoch: ChainEpoch, root: Cid, - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, ) -> Self { ForestExterns { diff --git a/src/interpreter/vm.rs b/src/interpreter/vm.rs index dc523e5091e8..5fa1a5b2f088 100644 --- a/src/interpreter/vm.rs +++ b/src/interpreter/vm.rs @@ -162,7 +162,7 @@ pub struct ExecutionContext { // The chain config is used to determine which consensus rules to use. pub chain_config: Arc, // Caching interface to the DB - pub chain_index: Arc>>, + pub chain_index: ChainIndex, // UNIX timestamp for epoch pub timestamp: u64, } diff --git a/src/message_pool/msgpool/mod.rs b/src/message_pool/msgpool/mod.rs index 98720eefd97e..89a24b50c3eb 100644 --- a/src/message_pool/msgpool/mod.rs +++ b/src/message_pool/msgpool/mod.rs @@ -16,6 +16,7 @@ use crate::libp2p::{NetworkMessage, PUBSUB_MSG_STR, Topic}; use crate::message::{MessageRead as _, SignedMessage}; use crate::networks::ChainConfig; use crate::shim::{address::Address, crypto::Signature}; +use crate::utils::ShallowClone as _; use crate::utils::cache::SizeTrackingLruCache; use crate::utils::get_size::CidWrapper; use ahash::{HashMap, HashMapExt, HashSet, HashSetExt}; @@ -57,7 +58,7 @@ async fn republish_pending_messages( where T: Provider, { - let ts = cur_tipset.read().clone(); + let ts = cur_tipset.read().shallow_clone(); let mut pending_map: HashMap> = HashMap::new(); republished.write().clear(); @@ -291,7 +292,7 @@ where .await .map_err(|e| Error::Other(format!("Republish receiver dropped: {e}")))?; } - let cur_ts = cur_tipset.read().clone(); + let cur_ts = cur_tipset.read().shallow_clone(); let mpool_ctx = MpoolCtx { api, key_cache, diff --git a/src/rpc/methods/chain.rs b/src/rpc/methods/chain.rs index 6f64ad8a34bb..cfb86e22ed7a 100644 --- a/src/rpc/methods/chain.rs +++ b/src/rpc/methods/chain.rs @@ -28,6 +28,7 @@ use crate::shim::clock::ChainEpoch; use crate::shim::error::ExitCode; use crate::shim::executor::Receipt; use crate::shim::message::Message; +use crate::utils::ShallowClone; use crate::utils::db::CborStoreExt as _; use crate::utils::io::VoidAsyncWriter; use crate::utils::misc::env::is_env_truthy; @@ -1150,13 +1151,13 @@ impl ChainGetTipSetFinalityStatus { let finalized_tip_set = match (&ec_finalized_tip_set, &f3_finalized_tip_set) { (Some(ec), Some(f3)) => { if ec.epoch() >= f3.epoch() { - Some(ec.clone()) + Some(ec.shallow_clone()) } else { - Some(f3.clone()) + Some(f3.shallow_clone()) } } - (Some(ec), None) => Some(ec.clone()), - (None, Some(f3)) => Some(f3.clone()), + (Some(ec), None) => Some(ec.shallow_clone()), + (None, Some(f3)) => Some(f3.shallow_clone()), (None, None) => None, }; ChainFinalityStatus { @@ -1178,11 +1179,11 @@ impl ChainGetTipSetFinalityStatus { if let Some((cached_head, cached_threshold, cached_tipset)) = &*cache && cached_head == &head { - (*cached_threshold, cached_tipset.clone()) + (*cached_threshold, cached_tipset.shallow_clone()) } else { let (threshold, tipset) = - Self::get_ec_finality_threshold_depth_and_tipset(ctx, head.clone()); - *cache = Some((head, threshold, tipset.clone())); + Self::get_ec_finality_threshold_depth_and_tipset(ctx, head.shallow_clone()); + *cache = Some((head, threshold, tipset.shallow_clone())); (threshold, tipset) } } @@ -1247,7 +1248,7 @@ impl ChainGetTipSetFinalityStatus { head, ResolveNullTipset::TakeOlder, ) { - Some(ts.clone()) + Some(ts.shallow_clone()) } else { None }; @@ -1382,7 +1383,7 @@ impl RpcMethod<1> for ChainGetTipsetByParentState { .heaviest_tipset() .chain(ctx.store()) .find(|ts| ts.parent_state() == &parent_state) - .clone()) + .shallow_clone()) } } @@ -1598,12 +1599,22 @@ impl From for ApiHeadChange { } } -#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, JsonSchema)] +#[derive(PartialEq, Debug, Serialize, Deserialize, JsonSchema)] #[serde(tag = "Type", content = "Val", rename_all = "snake_case")] pub enum PathChange { Revert(T), Apply(T), } + +impl Clone for PathChange { + fn clone(&self) -> Self { + match self { + Self::Revert(i) => Self::Revert(i.clone()), + Self::Apply(i) => Self::Apply(i.clone()), + } + } +} + impl HasLotusJson for PathChange { type LotusJson = PathChange<::LotusJson>; @@ -1685,7 +1696,7 @@ impl PathChanges { #[cfg(test)] impl quickcheck::Arbitrary for PathChange where - T: quickcheck::Arbitrary, + T: quickcheck::Arbitrary + ShallowClone, { fn arbitrary(g: &mut quickcheck::Gen) -> Self { let inner = T::arbitrary(g); diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index 8d1ede8b72b4..00c7899668e5 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -52,6 +52,7 @@ use crate::shim::message::Message; use crate::shim::trace::{CallReturn, ExecutionEvent}; use crate::shim::{clock::ChainEpoch, state_tree::StateTree}; use crate::state_manager::{ExecutedMessage, ExecutedTipset, TipsetState, VMFlush}; +use crate::utils::ShallowClone as _; use crate::utils::cache::SizeTrackingLruCache; use crate::utils::db::BlockstoreExt as _; use crate::utils::encoding::from_slice_with_fallback; @@ -1844,7 +1845,7 @@ where } while high < BLOCK_GAS_LIMIT { - if can_succeed(data, msg.clone(), prior_messages, ts.clone(), high).await? { + if can_succeed(data, msg.clone(), prior_messages, ts.shallow_clone(), high).await? { break; } low = high; @@ -1854,7 +1855,15 @@ where let mut check_threshold = high / 100; while (high - low) > check_threshold { let median = (high + low) / 2; - if can_succeed(data, msg.clone(), prior_messages, ts.clone(), median).await? { + if can_succeed( + data, + msg.clone(), + prior_messages, + ts.shallow_clone(), + median, + ) + .await? + { high = median; } else { low = median; @@ -2073,7 +2082,7 @@ where }; let api_invoc_result = 'invoc: { - for ts in ts.clone().chain(ctx.store()) { + for ts in ts.shallow_clone().chain(ctx.store()) { match ctx .state_manager .call_on_state(state_root, &message, Some(ts)) @@ -3378,7 +3387,7 @@ where { let (state_root, raw_traces) = { let sm = ctx.state_manager.clone(); - let ts = ts.clone(); + let ts = ts.shallow_clone(); tokio::task::spawn_blocking(move || sm.execution_trace(&ts)) .await .context("execution_trace task panicked")?? @@ -3510,7 +3519,7 @@ where let (pre_root, invoc_result, post_root) = ctx .state_manager - .replay_for_prestate(ts.clone(), message_cid) + .replay_for_prestate(ts.shallow_clone(), message_cid) .await .map_err(|e| anyhow::anyhow!("replay for prestate failed: {e}"))?; @@ -3629,7 +3638,7 @@ impl RpcMethod<3> for EthTraceCall { let (invoke_result, post_state_root) = ctx .state_manager - .apply_on_state_with_gas(Some(ts.clone()), msg.clone(), VMFlush::Flush) + .apply_on_state_with_gas(Some(ts.shallow_clone()), msg.clone(), VMFlush::Flush) .await .context("failed to apply message")?; let post_state_root = diff --git a/src/rpc/methods/f3.rs b/src/rpc/methods/f3.rs index 60e65b0c952c..9f9ed6dd225b 100644 --- a/src/rpc/methods/f3.rs +++ b/src/rpc/methods/f3.rs @@ -15,7 +15,6 @@ pub use self::types::{ }; use self::{types::*, util::*}; use super::wallet::WalletSign; -use crate::shim::actors::{miner, power}; use crate::{ blocks::Tipset, chain::index::ResolveNullTipset, @@ -28,11 +27,12 @@ use crate::{ lotus_json::{HasLotusJson as _, LotusJson}, rpc::{ApiPaths, Ctx, Permission, RpcMethod, ServerError, types::ApiTipsetKey}, shim::{ + actors::{miner, power}, address::{Address, Protocol}, clock::ChainEpoch, crypto::Signature, }, - utils::misc::env::is_env_set_and_truthy, + utils::{ShallowClone, misc::env::is_env_set_and_truthy}, }; use ahash::{HashMap, HashSet}; use anyhow::Context as _; @@ -174,7 +174,7 @@ impl GetPowerTable { }); let db = BlockstoreWithReadCache::new( ctx.store_owned(), - BLOCKSTORE_CACHE.clone(), + BLOCKSTORE_CACHE.shallow_clone(), Some(DefaultBlockstoreReadCacheStats::default()), ); @@ -537,7 +537,7 @@ impl RpcMethod<1> for Finalize { .chain_exchange_headers(None, &tsk, nonzero!(1_u64)) .await? .first() - .cloned() + .map(ShallowClone::shallow_clone) .with_context(|| format!("failed to get tipset via chain exchange. tsk: {tsk}"))?, }; let head = ctx.chain_store().heaviest_tipset(); @@ -578,7 +578,7 @@ impl RpcMethod<1> for Finalize { let ts = Arc::new(Tipset::from(fts)); ctx.chain_store().put_tipset(&ts)?; ctx.chain_store() - .set_heaviest_tipset(finalized_ts.clone())?; + .set_heaviest_tipset(finalized_ts.shallow_clone())?; } ctx.chain_store().set_f3_finalized_tipset(finalized_ts); } diff --git a/src/rpc/methods/gas.rs b/src/rpc/methods/gas.rs index d739b77a29c1..167269dd92b7 100644 --- a/src/rpc/methods/gas.rs +++ b/src/rpc/methods/gas.rs @@ -15,6 +15,7 @@ use crate::shim::{ message::Message, }; use crate::state_manager::VMFlush; +use crate::utils::ShallowClone as _; use anyhow::{Context, Result}; use enumflags2::BitFlags; use fvm_ipld_blockstore::Blockstore; @@ -260,7 +261,7 @@ impl GasEstimateGasLimit { .call_with_gas( &mut chain_msg, &prior_messages, - Some(ts.clone()), + Some(ts.shallow_clone()), VMFlush::Skip, ) .await?; diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs index 2cff71b1a41e..aae393bb4c63 100644 --- a/src/rpc/methods/state.rs +++ b/src/rpc/methods/state.rs @@ -45,6 +45,7 @@ use crate::state_manager::{ExecutedTipset, NO_CALLBACK}; use crate::state_manager::{ MarketBalance, StateManager, circulating_supply::GenesisInfo, utils::structured, }; +use crate::utils::ShallowClone as _; use crate::utils::db::car_stream::{CarBlock, CarWriter}; use crate::{ beacon::BeaconEntry, @@ -1574,11 +1575,11 @@ impl RpcMethod<3> for ForestStateCompute { let from_ts = if from_epoch >= to_ts.epoch() { // When `from_epoch` is a null epoch or `n_epochs` is 1, // `to_ts.epoch()` could be less than or equal to `from_epoch` - to_ts.clone() + to_ts.shallow_clone() } else { ctx.chain_index().tipset_by_height( from_epoch, - to_ts.clone(), + to_ts.shallow_clone(), ResolveNullTipset::TakeOlder, )? }; @@ -1589,7 +1590,7 @@ impl RpcMethod<3> for ForestStateCompute { .take_while(|ts| ts.epoch() >= from_ts.epoch()) { let chain_store = ctx.chain_store().clone(); - let network_context = ctx.sync_network_context.clone(); + let network_context = ctx.sync_network_context.shallow_clone(); futures.push_front(async move { if crate::chain_sync::load_full_tipset(&chain_store, ts.key()).is_err() { // Backfill full tipset from the network @@ -2723,7 +2724,7 @@ impl RpcMethod<3> for StateListMessages { } let mut out = Vec::new(); - let mut cur_ts = ts.clone(); + let mut cur_ts = ts.shallow_clone(); while cur_ts.epoch() >= max_height { let msgs = ctx.chain_store().messages_for_tipset(&cur_ts)?; diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index e5293f703a27..5534bada4e13 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -496,7 +496,7 @@ impl RPCState { self.state_manager.chain_store() } - pub fn chain_index(&self) -> &Arc>> { + pub fn chain_index(&self) -> &crate::chain::index::ChainIndex { self.chain_store().chain_index() } diff --git a/src/state_manager/chain_rand.rs b/src/state_manager/chain_rand.rs index 62855f076fad..4f9b3f888489 100644 --- a/src/state_manager/chain_rand.rs +++ b/src/state_manager/chain_rand.rs @@ -9,6 +9,7 @@ use crate::chain::index::{ChainIndex, ResolveNullTipset}; use crate::networks::ChainConfig; use crate::shim::clock::ChainEpoch; use crate::shim::externs::Rand; +use crate::utils::ShallowClone; use crate::utils::encoding::blake2b_256; use anyhow::{Context as _, bail}; use blake2b_simd::Params; @@ -20,17 +21,17 @@ use fvm_ipld_blockstore::Blockstore; pub struct ChainRand { chain_config: Arc, tipset: Tipset, - chain_index: Arc>>, + chain_index: ChainIndex, beacon: Arc, } -impl Clone for ChainRand { - fn clone(&self) -> Self { +impl ShallowClone for ChainRand { + fn shallow_clone(&self) -> Self { ChainRand { - chain_config: self.chain_config.clone(), - tipset: self.tipset.clone(), - chain_index: self.chain_index.clone(), - beacon: self.beacon.clone(), + chain_config: self.chain_config.shallow_clone(), + tipset: self.tipset.shallow_clone(), + chain_index: self.chain_index.shallow_clone(), + beacon: self.beacon.shallow_clone(), } } } diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index 4752bc8de79c..9586618b0193 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -54,6 +54,7 @@ use crate::shim::{ use crate::state_manager::cache::TipsetStateCache; use crate::state_manager::chain_rand::draw_randomness; use crate::state_migration::run_state_migrations; +use crate::utils::ShallowClone as _; use crate::utils::get_size::{GetSize, vec_heap_size_helper}; use ahash::{HashMap, HashMapExt}; use anyhow::{Context as _, bail, ensure}; @@ -336,7 +337,7 @@ where } /// Returns reference to the state manager's [`ChainIndex`]. - pub fn chain_index(&self) -> &Arc>> { + pub fn chain_index(&self) -> &ChainIndex { self.cs.chain_index() } @@ -347,10 +348,10 @@ where pub fn chain_rand(&self, tipset: Tipset) -> ChainRand { ChainRand::new( - self.chain_config().clone(), + self.chain_config().shallow_clone(), tipset, - self.chain_index().clone(), - self.beacon.clone(), + self.chain_index().shallow_clone(), + self.beacon.shallow_clone(), ) } @@ -491,7 +492,7 @@ where Some((state_root, receipt_root, receipts)) => (state_root, receipt_root, receipts), None => { let state_output = self - .compute_tipset_state(msg_ts.clone(), NO_CALLBACK, VMTrace::NotTraced) + .compute_tipset_state(msg_ts.shallow_clone(), NO_CALLBACK, VMTrace::NotTraced) .await?; recomputed = true; ( @@ -518,7 +519,7 @@ where Err(e) if recomputed => return Err(e), Err(_) => { self.compute_tipset_state( - msg_ts.clone(), + msg_ts.shallow_clone(), NO_CALLBACK, VMTrace::NotTraced, ) @@ -571,7 +572,7 @@ where let genesis_info = GenesisInfo::from_chain_config(self.chain_config().clone()); let mut vm = VM::new( ExecutionContext { - heaviest_tipset: tipset.clone(), + heaviest_tipset: tipset.shallow_clone(), state_tree_root: state_cid, epoch: height, rand: Box::new(rand), @@ -581,8 +582,8 @@ where self.blockstore(), &state_cid, )?, - chain_config: self.chain_config().clone(), - chain_index: self.chain_index().clone(), + chain_config: self.chain_config().shallow_clone(), + chain_index: self.chain_index().shallow_clone(), timestamp: tipset.min_timestamp(), }, &self.engine, @@ -625,7 +626,7 @@ where /// changes. pub fn call(&self, message: &Message, tipset: Option) -> Result { let ts = tipset.unwrap_or_else(|| self.heaviest_tipset()); - let chain_rand = self.chain_rand(ts.clone()); + let chain_rand = self.chain_rand(ts.shallow_clone()); self.call_raw(None, message, chain_rand, &ts) } @@ -638,7 +639,7 @@ where tipset: Option, ) -> Result { let ts = tipset.unwrap_or_else(|| self.cs.heaviest_tipset()); - let chain_rand = self.chain_rand(ts.clone()); + let chain_rand = self.chain_rand(ts.shallow_clone()); self.call_raw(Some(state_cid), message, chain_rand, &ts) } @@ -725,8 +726,8 @@ where self.blockstore(), &state_root, )?, - chain_config: self.chain_config().clone(), - chain_index: self.chain_index().clone(), + chain_config: self.chain_config().shallow_clone(), + chain_index: self.chain_index().shallow_clone(), timestamp: ts.min_timestamp(), }, &self.engine, @@ -833,11 +834,11 @@ where let genesis_timestamp = self.chain_store().genesis_block_header().timestamp; let exec = TipsetExecutor::new( - self.chain_index().clone(), - self.chain_config().clone(), - self.beacon_schedule().clone(), + self.chain_index().shallow_clone(), + self.chain_config().shallow_clone(), + self.beacon_schedule().shallow_clone(), &self.engine, - ts.clone(), + ts.shallow_clone(), ); let mut no_cb = NO_CALLBACK; let (parent_state, epoch, block_messages) = @@ -1017,9 +1018,9 @@ where ); Ok(apply_block_messages( self.chain_store().genesis_block_header().timestamp, - Arc::clone(self.chain_index()), - Arc::clone(self.chain_config()), - self.beacon_schedule().clone(), + self.chain_index().shallow_clone(), + self.chain_config().shallow_clone(), + self.beacon_schedule().shallow_clone(), &self.engine, tipset, callback, @@ -1065,9 +1066,9 @@ where messages, tipset, self.chain_store().genesis_block_header().timestamp, - Arc::clone(self.chain_index()), - Arc::clone(self.chain_config()), - self.beacon_schedule().clone(), + self.chain_index().shallow_clone(), + self.chain_config().shallow_clone(), + self.beacon_schedule().shallow_clone(), &self.engine, callback, enable_tracing, @@ -1244,13 +1245,13 @@ where let maybe_message_receipt = self.tipset_executed_message(¤t_tipset, &message, true)?; if let Some(r) = maybe_message_receipt { - return Ok((Some(current_tipset.clone()), Some(r))); + return Ok((Some(current_tipset.shallow_clone()), Some(r))); } let mut candidate_tipset: Option = None; let mut candidate_receipt: Option = None; - let sm_cloned = Arc::clone(self); + let sm_cloned = self.shallow_clone(); let message_for_task = message.clone(); let height_of_head = current_tipset.epoch(); @@ -1694,9 +1695,9 @@ where let genesis_timestamp = self.chain_store().genesis_block_header().timestamp; validate_tipsets( genesis_timestamp, - self.chain_index().clone(), - self.chain_config().clone(), - self.beacon_schedule().clone(), + self.chain_index(), + self.chain_config(), + self.beacon_schedule(), &self.engine, tipsets, ) @@ -1826,11 +1827,11 @@ where let ExecutedTipset { state_root, .. } = apply_block_messages( genesis_timestamp, - self.chain_index().clone(), - self.chain_config().clone(), - self.beacon_schedule().clone(), + self.chain_index().shallow_clone(), + self.chain_config().shallow_clone(), + self.beacon_schedule().shallow_clone(), &self.engine, - tipset.clone(), + tipset.shallow_clone(), Some(callback), VMTrace::Traced, )?; @@ -1841,9 +1842,9 @@ where pub fn validate_tipsets( genesis_timestamp: u64, - chain_index: Arc>>, - chain_config: Arc, - beacon: Arc, + chain_index: &ChainIndex, + chain_config: &Arc, + beacon: &Arc, engine: &MultiEngine, tipsets: T, ) -> anyhow::Result<()> @@ -1863,9 +1864,9 @@ where .. } = apply_block_messages( genesis_timestamp, - chain_index.clone(), - chain_config.clone(), - beacon.clone(), + chain_index.shallow_clone(), + chain_config.shallow_clone(), + beacon.shallow_clone(), engine, parent, NO_CALLBACK, @@ -1899,26 +1900,26 @@ struct TipsetExecutor<'a, DB: Blockstore + Send + Sync + 'static> { tipset: Tipset, rand: ChainRand, chain_config: Arc, - chain_index: Arc>>, + chain_index: ChainIndex, genesis_info: GenesisInfo, engine: &'a MultiEngine, } impl<'a, DB: Blockstore + Send + Sync + 'static> TipsetExecutor<'a, DB> { fn new( - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, beacon: Arc, engine: &'a MultiEngine, tipset: Tipset, ) -> Self { let rand = ChainRand::new( - chain_config.clone(), - tipset.clone(), - chain_index.clone(), + chain_config.shallow_clone(), + tipset.shallow_clone(), + chain_index.shallow_clone(), beacon, ); - let genesis_info = GenesisInfo::from_chain_config(chain_config.clone()); + let genesis_info = GenesisInfo::from_chain_config(chain_config.shallow_clone()); Self { tipset, rand, @@ -1943,14 +1944,14 @@ impl<'a, DB: Blockstore + Send + Sync + 'static> TipsetExecutor<'a, DB> { )?; VM::new( ExecutionContext { - heaviest_tipset: self.tipset.clone(), + heaviest_tipset: self.tipset.shallow_clone(), state_tree_root: state_root, epoch, - rand: Box::new(self.rand.clone()), + rand: Box::new(self.rand.shallow_clone()), base_fee: self.tipset.min_ticket_block().parent_base_fee.clone(), circ_supply, - chain_config: self.chain_config.clone(), - chain_index: self.chain_index.clone(), + chain_config: self.chain_config.shallow_clone(), + chain_index: self.chain_index.shallow_clone(), timestamp, }, self.engine, @@ -2085,7 +2086,7 @@ impl<'a, DB: Blockstore + Send + Sync + 'static> TipsetExecutor<'a, DB> { #[allow(clippy::too_many_arguments)] pub fn apply_block_messages( genesis_timestamp: u64, - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, beacon: Arc, engine: &MultiEngine, @@ -2118,11 +2119,11 @@ where } let exec = TipsetExecutor::new( - chain_index.clone(), + chain_index.shallow_clone(), chain_config, beacon, engine, - tipset.clone(), + tipset.shallow_clone(), ); // step 2: running cron for any null-tipsets @@ -2198,7 +2199,7 @@ pub fn compute_state( messages: Vec, tipset: Tipset, genesis_timestamp: u64, - chain_index: Arc>>, + chain_index: ChainIndex, chain_config: Arc, beacon: Arc, engine: &MultiEngine, diff --git a/src/tool/subcommands/archive_cmd.rs b/src/tool/subcommands/archive_cmd.rs index 582842b48918..51174e9190fa 100644 --- a/src/tool/subcommands/archive_cmd.rs +++ b/src/tool/subcommands/archive_cmd.rs @@ -46,6 +46,7 @@ use crate::shim::fvm_shared_latest::address::Network; use crate::shim::machine::GLOBAL_MULTI_ENGINE; use crate::state_manager::{ExecutedTipset, NO_CALLBACK, apply_block_messages}; use crate::tool::subcommands::api_cmd::generate_test_snapshot::ReadOpsTrackingStore; +use crate::utils::ShallowClone as _; use crate::utils::db::car_stream::{CarBlock, CarBlockWrite as _, CarStream}; use crate::utils::multihash::MultihashCode; use anyhow::{Context as _, bail}; @@ -584,7 +585,7 @@ pub async fn do_export( let seen = if let Some(diff) = diff { let diff_ts: Tipset = index - .tipset_by_height(diff, ts.clone(), ResolveNullTipset::TakeOlder) + .tipset_by_height(diff, ts.shallow_clone(), ResolveNullTipset::TakeOlder) .context("diff epoch must be smaller than target epoch")?; let diff_ts: &Tipset = &diff_ts; let diff_limit = diff_depth.map(|depth| diff_ts.epoch() - depth).unwrap_or(0); @@ -845,7 +846,7 @@ async fn show_tipset_diff( let ExecutedTipset { state_root, .. } = apply_block_messages( timestamp, - Arc::new(chain_index), + chain_index, Arc::new(chain_config), beacon, &GLOBAL_MULTI_ENGINE, diff --git a/src/tool/subcommands/benchmark_cmd.rs b/src/tool/subcommands/benchmark_cmd.rs index 5eb93274f19a..8ad2f6a09013 100644 --- a/src/tool/subcommands/benchmark_cmd.rs +++ b/src/tool/subcommands/benchmark_cmd.rs @@ -222,7 +222,7 @@ async fn benchmark_exporting( ) -> anyhow::Result<()> { let store = Arc::new(open_store(input)?); let heaviest = store.heaviest_tipset()?; - let idx = ChainIndex::new(&store); + let idx = ChainIndex::new(store.clone()); let ts = idx.tipset_by_height( epoch.unwrap_or(heaviest.epoch()), heaviest, diff --git a/src/tool/subcommands/snapshot_cmd.rs b/src/tool/subcommands/snapshot_cmd.rs index 4da186e70b44..b84b98545504 100644 --- a/src/tool/subcommands/snapshot_cmd.rs +++ b/src/tool/subcommands/snapshot_cmd.rs @@ -425,7 +425,7 @@ where ensure_proof_params_downloaded(), )?; - let chain_index = Arc::new(ChainIndex::new(Arc::new(db.clone()))); + let chain_index = ChainIndex::new(db.clone()); // Prepare tipsets for validation let tipsets = ts @@ -441,9 +441,9 @@ where // iterator is consumed. crate::state_manager::validate_tipsets( genesis.timestamp, - chain_index.clone(), - chain_config, - beacon, + &chain_index, + &chain_config, + &beacon, &GLOBAL_MULTI_ENGINE, tipsets, )?; @@ -488,7 +488,7 @@ fn print_computed_state(snapshot: PathBuf, epoch: ChainEpoch, json: bool) -> any let ExecutedTipset { state_root, .. } = apply_block_messages( timestamp, - Arc::new(chain_index), + chain_index, Arc::new(chain_config), beacon, &GLOBAL_MULTI_ENGINE, diff --git a/src/utils/cache/lru.rs b/src/utils/cache/lru.rs index 2686be68d3d0..b219b35f7c7c 100644 --- a/src/utils/cache/lru.rs +++ b/src/utils/cache/lru.rs @@ -22,6 +22,8 @@ use prometheus_client::{ registry::Unit, }; +use crate::utils::ShallowClone; + pub trait KeyConstraints: GetSize + Debug + Send + Sync + Hash + PartialEq + Eq + Clone + 'static { @@ -36,7 +38,7 @@ pub trait LruValueConstraints: GetSize + Debug + Send + Sync + Clone + 'static { impl LruValueConstraints for T where T: GetSize + Debug + Send + Sync + Clone + 'static {} -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SizeTrackingLruCache where K: KeyConstraints, @@ -47,13 +49,27 @@ where cache: Arc>>, } +impl ShallowClone for SizeTrackingLruCache +where + K: KeyConstraints, + V: LruValueConstraints, +{ + fn shallow_clone(&self) -> Self { + Self { + cache_id: self.cache_id, + cache_name: self.cache_name.clone(), + cache: self.cache.shallow_clone(), + } + } +} + impl SizeTrackingLruCache where K: KeyConstraints, V: LruValueConstraints, { fn register_metrics(&self) { - crate::metrics::register_collector(Box::new(self.clone())); + crate::metrics::register_collector(Box::new(self.shallow_clone())); } fn new_inner(cache_name: Cow<'static, str>, capacity: Option) -> Self { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 9aabea58ca51..5c0dce2a7f82 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -17,6 +17,8 @@ pub mod p2p; pub mod proofs_api; pub mod rand; pub mod reqwest_resume; +mod shallow_clone; +pub use shallow_clone::ShallowClone; #[cfg(feature = "sqlite")] pub mod sqlite; pub mod stats; diff --git a/src/utils/shallow_clone.rs b/src/utils/shallow_clone.rs new file mode 100644 index 000000000000..2e91de6deddc --- /dev/null +++ b/src/utils/shallow_clone.rs @@ -0,0 +1,20 @@ +// Copyright 2019-2026 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use std::sync::Arc; + +pub trait ShallowClone { + fn shallow_clone(&self) -> Self; +} + +impl ShallowClone for Arc { + fn shallow_clone(&self) -> Self { + self.clone() + } +} + +impl ShallowClone for Option { + fn shallow_clone(&self) -> Self { + self.as_ref().map(ShallowClone::shallow_clone) + } +} From ac71a907b87e6ad9c8e7576ffc75dc1c67b0ff2f Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Mon, 13 Apr 2026 15:58:10 +0800 Subject: [PATCH 2/2] doc for ShallowClone --- src/utils/shallow_clone.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/utils/shallow_clone.rs b/src/utils/shallow_clone.rs index 2e91de6deddc..3d0303fa5f32 100644 --- a/src/utils/shallow_clone.rs +++ b/src/utils/shallow_clone.rs @@ -3,7 +3,17 @@ use std::sync::Arc; +/// A trait for performing a lightweight clone of a type. +/// +/// Implementations should clone only the outer wrapper and preserve any +/// shared internal state where appropriate (for example, `Arc` clones the +/// pointer without cloning the inner value). pub trait ShallowClone { + /// Performs a lightweight clone. + /// + /// Implementations should clone only the outer wrapper and preserve any + /// shared internal state where appropriate (for example, `Arc` clones the + /// pointer without cloning the inner value). fn shallow_clone(&self) -> Self; }