use crate::{
data_access_layer::{balance::BalanceFailure, BalanceIdentifier},
tracking_copy::TrackingCopyError,
};
use casper_types::{
account::AccountHash,
execution::Effects,
system::mint::{BalanceHoldAddr, BalanceHoldAddrTag},
Digest, ProtocolVersion, StoredValue, U512,
};
use std::fmt::{Display, Formatter};
use thiserror::Error;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum BalanceHoldKind {
#[default]
All,
Tag(BalanceHoldAddrTag),
}
impl BalanceHoldKind {
pub fn matches(&self, balance_hold_addr_tag: BalanceHoldAddrTag) -> bool {
match self {
BalanceHoldKind::All => true,
BalanceHoldKind::Tag(tag) => tag == &balance_hold_addr_tag,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BalanceHoldMode {
Hold {
identifier: BalanceIdentifier,
hold_amount: U512,
insufficient_handling: InsufficientBalanceHandling,
},
Clear {
identifier: BalanceIdentifier,
},
}
impl Default for BalanceHoldMode {
fn default() -> Self {
BalanceHoldMode::Hold {
insufficient_handling: InsufficientBalanceHandling::HoldRemaining,
hold_amount: U512::zero(),
identifier: BalanceIdentifier::Account(AccountHash::default()),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum InsufficientBalanceHandling {
#[default]
HoldRemaining,
Noop,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct BalanceHoldRequest {
state_hash: Digest,
protocol_version: ProtocolVersion,
hold_kind: BalanceHoldKind,
hold_mode: BalanceHoldMode,
}
impl BalanceHoldRequest {
#[allow(clippy::too_many_arguments)]
pub fn new_gas_hold(
state_hash: Digest,
protocol_version: ProtocolVersion,
identifier: BalanceIdentifier,
hold_amount: U512,
insufficient_handling: InsufficientBalanceHandling,
) -> Self {
let hold_kind = BalanceHoldKind::Tag(BalanceHoldAddrTag::Gas);
let hold_mode = BalanceHoldMode::Hold {
identifier,
hold_amount,
insufficient_handling,
};
BalanceHoldRequest {
state_hash,
protocol_version,
hold_kind,
hold_mode,
}
}
#[allow(clippy::too_many_arguments)]
pub fn new_processing_hold(
state_hash: Digest,
protocol_version: ProtocolVersion,
identifier: BalanceIdentifier,
hold_amount: U512,
insufficient_handling: InsufficientBalanceHandling,
) -> Self {
let hold_kind = BalanceHoldKind::Tag(BalanceHoldAddrTag::Processing);
let hold_mode = BalanceHoldMode::Hold {
identifier,
hold_amount,
insufficient_handling,
};
BalanceHoldRequest {
state_hash,
protocol_version,
hold_kind,
hold_mode,
}
}
pub fn new_clear(
state_hash: Digest,
protocol_version: ProtocolVersion,
hold_kind: BalanceHoldKind,
identifier: BalanceIdentifier,
) -> Self {
let hold_mode = BalanceHoldMode::Clear { identifier };
BalanceHoldRequest {
state_hash,
protocol_version,
hold_kind,
hold_mode,
}
}
pub fn state_hash(&self) -> Digest {
self.state_hash
}
pub fn protocol_version(&self) -> ProtocolVersion {
self.protocol_version
}
pub fn balance_hold_kind(&self) -> BalanceHoldKind {
self.hold_kind
}
pub fn balance_hold_mode(&self) -> BalanceHoldMode {
self.hold_mode.clone()
}
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum BalanceHoldError {
TrackingCopy(TrackingCopyError),
Balance(BalanceFailure),
InsufficientBalance {
remaining_balance: U512,
},
UnexpectedWildcardVariant, UnexpectedHoldValue(StoredValue),
}
impl From<BalanceFailure> for BalanceHoldError {
fn from(be: BalanceFailure) -> Self {
BalanceHoldError::Balance(be)
}
}
impl From<TrackingCopyError> for BalanceHoldError {
fn from(tce: TrackingCopyError) -> Self {
BalanceHoldError::TrackingCopy(tce)
}
}
impl Display for BalanceHoldError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BalanceHoldError::TrackingCopy(err) => {
write!(f, "TrackingCopy: {:?}", err)
}
BalanceHoldError::InsufficientBalance { remaining_balance } => {
write!(f, "InsufficientBalance: {}", remaining_balance)
}
BalanceHoldError::UnexpectedWildcardVariant => {
write!(
f,
"UnexpectedWildcardVariant: unsupported use of BalanceHoldKind::All"
)
}
BalanceHoldError::Balance(be) => Display::fmt(be, f),
BalanceHoldError::UnexpectedHoldValue(value) => {
write!(f, "Found an unexpected hold value in storage: {:?}", value,)
}
}
}
}
#[derive(Debug)]
pub enum BalanceHoldResult {
RootNotFound,
BlockTimeNotFound,
Success {
holds: Option<Vec<BalanceHoldAddr>>,
total_balance: Box<U512>,
available_balance: Box<U512>,
hold: Box<U512>,
held: Box<U512>,
effects: Box<Effects>,
},
Failure(BalanceHoldError),
}
impl BalanceHoldResult {
pub fn success(
holds: Option<Vec<BalanceHoldAddr>>,
total_balance: U512,
available_balance: U512,
hold: U512,
held: U512,
effects: Effects,
) -> Self {
BalanceHoldResult::Success {
holds,
total_balance: Box::new(total_balance),
available_balance: Box::new(available_balance),
hold: Box::new(hold),
held: Box::new(held),
effects: Box::new(effects),
}
}
pub fn total_balance(&self) -> Option<&U512> {
match self {
BalanceHoldResult::Success { total_balance, .. } => Some(total_balance),
_ => None,
}
}
pub fn available_balance(&self) -> Option<&U512> {
match self {
BalanceHoldResult::Success {
available_balance, ..
} => Some(available_balance),
_ => None,
}
}
pub fn held(&self) -> Option<&U512> {
match self {
BalanceHoldResult::Success { held, .. } => Some(held),
_ => None,
}
}
pub fn holds(&self) -> Option<Vec<BalanceHoldAddr>> {
match self {
BalanceHoldResult::RootNotFound
| BalanceHoldResult::BlockTimeNotFound
| BalanceHoldResult::Failure(_) => None,
BalanceHoldResult::Success { holds, .. } => holds.clone(),
}
}
pub fn has_holds(&self) -> bool {
match self.holds() {
None => false,
Some(holds) => !holds.is_empty(),
}
}
pub fn is_fully_covered(&self) -> bool {
match self {
BalanceHoldResult::RootNotFound
| BalanceHoldResult::BlockTimeNotFound
| BalanceHoldResult::Failure(_) => false,
BalanceHoldResult::Success { hold, held, .. } => hold == held,
}
}
pub fn is_success(&self) -> bool {
matches!(self, BalanceHoldResult::Success { .. })
}
pub fn is_root_not_found(&self) -> bool {
matches!(self, BalanceHoldResult::RootNotFound)
}
pub fn effects(&self) -> Effects {
match self {
BalanceHoldResult::RootNotFound
| BalanceHoldResult::BlockTimeNotFound
| BalanceHoldResult::Failure(_) => Effects::new(),
BalanceHoldResult::Success { effects, .. } => *effects.clone(),
}
}
pub fn error_message(&self) -> String {
match self {
BalanceHoldResult::Success { hold, held, .. } => {
if hold == held {
String::default()
} else {
format!(
"insufficient balance to cover hold amount: {}, held remaining amount: {}",
hold, held
)
}
}
BalanceHoldResult::RootNotFound => "root not found".to_string(),
BalanceHoldResult::BlockTimeNotFound => "block time not found".to_string(),
BalanceHoldResult::Failure(bhe) => {
format!("{:?}", bhe)
}
}
}
}
impl From<BalanceFailure> for BalanceHoldResult {
fn from(be: BalanceFailure) -> Self {
BalanceHoldResult::Failure(be.into())
}
}
impl From<TrackingCopyError> for BalanceHoldResult {
fn from(tce: TrackingCopyError) -> Self {
BalanceHoldResult::Failure(tce.into())
}
}