Skip to main content

tycho_block_util/state/
shard_state_stuff.rs

1use std::mem::ManuallyDrop;
2use std::sync::Arc;
3
4use anyhow::{Context, Result};
5use tycho_types::cell::Lazy;
6use tycho_types::merkle::MerkleUpdate;
7use tycho_types::models::*;
8use tycho_types::prelude::*;
9use tycho_util::FastHashSet;
10use tycho_util::mem::Reclaimer;
11
12use crate::dict::split_aug_dict_raw;
13use crate::state::RefMcStateHandle;
14
15/// Parsed shard state.
16#[derive(Clone)]
17#[repr(transparent)]
18pub struct ShardStateStuff {
19    inner: Arc<Inner>,
20}
21
22impl ShardStateStuff {
23    pub fn construct_split_root(left: Cell, right: Cell) -> Result<Cell> {
24        CellBuilder::build_from(ShardStateSplit {
25            left: Lazy::from_raw(left)?,
26            right: Lazy::from_raw(right)?,
27        })
28        .map_err(From::from)
29    }
30
31    pub fn from_root(block_id: &BlockId, root: Cell, handle: RefMcStateHandle) -> Result<Self> {
32        let shard_state = root.parse::<Box<ShardStateUnsplit>>()?;
33        Self::from_state_and_root(block_id, shard_state, root, handle)
34    }
35
36    pub fn from_state_and_root(
37        block_id: &BlockId,
38        shard_state: Box<ShardStateUnsplit>,
39        root: Cell,
40        handle: RefMcStateHandle,
41    ) -> Result<Self> {
42        anyhow::ensure!(
43            shard_state.shard_ident == block_id.shard,
44            "shard state shard_ident mismatch"
45        );
46
47        anyhow::ensure!(shard_state.seqno == block_id.seqno, "state seqno mismatch");
48
49        Ok(Self {
50            inner: Arc::new(Inner {
51                block_id: *block_id,
52                parts: ManuallyDrop::new(InnerParts {
53                    shard_state_extra: shard_state.load_custom()?,
54                    shard_state,
55                    root,
56                    handle,
57                }),
58            }),
59        })
60    }
61
62    pub fn block_id(&self) -> &BlockId {
63        &self.inner.block_id
64    }
65
66    pub fn state(&self) -> &ShardStateUnsplit {
67        &self.inner.shard_state
68    }
69
70    pub fn state_extra(&self) -> Result<&McStateExtra> {
71        let Some(extra) = self.inner.shard_state_extra.as_ref() else {
72            anyhow::bail!("given state is not a masterchain state");
73        };
74        Ok(extra)
75    }
76
77    pub fn ref_mc_state_handle(&self) -> &RefMcStateHandle {
78        &self.inner.handle
79    }
80
81    pub fn root_cell(&self) -> &Cell {
82        &self.inner.root
83    }
84
85    pub fn shards(&self) -> Result<&ShardHashes> {
86        Ok(&self.state_extra()?.shards)
87    }
88
89    pub fn config_params(&self) -> Result<&BlockchainConfig> {
90        Ok(&self.state_extra()?.config)
91    }
92
93    pub fn get_gen_chain_time(&self) -> u64 {
94        let state = self.state();
95        debug_assert!(state.gen_utime_ms < 1000);
96        state.gen_utime as u64 * 1000 + state.gen_utime_ms as u64
97    }
98
99    pub fn get_top_shards(&self) -> Result<Vec<ShardIdent>> {
100        let mut res = vec![self.block_id().shard];
101
102        for item in self.shards()?.latest_blocks() {
103            let block_id = item?;
104            res.push(block_id.shard);
105        }
106
107        Ok(res)
108    }
109
110    /// Creates a derived state which tracks access to cells data and references.
111    #[must_use = "this new state must be used to track cell usage"]
112    pub fn track_usage(&self, usage_mode: UsageTreeMode) -> Result<(UsageTree, Self)> {
113        let usage_tree = UsageTree::new(usage_mode);
114        let root = usage_tree.track(&Cell::untrack(self.inner.root.clone()));
115
116        // NOTE: Reload parsed object from a tracked cell to fill the tracker
117        // with the tree root.
118        let shard_state = root.parse::<Box<ShardStateUnsplit>>()?;
119
120        let shard_state = Self {
121            inner: Arc::new(Inner {
122                block_id: self.inner.block_id,
123                parts: ManuallyDrop::new(InnerParts {
124                    shard_state_extra: shard_state.load_custom()?,
125                    shard_state,
126                    root,
127                    handle: self.inner.handle.clone(),
128                }),
129            }),
130        };
131
132        Ok((usage_tree, shard_state))
133    }
134
135    /// Applies merkle update of the specified block and preserves
136    /// the `tracker` from the initial state.
137    ///
138    /// NOTE: Call from inside `rayon`.
139    pub fn par_make_next_state(
140        &self,
141        next_block_id: &BlockId,
142        merkle_update: &MerkleUpdate,
143        split_at_depth: Option<u8>,
144    ) -> Result<Self> {
145        let old_split_at = if let Some(depth) = split_at_depth {
146            let shard_accounts = self
147                .root_cell()
148                .reference_cloned(1)
149                .context("invalid shard state")?
150                .parse::<ShardAccounts>()
151                .context("failed to load shard accounts")?;
152
153            split_aug_dict_raw(shard_accounts, depth)
154                .context("failed to split shard accounts")?
155                .into_keys()
156                .collect::<FastHashSet<_>>()
157        } else {
158            Default::default()
159        };
160
161        let new_root = merkle_update
162            .par_apply(&self.inner.root, &old_split_at)
163            .context("failed to apply merkle update")?;
164
165        let shard_state = new_root.parse::<Box<ShardStateUnsplit>>()?;
166        anyhow::ensure!(
167            shard_state.shard_ident == next_block_id.shard,
168            "shard state shard_ident mismatch"
169        );
170        anyhow::ensure!(
171            shard_state.seqno == next_block_id.seqno,
172            "state seqno mismatch"
173        );
174
175        Ok(Self {
176            inner: Arc::new(Inner {
177                block_id: *next_block_id,
178                parts: ManuallyDrop::new(InnerParts {
179                    shard_state_extra: shard_state.load_custom()?,
180                    shard_state,
181                    root: new_root,
182                    handle: self.inner.handle.clone(),
183                }),
184            }),
185        })
186    }
187}
188
189impl AsRef<ShardStateUnsplit> for ShardStateStuff {
190    #[inline]
191    fn as_ref(&self) -> &ShardStateUnsplit {
192        &self.inner.shard_state
193    }
194}
195
196unsafe impl arc_swap::RefCnt for ShardStateStuff {
197    type Base = Inner;
198
199    fn into_ptr(me: Self) -> *mut Self::Base {
200        arc_swap::RefCnt::into_ptr(me.inner)
201    }
202
203    fn as_ptr(me: &Self) -> *mut Self::Base {
204        arc_swap::RefCnt::as_ptr(&me.inner)
205    }
206
207    unsafe fn from_ptr(ptr: *const Self::Base) -> Self {
208        Self {
209            inner: unsafe { arc_swap::RefCnt::from_ptr(ptr) },
210        }
211    }
212}
213
214#[doc(hidden)]
215pub struct Inner {
216    block_id: BlockId,
217    parts: ManuallyDrop<InnerParts>,
218}
219
220impl std::ops::Deref for Inner {
221    type Target = InnerParts;
222
223    #[inline]
224    fn deref(&self) -> &Self::Target {
225        &self.parts
226    }
227}
228
229impl Drop for Inner {
230    fn drop(&mut self) {
231        // SAFETY: Inner is dropped only once.
232        let parts = unsafe { ManuallyDrop::take(&mut self.parts) };
233        Reclaimer::instance().drop(parts);
234    }
235}
236
237#[doc(hidden)]
238pub struct InnerParts {
239    shard_state: Box<ShardStateUnsplit>,
240    shard_state_extra: Option<McStateExtra>,
241    root: Cell,
242    // The fields of a struct are dropped in declaration order. So we need the
243    // `root` field to drop BEFORE the handle.
244    handle: RefMcStateHandle,
245}