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
34pub trait TrackingCopyExt<R> {
36 type Error;
38
39 fn read_account_key(&mut self, account_hash: AccountHash) -> Result<Key, Self::Error>;
41
42 fn get_block_time(&self) -> Result<Option<BlockTime>, Self::Error>;
44
45 fn get_balance_hold_config(
47 &self,
48 hold_kind: BalanceHoldAddrTag,
49 ) -> Result<Option<(BlockTime, HoldBalanceHandling, u64)>, Self::Error>;
50
51 fn get_purse_balance_key(&self, purse_key: Key) -> Result<Key, Self::Error>;
53
54 fn get_balance_hold_addresses(
56 &self,
57 purse_addr: URefAddr,
58 ) -> Result<Vec<BalanceHoldAddr>, Self::Error>;
59
60 fn get_total_balance(&self, key: Key) -> Result<Motes, Self::Error>;
62
63 fn get_available_balance(&mut self, balance_key: Key) -> Result<Motes, Self::Error>;
65
66 fn get_purse_balance_key_with_proof(
68 &self,
69 purse_key: Key,
70 ) -> Result<(Key, TrieMerkleProof<Key, StoredValue>), Self::Error>;
71
72 fn get_total_balance_with_proof(
74 &self,
75 balance_key: Key,
76 ) -> Result<(U512, TrieMerkleProof<Key, StoredValue>), Self::Error>;
77
78 fn clear_expired_balance_holds(
80 &mut self,
81 purse_addr: URefAddr,
82 filter: Vec<(BalanceHoldAddrTag, HoldsEpoch)>,
83 ) -> Result<(), Self::Error>;
84
85 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 fn get_balance_holds_with_proof(
95 &self,
96 purse_addr: URefAddr,
97 ) -> Result<BTreeMap<BlockTime, BalanceHoldsWithProof>, Self::Error>;
98
99 fn get_message_topics(&self, entity_addr: EntityAddr) -> Result<MessageTopics, Self::Error>;
101
102 fn get_named_keys(&self, entity_addr: EntityAddr) -> Result<NamedKeys, Self::Error>;
104
105 fn get_v1_entry_points(&self, entity_addr: EntityAddr) -> Result<EntryPoints, Self::Error>;
107
108 fn get_package(&mut self, package_hash: HashAddr) -> Result<Package, Self::Error>;
110
111 fn get_contract(&mut self, contract_hash: ContractHash) -> Result<Contract, Self::Error>;
113
114 fn get_system_entity_registry(&self) -> Result<SystemHashRegistry, Self::Error>;
116
117 fn get_checksum_registry(&mut self) -> Result<Option<ChecksumRegistry>, Self::Error>;
119
120 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 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 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 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 continue;
416 }
417 }
418 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 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 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 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 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 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}