casper_storage/data_access_layer/
balance_hold.rs

1use crate::{
2    data_access_layer::{balance::BalanceFailure, BalanceIdentifier},
3    tracking_copy::TrackingCopyError,
4};
5use casper_types::{
6    account::AccountHash,
7    execution::Effects,
8    system::mint::{BalanceHoldAddr, BalanceHoldAddrTag},
9    Digest, ProtocolVersion, StoredValue, U512,
10};
11use std::fmt::{Display, Formatter};
12use thiserror::Error;
13
14/// Balance hold kind.
15#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
16pub enum BalanceHoldKind {
17    /// All balance holds.
18    #[default]
19    All,
20    /// Selection of a specific kind of balance.
21    Tag(BalanceHoldAddrTag),
22}
23
24impl BalanceHoldKind {
25    /// Returns true of imputed tag applies to instance.
26    pub fn matches(&self, balance_hold_addr_tag: BalanceHoldAddrTag) -> bool {
27        match self {
28            BalanceHoldKind::All => true,
29            BalanceHoldKind::Tag(tag) => tag == &balance_hold_addr_tag,
30        }
31    }
32}
33
34/// Balance hold mode.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum BalanceHoldMode {
37    /// Balance hold request.
38    Hold {
39        /// Balance identifier.
40        identifier: BalanceIdentifier,
41        /// Hold amount.
42        hold_amount: U512,
43        /// How should insufficient balance be handled.
44        insufficient_handling: InsufficientBalanceHandling,
45    },
46    /// Clear balance holds.
47    Clear {
48        /// Identifier of balance to be cleared of holds.
49        identifier: BalanceIdentifier,
50    },
51}
52
53impl Default for BalanceHoldMode {
54    fn default() -> Self {
55        BalanceHoldMode::Hold {
56            insufficient_handling: InsufficientBalanceHandling::HoldRemaining,
57            hold_amount: U512::zero(),
58            identifier: BalanceIdentifier::Account(AccountHash::default()),
59        }
60    }
61}
62
63/// How to handle available balance is less than hold amount?
64#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
65pub enum InsufficientBalanceHandling {
66    /// Hold however much balance remains.
67    #[default]
68    HoldRemaining,
69    /// No operation. Aka, do not place a hold.
70    Noop,
71}
72
73/// Balance hold request.
74#[derive(Debug, Clone, PartialEq, Eq, Default)]
75pub struct BalanceHoldRequest {
76    state_hash: Digest,
77    protocol_version: ProtocolVersion,
78    hold_kind: BalanceHoldKind,
79    hold_mode: BalanceHoldMode,
80}
81
82impl BalanceHoldRequest {
83    /// Creates a new [`BalanceHoldRequest`] for adding a gas balance hold.
84    #[allow(clippy::too_many_arguments)]
85    pub fn new_gas_hold(
86        state_hash: Digest,
87        protocol_version: ProtocolVersion,
88        identifier: BalanceIdentifier,
89        hold_amount: U512,
90        insufficient_handling: InsufficientBalanceHandling,
91    ) -> Self {
92        let hold_kind = BalanceHoldKind::Tag(BalanceHoldAddrTag::Gas);
93        let hold_mode = BalanceHoldMode::Hold {
94            identifier,
95            hold_amount,
96            insufficient_handling,
97        };
98        BalanceHoldRequest {
99            state_hash,
100            protocol_version,
101            hold_kind,
102            hold_mode,
103        }
104    }
105
106    /// Creates a new [`BalanceHoldRequest`] for adding a processing balance hold.
107    #[allow(clippy::too_many_arguments)]
108    pub fn new_processing_hold(
109        state_hash: Digest,
110        protocol_version: ProtocolVersion,
111        identifier: BalanceIdentifier,
112        hold_amount: U512,
113        insufficient_handling: InsufficientBalanceHandling,
114    ) -> Self {
115        let hold_kind = BalanceHoldKind::Tag(BalanceHoldAddrTag::Processing);
116        let hold_mode = BalanceHoldMode::Hold {
117            identifier,
118            hold_amount,
119            insufficient_handling,
120        };
121        BalanceHoldRequest {
122            state_hash,
123            protocol_version,
124            hold_kind,
125            hold_mode,
126        }
127    }
128
129    /// Creates a new [`BalanceHoldRequest`] for clearing holds.
130    pub fn new_clear(
131        state_hash: Digest,
132        protocol_version: ProtocolVersion,
133        hold_kind: BalanceHoldKind,
134        identifier: BalanceIdentifier,
135    ) -> Self {
136        let hold_mode = BalanceHoldMode::Clear { identifier };
137        BalanceHoldRequest {
138            state_hash,
139            protocol_version,
140            hold_kind,
141            hold_mode,
142        }
143    }
144
145    /// Returns a state hash.
146    pub fn state_hash(&self) -> Digest {
147        self.state_hash
148    }
149
150    /// Protocol version.
151    pub fn protocol_version(&self) -> ProtocolVersion {
152        self.protocol_version
153    }
154
155    /// Balance hold kind.
156    pub fn balance_hold_kind(&self) -> BalanceHoldKind {
157        self.hold_kind
158    }
159
160    /// Balance hold mode.
161    pub fn balance_hold_mode(&self) -> BalanceHoldMode {
162        self.hold_mode.clone()
163    }
164}
165
166/// Possible balance hold errors.
167#[derive(Error, Debug, Clone)]
168#[non_exhaustive]
169pub enum BalanceHoldError {
170    /// Tracking copy error.
171    TrackingCopy(TrackingCopyError),
172    /// Balance error.
173    Balance(BalanceFailure),
174    /// Insufficient balance error.
175    InsufficientBalance {
176        /// Remaining balance error.
177        remaining_balance: U512,
178    },
179    /// Unexpected wildcard variant error.
180    UnexpectedWildcardVariant, // programmer error,
181    /// Unexpected hold value error.
182    UnexpectedHoldValue(StoredValue),
183}
184
185impl From<BalanceFailure> for BalanceHoldError {
186    fn from(be: BalanceFailure) -> Self {
187        BalanceHoldError::Balance(be)
188    }
189}
190
191impl From<TrackingCopyError> for BalanceHoldError {
192    fn from(tce: TrackingCopyError) -> Self {
193        BalanceHoldError::TrackingCopy(tce)
194    }
195}
196
197impl Display for BalanceHoldError {
198    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
199        match self {
200            BalanceHoldError::TrackingCopy(err) => {
201                write!(f, "TrackingCopy: {:?}", err)
202            }
203            BalanceHoldError::InsufficientBalance { remaining_balance } => {
204                write!(f, "InsufficientBalance: {}", remaining_balance)
205            }
206            BalanceHoldError::UnexpectedWildcardVariant => {
207                write!(
208                    f,
209                    "UnexpectedWildcardVariant: unsupported use of BalanceHoldKind::All"
210                )
211            }
212            BalanceHoldError::Balance(be) => Display::fmt(be, f),
213            BalanceHoldError::UnexpectedHoldValue(value) => {
214                write!(f, "Found an unexpected hold value in storage: {:?}", value,)
215            }
216        }
217    }
218}
219
220/// Result enum that represents all possible outcomes of a balance hold request.
221#[derive(Debug)]
222pub enum BalanceHoldResult {
223    /// Returned if a passed state root hash is not found.
224    RootNotFound,
225    /// Returned if global state does not have an entry for block time.
226    BlockTimeNotFound,
227    /// Balance hold successfully placed.
228    Success {
229        /// Hold addresses, if any.
230        holds: Option<Vec<BalanceHoldAddr>>,
231        /// Purse total balance.
232        total_balance: Box<U512>,
233        /// Purse available balance after hold placed.
234        available_balance: Box<U512>,
235        /// How much were we supposed to hold?
236        hold: Box<U512>,
237        /// How much did we actually hold?
238        held: Box<U512>,
239        /// Effects of bidding interaction.
240        effects: Box<Effects>,
241    },
242    /// Failed to place balance hold.
243    Failure(BalanceHoldError),
244}
245
246impl BalanceHoldResult {
247    /// Success ctor.
248    pub fn success(
249        holds: Option<Vec<BalanceHoldAddr>>,
250        total_balance: U512,
251        available_balance: U512,
252        hold: U512,
253        held: U512,
254        effects: Effects,
255    ) -> Self {
256        BalanceHoldResult::Success {
257            holds,
258            total_balance: Box::new(total_balance),
259            available_balance: Box::new(available_balance),
260            hold: Box::new(hold),
261            held: Box::new(held),
262            effects: Box::new(effects),
263        }
264    }
265
266    /// Returns the total balance for a [`BalanceHoldResult::Success`] variant.
267    pub fn total_balance(&self) -> Option<&U512> {
268        match self {
269            BalanceHoldResult::Success { total_balance, .. } => Some(total_balance),
270            _ => None,
271        }
272    }
273
274    /// Returns the available balance for a [`BalanceHoldResult::Success`] variant.
275    pub fn available_balance(&self) -> Option<&U512> {
276        match self {
277            BalanceHoldResult::Success {
278                available_balance, ..
279            } => Some(available_balance),
280            _ => None,
281        }
282    }
283
284    /// Returns the held amount for a [`BalanceHoldResult::Success`] variant.
285    pub fn held(&self) -> Option<&U512> {
286        match self {
287            BalanceHoldResult::Success { held, .. } => Some(held),
288            _ => None,
289        }
290    }
291
292    /// Hold address, if any.
293    pub fn holds(&self) -> Option<Vec<BalanceHoldAddr>> {
294        match self {
295            BalanceHoldResult::RootNotFound
296            | BalanceHoldResult::BlockTimeNotFound
297            | BalanceHoldResult::Failure(_) => None,
298            BalanceHoldResult::Success { holds, .. } => holds.clone(),
299        }
300    }
301
302    /// Does this result contain any hold addresses?
303    pub fn has_holds(&self) -> bool {
304        match self.holds() {
305            None => false,
306            Some(holds) => !holds.is_empty(),
307        }
308    }
309
310    /// Was the hold fully covered?
311    pub fn is_fully_covered(&self) -> bool {
312        match self {
313            BalanceHoldResult::RootNotFound
314            | BalanceHoldResult::BlockTimeNotFound
315            | BalanceHoldResult::Failure(_) => false,
316            BalanceHoldResult::Success { hold, held, .. } => hold == held,
317        }
318    }
319
320    /// Was the hold successful?
321    pub fn is_success(&self) -> bool {
322        matches!(self, BalanceHoldResult::Success { .. })
323    }
324
325    /// Was the root not found?
326    pub fn is_root_not_found(&self) -> bool {
327        matches!(self, BalanceHoldResult::RootNotFound)
328    }
329
330    /// The effects, if any.
331    pub fn effects(&self) -> Effects {
332        match self {
333            BalanceHoldResult::RootNotFound
334            | BalanceHoldResult::BlockTimeNotFound
335            | BalanceHoldResult::Failure(_) => Effects::new(),
336            BalanceHoldResult::Success { effects, .. } => *effects.clone(),
337        }
338    }
339
340    /// Error message.
341    pub fn error_message(&self) -> String {
342        match self {
343            BalanceHoldResult::Success { hold, held, .. } => {
344                if hold == held {
345                    String::default()
346                } else {
347                    format!(
348                        "insufficient balance to cover hold amount: {}, held remaining amount: {}",
349                        hold, held
350                    )
351                }
352            }
353            BalanceHoldResult::RootNotFound => "root not found".to_string(),
354            BalanceHoldResult::BlockTimeNotFound => "block time not found".to_string(),
355            BalanceHoldResult::Failure(bhe) => {
356                format!("{:?}", bhe)
357            }
358        }
359    }
360}
361
362impl From<BalanceFailure> for BalanceHoldResult {
363    fn from(be: BalanceFailure) -> Self {
364        BalanceHoldResult::Failure(be.into())
365    }
366}
367
368impl From<TrackingCopyError> for BalanceHoldResult {
369    fn from(tce: TrackingCopyError) -> Self {
370        BalanceHoldResult::Failure(tce.into())
371    }
372}