casper_storage/tracking_copy/
ext.rs

1use std::{
2    collections::{btree_map::Entry, BTreeMap},
3    convert::TryInto,
4};
5use tracing::{error, warn};
6
7use crate::{
8    data_access_layer::balance::{
9        AvailableBalanceChecker, BalanceHolds, BalanceHoldsWithProof, ProcessingHoldBalanceHandling,
10    },
11    global_state::{error::Error as GlobalStateError, state::StateReader},
12    tracking_copy::{TrackingCopy, TrackingCopyEntityExt, TrackingCopyError},
13    KeyPrefix,
14};
15use casper_types::{
16    account::AccountHash,
17    addressable_entity::MessageTopics,
18    bytesrepr::ToBytes,
19    contract_messages::TopicNameHash,
20    contracts::{ContractHash, NamedKeys},
21    global_state::TrieMerkleProof,
22    system::{
23        mint::{
24            BalanceHoldAddr, BalanceHoldAddrTag, MINT_GAS_HOLD_HANDLING_KEY,
25            MINT_GAS_HOLD_INTERVAL_KEY,
26        },
27        MINT,
28    },
29    BlockGlobalAddr, BlockTime, ByteCode, ByteCodeAddr, ByteCodeHash, CLValue, ChecksumRegistry,
30    Contract, EntityAddr, EntryPoints, HashAddr, HoldBalanceHandling, HoldsEpoch, Key, Motes,
31    Package, StoredValue, StoredValueTypeMismatch, SystemHashRegistry, URef, URefAddr, U512,
32};
33
34/// Higher-level operations on the state via a `TrackingCopy`.
35pub trait TrackingCopyExt<R> {
36    /// The type for the returned errors.
37    type Error;
38
39    /// Reads the entity key for a given account hash.
40    fn read_account_key(&mut self, account_hash: AccountHash) -> Result<Key, Self::Error>;
41
42    /// Returns block time associated with checked out root hash.
43    fn get_block_time(&self) -> Result<Option<BlockTime>, Self::Error>;
44
45    /// Returns balance hold configuration settings for imputed kind of balance hold.
46    fn get_balance_hold_config(
47        &self,
48        hold_kind: BalanceHoldAddrTag,
49    ) -> Result<Option<(BlockTime, HoldBalanceHandling, u64)>, Self::Error>;
50
51    /// Gets the purse balance key for a given purse.
52    fn get_purse_balance_key(&self, purse_key: Key) -> Result<Key, Self::Error>;
53
54    /// Gets the balance hold keys for the imputed purse (if any).
55    fn get_balance_hold_addresses(
56        &self,
57        purse_addr: URefAddr,
58    ) -> Result<Vec<BalanceHoldAddr>, Self::Error>;
59
60    /// Returns total balance.
61    fn get_total_balance(&self, key: Key) -> Result<Motes, Self::Error>;
62
63    /// Returns the available balance, considering any holds from holds_epoch to now.
64    fn get_available_balance(&mut self, balance_key: Key) -> Result<Motes, Self::Error>;
65
66    /// Gets the purse balance key for a given purse and provides a Merkle proof.
67    fn get_purse_balance_key_with_proof(
68        &self,
69        purse_key: Key,
70    ) -> Result<(Key, TrieMerkleProof<Key, StoredValue>), Self::Error>;
71
72    /// Gets the balance at a given balance key and provides a Merkle proof.
73    fn get_total_balance_with_proof(
74        &self,
75        balance_key: Key,
76    ) -> Result<(U512, TrieMerkleProof<Key, StoredValue>), Self::Error>;
77
78    /// Clear expired balance holds.
79    fn clear_expired_balance_holds(
80        &mut self,
81        purse_addr: URefAddr,
82        filter: Vec<(BalanceHoldAddrTag, HoldsEpoch)>,
83    ) -> Result<(), Self::Error>;
84
85    /// Gets the balance holds for a given balance, without Merkle proofs.
86    fn get_balance_holds(
87        &mut self,
88        purse_addr: URefAddr,
89        block_time: BlockTime,
90        interval: u64,
91    ) -> Result<BTreeMap<BlockTime, BalanceHolds>, Self::Error>;
92
93    /// Gets the balance holds for a given balance, with Merkle proofs.
94    fn get_balance_holds_with_proof(
95        &self,
96        purse_addr: URefAddr,
97    ) -> Result<BTreeMap<BlockTime, BalanceHoldsWithProof>, Self::Error>;
98
99    /// Returns the collection of message topics (if any) for a given HashAddr.
100    fn get_message_topics(&self, entity_addr: EntityAddr) -> Result<MessageTopics, Self::Error>;
101
102    /// Returns the collection of named keys for a given AddressableEntity.
103    fn get_named_keys(&self, entity_addr: EntityAddr) -> Result<NamedKeys, Self::Error>;
104
105    /// Returns the collection of entry points for a given AddresableEntity.
106    fn get_v1_entry_points(&self, entity_addr: EntityAddr) -> Result<EntryPoints, Self::Error>;
107
108    /// Gets a package by hash.
109    fn get_package(&mut self, package_hash: HashAddr) -> Result<Package, Self::Error>;
110
111    /// Get a Contract record.
112    fn get_contract(&mut self, contract_hash: ContractHash) -> Result<Contract, Self::Error>;
113
114    /// Gets the system entity registry.
115    fn get_system_entity_registry(&self) -> Result<SystemHashRegistry, Self::Error>;
116
117    /// Gets the system checksum registry.
118    fn get_checksum_registry(&mut self) -> Result<Option<ChecksumRegistry>, Self::Error>;
119
120    /// Gets byte code by hash.
121    fn get_byte_code(&mut self, byte_code_hash: ByteCodeHash) -> Result<ByteCode, Self::Error>;
122}
123
124impl<R> TrackingCopyExt<R> for TrackingCopy<R>
125where
126    R: StateReader<Key, StoredValue, Error = GlobalStateError>,
127{
128    type Error = TrackingCopyError;
129
130    fn read_account_key(&mut self, account_hash: AccountHash) -> Result<Key, Self::Error> {
131        let account_key = Key::Account(account_hash);
132        match self.read(&account_key)? {
133            Some(StoredValue::CLValue(cl_value)) => Ok(CLValue::into_t(cl_value)?),
134            Some(other) => Err(TrackingCopyError::TypeMismatch(
135                StoredValueTypeMismatch::new("Account".to_string(), other.type_name()),
136            )),
137            None => Err(TrackingCopyError::KeyNotFound(account_key)),
138        }
139    }
140
141    fn get_block_time(&self) -> Result<Option<BlockTime>, Self::Error> {
142        match self.read(&Key::BlockGlobal(BlockGlobalAddr::BlockTime))? {
143            None => Ok(None),
144            Some(StoredValue::CLValue(cl_value)) => {
145                let block_time = cl_value.into_t().map_err(Self::Error::CLValue)?;
146                Ok(Some(BlockTime::new(block_time)))
147            }
148            Some(unexpected) => {
149                warn!(?unexpected, "block time stored as unexpected value type");
150                Err(Self::Error::UnexpectedStoredValueVariant)
151            }
152        }
153    }
154
155    fn get_balance_hold_config(
156        &self,
157        hold_kind: BalanceHoldAddrTag,
158    ) -> Result<Option<(BlockTime, HoldBalanceHandling, u64)>, Self::Error> {
159        let block_time = match self.get_block_time()? {
160            None => return Ok(None),
161            Some(block_time) => block_time,
162        };
163        let (handling_key, interval_key) = match hold_kind {
164            BalanceHoldAddrTag::Processing => {
165                return Ok(Some((block_time, HoldBalanceHandling::Accrued, 0)));
166            }
167            BalanceHoldAddrTag::Gas => (MINT_GAS_HOLD_HANDLING_KEY, MINT_GAS_HOLD_INTERVAL_KEY),
168        };
169
170        let system_contract_registry = self.get_system_entity_registry()?;
171
172        let entity_hash = *system_contract_registry.get(MINT).ok_or_else(|| {
173            error!("Missing system mint contract hash");
174            TrackingCopyError::MissingSystemContractHash(MINT.to_string())
175        })?;
176
177        let named_keys = self.get_named_keys(EntityAddr::System(entity_hash))?;
178
179        // get the handling
180        let handling = {
181            let named_key =
182                named_keys
183                    .get(handling_key)
184                    .ok_or(TrackingCopyError::NamedKeyNotFound(
185                        handling_key.to_string(),
186                    ))?;
187            let _uref = named_key
188                .as_uref()
189                .ok_or(TrackingCopyError::UnexpectedKeyVariant(*named_key))?;
190
191            match self.read(&named_key.normalize()) {
192                Ok(Some(StoredValue::CLValue(cl_value))) => {
193                    let handling_tag = cl_value.into_t().map_err(TrackingCopyError::CLValue)?;
194                    HoldBalanceHandling::from_tag(handling_tag).map_err(|_| {
195                        TrackingCopyError::ValueNotFound(
196                            "No hold balance handling variant matches stored tag".to_string(),
197                        )
198                    })?
199                }
200                Ok(Some(unexpected)) => {
201                    warn!(
202                        ?unexpected,
203                        "hold balance handling unexpected stored value variant"
204                    );
205                    return Err(TrackingCopyError::UnexpectedStoredValueVariant);
206                }
207                Ok(None) => {
208                    error!("hold balance handling missing from gs");
209                    return Err(TrackingCopyError::ValueNotFound(handling_key.to_string()));
210                }
211                Err(gse) => {
212                    error!(?gse, "hold balance handling read error");
213                    return Err(TrackingCopyError::Storage(gse));
214                }
215            }
216        };
217
218        // get the interval.
219        let interval = {
220            let named_key =
221                named_keys
222                    .get(interval_key)
223                    .ok_or(TrackingCopyError::NamedKeyNotFound(
224                        interval_key.to_string(),
225                    ))?;
226            let _uref = named_key
227                .as_uref()
228                .ok_or(TrackingCopyError::UnexpectedKeyVariant(*named_key))?;
229
230            match self.read(&named_key.normalize()) {
231                Ok(Some(StoredValue::CLValue(cl_value))) => {
232                    cl_value.into_t().map_err(TrackingCopyError::CLValue)?
233                }
234                Ok(Some(unexpected)) => {
235                    warn!(
236                        ?unexpected,
237                        "hold balance interval unexpected stored value variant"
238                    );
239                    return Err(TrackingCopyError::UnexpectedStoredValueVariant);
240                }
241                Ok(None) => {
242                    error!("hold balance interval missing from gs");
243                    return Err(TrackingCopyError::ValueNotFound(handling_key.to_string()));
244                }
245                Err(gse) => return Err(TrackingCopyError::Storage(gse)),
246            }
247        };
248
249        Ok(Some((block_time, handling, interval)))
250    }
251
252    fn get_purse_balance_key(&self, purse_key: Key) -> Result<Key, Self::Error> {
253        let balance_key: URef = purse_key
254            .into_uref()
255            .ok_or(TrackingCopyError::UnexpectedKeyVariant(purse_key))?;
256        Ok(Key::Balance(balance_key.addr()))
257    }
258
259    fn get_balance_hold_addresses(
260        &self,
261        purse_addr: URefAddr,
262    ) -> Result<Vec<BalanceHoldAddr>, Self::Error> {
263        let tagged_keys = {
264            let mut ret: Vec<BalanceHoldAddr> = vec![];
265            let gas_prefix = KeyPrefix::GasBalanceHoldsByPurse(purse_addr).to_bytes()?;
266            for key in self.keys_with_prefix(&gas_prefix)? {
267                let addr = key
268                    .as_balance_hold()
269                    .ok_or(Self::Error::UnexpectedKeyVariant(key))?;
270                ret.push(*addr);
271            }
272            let processing_prefix =
273                KeyPrefix::ProcessingBalanceHoldsByPurse(purse_addr).to_bytes()?;
274            for key in self.keys_with_prefix(&processing_prefix)? {
275                let addr = key
276                    .as_balance_hold()
277                    .ok_or(Self::Error::UnexpectedKeyVariant(key))?;
278                ret.push(*addr);
279            }
280            ret
281        };
282        Ok(tagged_keys)
283    }
284
285    fn get_total_balance(&self, key: Key) -> Result<Motes, Self::Error> {
286        let key = {
287            if let Key::URef(uref) = key {
288                Key::Balance(uref.addr())
289            } else {
290                key
291            }
292        };
293        if let Key::Balance(_) = key {
294            let stored_value: StoredValue = self
295                .read(&key)?
296                .ok_or(TrackingCopyError::KeyNotFound(key))?;
297            let cl_value: CLValue = stored_value
298                .try_into()
299                .map_err(TrackingCopyError::TypeMismatch)?;
300            let total_balance = cl_value.into_t::<U512>()?;
301            Ok(Motes::new(total_balance))
302        } else {
303            Err(Self::Error::UnexpectedKeyVariant(key))
304        }
305    }
306
307    fn get_available_balance(&mut self, key: Key) -> Result<Motes, Self::Error> {
308        let purse_addr = {
309            if let Key::URef(uref) = key {
310                uref.addr()
311            } else if let Key::Balance(uref_addr) = key {
312                uref_addr
313            } else {
314                return Err(Self::Error::UnexpectedKeyVariant(key));
315            }
316        };
317
318        let total_balance = self.get_total_balance(Key::Balance(purse_addr))?.value();
319        let (block_time, handling, interval) =
320            match self.get_balance_hold_config(BalanceHoldAddrTag::Gas)? {
321                None => {
322                    // if there is no hold config at this root hash, holds are not a thing
323                    // and available balance = total balance
324                    return Ok(Motes::new(total_balance));
325                }
326                Some((block_time, handling, interval)) => (block_time, handling, interval),
327            };
328
329        let balance_holds = self.get_balance_holds(purse_addr, block_time, interval)?;
330        let gas_handling = (handling, interval).into();
331        let processing_handling = ProcessingHoldBalanceHandling::new();
332        match balance_holds.available_balance(
333            block_time,
334            total_balance,
335            gas_handling,
336            processing_handling,
337        ) {
338            Ok(balance) => Ok(Motes::new(balance)),
339            Err(balance_error) => Err(Self::Error::Balance(balance_error)),
340        }
341    }
342
343    fn get_purse_balance_key_with_proof(
344        &self,
345        purse_key: Key,
346    ) -> Result<(Key, TrieMerkleProof<Key, StoredValue>), Self::Error> {
347        let balance_key: Key = purse_key
348            .uref_to_hash()
349            .ok_or(TrackingCopyError::UnexpectedKeyVariant(purse_key))?;
350        let proof: TrieMerkleProof<Key, StoredValue> = self
351            .read_with_proof(&balance_key)?
352            .ok_or(TrackingCopyError::KeyNotFound(purse_key))?;
353        let stored_value_ref: &StoredValue = proof.value();
354        let cl_value: CLValue = stored_value_ref
355            .to_owned()
356            .try_into()
357            .map_err(TrackingCopyError::TypeMismatch)?;
358        let balance_key: Key = cl_value.into_t()?;
359        Ok((balance_key, proof))
360    }
361
362    fn get_total_balance_with_proof(
363        &self,
364        key: Key,
365    ) -> Result<(U512, TrieMerkleProof<Key, StoredValue>), Self::Error> {
366        let key = {
367            if let Key::URef(uref) = key {
368                Key::Balance(uref.addr())
369            } else {
370                key
371            }
372        };
373        if let Key::Balance(_) = key {
374            let proof: TrieMerkleProof<Key, StoredValue> = self
375                .read_with_proof(&key.normalize())?
376                .ok_or(TrackingCopyError::KeyNotFound(key))?;
377            let cl_value: CLValue = proof
378                .value()
379                .to_owned()
380                .try_into()
381                .map_err(TrackingCopyError::TypeMismatch)?;
382            let balance = cl_value.into_t()?;
383            Ok((balance, proof))
384        } else {
385            Err(Self::Error::UnexpectedKeyVariant(key))
386        }
387    }
388
389    fn clear_expired_balance_holds(
390        &mut self,
391        purse_addr: URefAddr,
392        filter: Vec<(BalanceHoldAddrTag, HoldsEpoch)>,
393    ) -> Result<(), Self::Error> {
394        for (tag, holds_epoch) in filter {
395            let prefix = match tag {
396                BalanceHoldAddrTag::Gas => KeyPrefix::GasBalanceHoldsByPurse(purse_addr),
397                BalanceHoldAddrTag::Processing => {
398                    KeyPrefix::ProcessingBalanceHoldsByPurse(purse_addr)
399                }
400            };
401            let immut: &_ = self;
402            let hold_keys = immut.keys_with_prefix(&prefix.to_bytes()?)?;
403            for hold_key in hold_keys {
404                let balance_hold_addr = hold_key
405                    .as_balance_hold()
406                    .ok_or(Self::Error::UnexpectedKeyVariant(hold_key))?;
407                let hold_block_time = balance_hold_addr.block_time();
408                if let Some(earliest_relevant_timestamp) = holds_epoch.value() {
409                    if hold_block_time.value() > earliest_relevant_timestamp {
410                        // skip still relevant holds
411                        //  the expectation is that holds are cleared after balance checks,
412                        //  and before payment settlement; if that ordering changes in the
413                        //  future this strategy should be reevaluated to determine if it
414                        //  remains correct.
415                        continue;
416                    }
417                }
418                // prune away holds with a timestamp newer than epoch timestamp
419                //  including holds with a timestamp == epoch timestamp
420                self.prune(hold_key)
421            }
422        }
423        Ok(())
424    }
425
426    fn get_balance_holds(
427        &mut self,
428        purse_addr: URefAddr,
429        block_time: BlockTime,
430        interval: u64,
431    ) -> Result<BTreeMap<BlockTime, BalanceHolds>, Self::Error> {
432        // NOTE: currently there are two kinds of holds, gas and processing.
433        // Processing holds only effect one block to prevent double spend and are always
434        // cleared at the end of processing each transaction. Gas holds persist for some
435        // interval, over many blocks and eras. Thus, using the holds_epoch for gas holds
436        // during transaction execution also picks up processing holds and call sites of
437        // this method currently pass the holds epoch for gas holds. This works fine for
438        // now, but if one or more other kinds of holds with differing periods are added
439        // in the future, this logic will need to be tweaked to take get the holds epoch
440        // for each hold kind and process each kind discretely in order and collate the
441        // non-expired hold total at the end.
442        let mut ret: BTreeMap<BlockTime, BalanceHolds> = BTreeMap::new();
443        let holds_epoch = { HoldsEpoch::from_millis(block_time.value(), interval) };
444        let holds = self.get_balance_hold_addresses(purse_addr)?;
445        for balance_hold_addr in holds {
446            let block_time = balance_hold_addr.block_time();
447            if let Some(timestamp) = holds_epoch.value() {
448                if block_time.value() < timestamp {
449                    // skip holds older than the interval
450                    //  don't skip holds with a timestamp >= epoch timestamp
451                    continue;
452                }
453            }
454            let hold_key: Key = balance_hold_addr.into();
455            let hold_amount = match self.read(&hold_key) {
456                Ok(Some(StoredValue::CLValue(cl_value))) => match cl_value.into_t::<U512>() {
457                    Ok(val) => val,
458                    Err(cve) => return Err(Self::Error::CLValue(cve)),
459                },
460                Ok(Some(_)) => return Err(Self::Error::UnexpectedStoredValueVariant),
461                Ok(None) => return Err(Self::Error::KeyNotFound(hold_key)),
462                Err(tce) => return Err(tce),
463            };
464            match ret.entry(block_time) {
465                Entry::Vacant(entry) => {
466                    let mut inner = BTreeMap::new();
467                    inner.insert(balance_hold_addr.tag(), hold_amount);
468                    entry.insert(inner);
469                }
470                Entry::Occupied(mut occupied_entry) => {
471                    let inner = occupied_entry.get_mut();
472                    match inner.entry(balance_hold_addr.tag()) {
473                        Entry::Vacant(entry) => {
474                            entry.insert(hold_amount);
475                        }
476                        Entry::Occupied(_) => {
477                            unreachable!(
478                                "there should be only one entry per (block_time, hold kind)"
479                            );
480                        }
481                    }
482                }
483            }
484        }
485        Ok(ret)
486    }
487
488    fn get_balance_holds_with_proof(
489        &self,
490        purse_addr: URefAddr,
491    ) -> Result<BTreeMap<BlockTime, BalanceHoldsWithProof>, Self::Error> {
492        // NOTE: currently there are two kinds of holds, gas and processing.
493        // Processing holds only effect one block to prevent double spend and are always
494        // cleared at the end of processing each transaction. Gas holds persist for some
495        // interval, over many blocks and eras. Thus, using the holds_epoch for gas holds
496        // during transaction execution also picks up processing holds and call sites of
497        // this method currently pass the holds epoch for gas holds. This works fine for
498        // now, but if one or more other kinds of holds with differing periods are added
499        // in the future, this logic will need to be tweaked to take get the holds epoch
500        // for each hold kind and process each kind discretely in order and collate the
501        // non-expired hold total at the end.
502        let mut ret: BTreeMap<BlockTime, BalanceHoldsWithProof> = BTreeMap::new();
503        let (block_time, interval) = match self.get_balance_hold_config(BalanceHoldAddrTag::Gas)? {
504            Some((block_time, _, interval)) => (block_time.value(), interval),
505            None => {
506                // if there is no holds config at this root hash, there can't be any holds
507                return Ok(ret);
508            }
509        };
510        let holds_epoch = { HoldsEpoch::from_millis(block_time, interval) };
511        let holds = self.get_balance_hold_addresses(purse_addr)?;
512        for balance_hold_addr in holds {
513            let block_time = balance_hold_addr.block_time();
514            if let Some(timestamp) = holds_epoch.value() {
515                if block_time.value() < timestamp {
516                    // skip holds older than the interval
517                    //  don't skip holds with a timestamp >= epoch timestamp
518                    continue;
519                }
520            }
521            let hold_key: Key = balance_hold_addr.into();
522            let proof: TrieMerkleProof<Key, StoredValue> = self
523                .read_with_proof(&hold_key.normalize())?
524                .ok_or(TrackingCopyError::KeyNotFound(hold_key))?;
525            let cl_value: CLValue = proof
526                .value()
527                .to_owned()
528                .try_into()
529                .map_err(TrackingCopyError::TypeMismatch)?;
530            let hold_amount = cl_value.into_t()?;
531            match ret.entry(block_time) {
532                Entry::Vacant(entry) => {
533                    let mut inner = BTreeMap::new();
534                    inner.insert(balance_hold_addr.tag(), (hold_amount, proof));
535                    entry.insert(inner);
536                }
537                Entry::Occupied(mut occupied_entry) => {
538                    let inner = occupied_entry.get_mut();
539                    match inner.entry(balance_hold_addr.tag()) {
540                        Entry::Vacant(entry) => {
541                            entry.insert((hold_amount, proof));
542                        }
543                        Entry::Occupied(_) => {
544                            unreachable!(
545                                "there should be only one entry per (block_time, hold kind)"
546                            );
547                        }
548                    }
549                }
550            }
551        }
552        Ok(ret)
553    }
554
555    fn get_message_topics(&self, hash_addr: EntityAddr) -> Result<MessageTopics, Self::Error> {
556        let keys = self.get_keys_by_prefix(&KeyPrefix::MessageEntriesByEntity(hash_addr))?;
557
558        let mut topics: BTreeMap<String, TopicNameHash> = BTreeMap::new();
559
560        for entry_key in &keys {
561            if let Some(topic_name_hash) = entry_key.as_message_topic_name_hash() {
562                match self.read(entry_key)?.as_ref() {
563                    Some(StoredValue::Message(_)) => {
564                        continue;
565                    }
566                    Some(StoredValue::MessageTopic(summary)) => {
567                        topics.insert(summary.topic_name().to_owned(), topic_name_hash);
568                    }
569                    Some(other) => {
570                        return Err(TrackingCopyError::TypeMismatch(
571                            StoredValueTypeMismatch::new(
572                                "MessageTopic".to_string(),
573                                other.type_name(),
574                            ),
575                        ));
576                    }
577                    None => match self.cache.reads_cached.get(entry_key) {
578                        Some(StoredValue::Message(_)) => {
579                            continue;
580                        }
581                        Some(StoredValue::MessageTopic(summary)) => {
582                            topics.insert(summary.topic_name().to_owned(), topic_name_hash);
583                        }
584                        Some(_) | None => {
585                            return Err(TrackingCopyError::KeyNotFound(*entry_key));
586                        }
587                    },
588                };
589            }
590        }
591
592        Ok(MessageTopics::from(topics))
593    }
594
595    fn get_named_keys(&self, entity_addr: EntityAddr) -> Result<NamedKeys, Self::Error> {
596        Ok(self
597            .runtime_footprint_by_entity_addr(entity_addr)?
598            .take_named_keys())
599    }
600
601    fn get_v1_entry_points(&self, entity_addr: EntityAddr) -> Result<EntryPoints, Self::Error> {
602        Ok(self
603            .runtime_footprint_by_entity_addr(entity_addr)?
604            .entry_points()
605            .clone())
606    }
607
608    fn get_package(&mut self, hash_addr: HashAddr) -> Result<Package, Self::Error> {
609        let key = Key::Hash(hash_addr);
610        match self.read(&key)? {
611            Some(StoredValue::ContractPackage(contract_package)) => Ok(contract_package.into()),
612            Some(_) | None => match self.read(&Key::SmartContract(hash_addr))? {
613                Some(StoredValue::SmartContract(package)) => Ok(package),
614                Some(other) => Err(TrackingCopyError::TypeMismatch(
615                    StoredValueTypeMismatch::new(
616                        "Package or CLValue".to_string(),
617                        other.type_name(),
618                    ),
619                )),
620                None => Err(Self::Error::ValueNotFound(key.to_formatted_string())),
621            },
622        }
623    }
624
625    fn get_contract(&mut self, contract_hash: ContractHash) -> Result<Contract, Self::Error> {
626        let key = Key::Hash(contract_hash.value());
627        match self.read(&key)? {
628            Some(StoredValue::Contract(contract)) => Ok(contract),
629            Some(other) => Err(Self::Error::TypeMismatch(StoredValueTypeMismatch::new(
630                "Contract".to_string(),
631                other.type_name(),
632            ))),
633            None => Err(Self::Error::ValueNotFound(key.to_formatted_string())),
634        }
635    }
636
637    fn get_system_entity_registry(&self) -> Result<SystemHashRegistry, Self::Error> {
638        match self.read(&Key::SystemEntityRegistry)? {
639            Some(StoredValue::CLValue(registry)) => {
640                let registry: SystemHashRegistry =
641                    CLValue::into_t(registry).map_err(Self::Error::from)?;
642                Ok(registry)
643            }
644            Some(other) => Err(TrackingCopyError::TypeMismatch(
645                StoredValueTypeMismatch::new("CLValue".to_string(), other.type_name()),
646            )),
647            None => Err(TrackingCopyError::KeyNotFound(Key::SystemEntityRegistry)),
648        }
649    }
650
651    fn get_checksum_registry(&mut self) -> Result<Option<ChecksumRegistry>, Self::Error> {
652        match self.get(&Key::ChecksumRegistry)? {
653            Some(StoredValue::CLValue(registry)) => {
654                let registry: ChecksumRegistry =
655                    CLValue::into_t(registry).map_err(Self::Error::from)?;
656                Ok(Some(registry))
657            }
658            Some(other) => Err(TrackingCopyError::TypeMismatch(
659                StoredValueTypeMismatch::new("CLValue".to_string(), other.type_name()),
660            )),
661            None => Ok(None),
662        }
663    }
664
665    fn get_byte_code(&mut self, byte_code_hash: ByteCodeHash) -> Result<ByteCode, Self::Error> {
666        let key = Key::ByteCode(ByteCodeAddr::V1CasperWasm(byte_code_hash.value()));
667        match self.get(&key)? {
668            Some(StoredValue::ByteCode(byte_code)) => Ok(byte_code),
669            Some(other) => Err(TrackingCopyError::TypeMismatch(
670                StoredValueTypeMismatch::new("ContractWasm".to_string(), other.type_name()),
671            )),
672            None => Err(TrackingCopyError::KeyNotFound(key)),
673        }
674    }
675}