use std::collections::BTreeSet;
use serde::Serialize;
use thiserror::Error;
use tracing::error;
use casper_types::{
account::AccountHash,
bytesrepr::FromBytes,
execution::Effects,
system::{
auction,
auction::{DelegationRate, DelegatorKind, Reservation},
},
CLTyped, CLValue, CLValueError, Chainspec, Digest, InitiatorAddr, ProtocolVersion, PublicKey,
RuntimeArgs, TransactionEntryPoint, TransactionHash, Transfer, URefAddr, U512,
};
use crate::{
system::runtime_native::Config as NativeRuntimeConfig, tracking_copy::TrackingCopyError,
};
#[derive(Clone, Eq, PartialEq, Error, Serialize, Debug)]
pub enum AuctionMethodError {
#[error("invalid entry point for auction: {0}")]
InvalidEntryPoint(TransactionEntryPoint),
#[error("missing '{0}' arg")]
MissingArg(String),
#[error("failed to parse '{arg}' arg: {error}")]
CLValue {
arg: String,
error: CLValueError,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuctionMethod {
ActivateBid {
validator: PublicKey,
},
AddBid {
public_key: PublicKey,
delegation_rate: DelegationRate,
amount: U512,
minimum_delegation_amount: u64,
maximum_delegation_amount: u64,
minimum_bid_amount: u64,
reserved_slots: u32,
},
WithdrawBid {
public_key: PublicKey,
amount: U512,
minimum_bid_amount: u64,
},
Delegate {
delegator: DelegatorKind,
validator: PublicKey,
amount: U512,
max_delegators_per_validator: u32,
},
Undelegate {
delegator: DelegatorKind,
validator: PublicKey,
amount: U512,
},
Redelegate {
delegator: DelegatorKind,
validator: PublicKey,
amount: U512,
new_validator: PublicKey,
},
ChangeBidPublicKey {
public_key: PublicKey,
new_public_key: PublicKey,
},
AddReservations {
reservations: Vec<Reservation>,
},
CancelReservations {
validator: PublicKey,
delegators: Vec<DelegatorKind>,
max_delegators_per_validator: u32,
},
}
impl AuctionMethod {
pub fn from_parts(
entry_point: TransactionEntryPoint,
runtime_args: &RuntimeArgs,
chainspec: &Chainspec,
) -> Result<Self, AuctionMethodError> {
match entry_point {
TransactionEntryPoint::Call
| TransactionEntryPoint::Custom(_)
| TransactionEntryPoint::Transfer
| TransactionEntryPoint::Burn => {
Err(AuctionMethodError::InvalidEntryPoint(entry_point))
}
TransactionEntryPoint::ActivateBid => Self::new_activate_bid(runtime_args),
TransactionEntryPoint::AddBid => Self::new_add_bid(
runtime_args,
chainspec.core_config.minimum_delegation_amount,
chainspec.core_config.maximum_delegation_amount,
chainspec.core_config.minimum_bid_amount,
),
TransactionEntryPoint::WithdrawBid => {
Self::new_withdraw_bid(runtime_args, chainspec.core_config.minimum_bid_amount)
}
TransactionEntryPoint::Delegate => Self::new_delegate(
runtime_args,
chainspec.core_config.max_delegators_per_validator,
),
TransactionEntryPoint::Undelegate => Self::new_undelegate(runtime_args),
TransactionEntryPoint::Redelegate => Self::new_redelegate(runtime_args),
TransactionEntryPoint::ChangeBidPublicKey => {
Self::new_change_bid_public_key(runtime_args)
}
TransactionEntryPoint::AddReservations => Self::new_add_reservations(runtime_args),
TransactionEntryPoint::CancelReservations => Self::new_cancel_reservations(
runtime_args,
chainspec.core_config.max_delegators_per_validator,
),
}
}
fn new_activate_bid(runtime_args: &RuntimeArgs) -> Result<Self, AuctionMethodError> {
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
Ok(Self::ActivateBid { validator })
}
fn new_add_bid(
runtime_args: &RuntimeArgs,
global_minimum_delegation: u64,
global_maximum_delegation: u64,
global_minimum_bid_amount: u64,
) -> Result<Self, AuctionMethodError> {
let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
let delegation_rate = Self::get_named_argument(runtime_args, auction::ARG_DELEGATION_RATE)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
let minimum_delegation_amount =
Self::get_named_argument(runtime_args, auction::ARG_MINIMUM_DELEGATION_AMOUNT)
.unwrap_or(global_minimum_delegation);
let maximum_delegation_amount =
Self::get_named_argument(runtime_args, auction::ARG_MAXIMUM_DELEGATION_AMOUNT)
.unwrap_or(global_maximum_delegation);
let reserved_slots =
Self::get_named_argument(runtime_args, auction::ARG_RESERVED_SLOTS).unwrap_or(0);
Ok(Self::AddBid {
public_key,
delegation_rate,
amount,
minimum_delegation_amount,
maximum_delegation_amount,
minimum_bid_amount: global_minimum_bid_amount,
reserved_slots,
})
}
fn new_withdraw_bid(
runtime_args: &RuntimeArgs,
global_minimum_bid_amount: u64,
) -> Result<Self, AuctionMethodError> {
let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
Ok(Self::WithdrawBid {
public_key,
amount,
minimum_bid_amount: global_minimum_bid_amount,
})
}
fn new_delegate(
runtime_args: &RuntimeArgs,
max_delegators_per_validator: u32,
) -> Result<Self, AuctionMethodError> {
let delegator = {
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let purse: URefAddr =
Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR_PURSE)?;
DelegatorKind::Purse(purse)
}
}
};
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
Ok(Self::Delegate {
delegator,
validator,
amount,
max_delegators_per_validator,
})
}
fn new_undelegate(runtime_args: &RuntimeArgs) -> Result<Self, AuctionMethodError> {
let delegator = {
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let purse: URefAddr =
Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR_PURSE)?;
DelegatorKind::Purse(purse)
}
}
};
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
Ok(Self::Undelegate {
delegator,
validator,
amount,
})
}
fn new_redelegate(runtime_args: &RuntimeArgs) -> Result<Self, AuctionMethodError> {
let delegator = {
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let purse: URefAddr =
Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR_PURSE)?;
DelegatorKind::Purse(purse)
}
}
};
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
let new_validator = Self::get_named_argument(runtime_args, auction::ARG_NEW_VALIDATOR)?;
Ok(Self::Redelegate {
delegator,
validator,
amount,
new_validator,
})
}
fn new_change_bid_public_key(runtime_args: &RuntimeArgs) -> Result<Self, AuctionMethodError> {
let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
let new_public_key = Self::get_named_argument(runtime_args, auction::ARG_NEW_PUBLIC_KEY)?;
Ok(Self::ChangeBidPublicKey {
public_key,
new_public_key,
})
}
fn new_add_reservations(runtime_args: &RuntimeArgs) -> Result<Self, AuctionMethodError> {
let reservations = Self::get_named_argument(runtime_args, auction::ARG_RESERVATIONS)?;
Ok(Self::AddReservations { reservations })
}
fn new_cancel_reservations(
runtime_args: &RuntimeArgs,
max_delegators_per_validator: u32,
) -> Result<Self, AuctionMethodError> {
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let delegators = Self::get_named_argument(runtime_args, auction::ARG_DELEGATORS)?;
Ok(Self::CancelReservations {
validator,
delegators,
max_delegators_per_validator,
})
}
fn get_named_argument<T: FromBytes + CLTyped>(
args: &RuntimeArgs,
name: &str,
) -> Result<T, AuctionMethodError> {
let arg: &CLValue = args
.get(name)
.ok_or_else(|| AuctionMethodError::MissingArg(name.to_string()))?;
arg.to_t().map_err(|error| AuctionMethodError::CLValue {
arg: name.to_string(),
error,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BiddingRequest {
pub(crate) config: NativeRuntimeConfig,
pub(crate) state_hash: Digest,
pub(crate) protocol_version: ProtocolVersion,
pub(crate) auction_method: AuctionMethod,
pub(crate) transaction_hash: TransactionHash,
pub(crate) initiator: InitiatorAddr,
pub(crate) authorization_keys: BTreeSet<AccountHash>,
}
impl BiddingRequest {
#[allow(clippy::too_many_arguments)]
pub fn new(
config: NativeRuntimeConfig,
state_hash: Digest,
protocol_version: ProtocolVersion,
transaction_hash: TransactionHash,
initiator: InitiatorAddr,
authorization_keys: BTreeSet<AccountHash>,
auction_method: AuctionMethod,
) -> Self {
Self {
config,
state_hash,
protocol_version,
transaction_hash,
initiator,
authorization_keys,
auction_method,
}
}
pub fn config(&self) -> &NativeRuntimeConfig {
&self.config
}
pub fn state_hash(&self) -> Digest {
self.state_hash
}
pub fn protocol_version(&self) -> ProtocolVersion {
self.protocol_version
}
pub fn auction_method(&self) -> &AuctionMethod {
&self.auction_method
}
pub fn transaction_hash(&self) -> TransactionHash {
self.transaction_hash
}
pub fn initiator(&self) -> &InitiatorAddr {
&self.initiator
}
pub fn authorization_keys(&self) -> &BTreeSet<AccountHash> {
&self.authorization_keys
}
}
#[derive(Debug, Clone)]
pub enum AuctionMethodRet {
Unit,
UpdatedAmount(U512),
}
#[derive(Debug)]
pub enum BiddingResult {
RootNotFound,
Success {
transfers: Vec<Transfer>,
effects: Effects,
ret: AuctionMethodRet,
},
Failure(TrackingCopyError),
}
impl BiddingResult {
pub fn is_success(&self) -> bool {
matches!(self, BiddingResult::Success { .. })
}
pub fn effects(&self) -> Effects {
match self {
BiddingResult::RootNotFound | BiddingResult::Failure(_) => Effects::new(),
BiddingResult::Success { effects, .. } => effects.clone(),
}
}
}