#[cfg(feature = "serde")]
use serde_with::{hex::Hex, serde_as, DisplayFromStr};
use alloc::string::String;
use alloc::vec::Vec;
use bytecheck::CheckBytes;
use dusk_bytes::Serializable;
use piecrust_uplink::CONTRACT_ID_BYTES;
use rkyv::{Archive, Deserialize, Serialize};
use crate::abi::ContractId;
use crate::signatures::bls::{
PublicKey as BlsPublicKey, SecretKey as BlsSecretKey,
Signature as BlsSignature,
};
use crate::transfer::withdraw::Withdraw as TransferWithdraw;
use crate::{dusk, Dusk};
pub const STAKE_CONTRACT: ContractId = crate::reserved(0x2);
pub const EPOCH: u64 = 2160;
pub const DEFAULT_STAKE_WARNINGS: u8 = 1;
#[deprecated(
since = "0.1.0",
note = "please use `DEFAULT_MINIMUM_STAKE` instead"
)]
pub const MINIMUM_STAKE: Dusk = DEFAULT_MINIMUM_STAKE;
pub const DEFAULT_MINIMUM_STAKE: Dusk = dusk(1_000.0);
#[derive(Debug, Clone, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StakeConfig {
pub warnings: u8,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub minimum_stake: Dusk,
}
impl StakeConfig {
#[must_use]
pub const fn new() -> Self {
Self {
warnings: DEFAULT_STAKE_WARNINGS,
minimum_stake: DEFAULT_MINIMUM_STAKE,
}
}
}
impl Default for StakeConfig {
fn default() -> Self {
Self::new()
}
}
#[must_use]
pub const fn next_epoch(block_height: u64) -> u64 {
let to_next_epoch = EPOCH - (block_height % EPOCH);
block_height + to_next_epoch
}
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Stake {
chain_id: u8,
keys: StakeKeys,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
value: u64,
signature: DoubleSignature,
}
impl Stake {
const MESSAGE_SIZE: usize =
1 + BlsPublicKey::SIZE + BlsPublicKey::SIZE + u64::SIZE;
#[must_use]
pub fn new(
account_sk: &BlsSecretKey,
owner_sk: &BlsSecretKey,
value: u64,
chain_id: u8,
) -> Self {
let account = BlsPublicKey::from(account_sk);
let owner = BlsPublicKey::from(owner_sk);
let keys = StakeKeys::new(account, owner);
let mut stake = Stake {
chain_id,
keys,
value,
signature: DoubleSignature::default(),
};
let msg = stake.signature_message();
stake.signature = DoubleSignature {
account: account_sk.sign(&msg),
owner: owner_sk.sign(&msg),
};
stake
}
#[must_use]
pub fn new_from_contract(
sk: &BlsSecretKey,
contract: ContractId,
value: u64,
chain_id: u8,
) -> Self {
let key = BlsPublicKey::from(sk);
let keys = StakeKeys::new(key, contract);
let mut stake = Stake {
chain_id,
keys,
value,
signature: DoubleSignature::default(),
};
let msg = stake.signature_message();
stake.signature = DoubleSignature {
account: sk.sign(&msg),
owner: sk.sign(&msg),
};
stake
}
#[must_use]
pub fn keys(&self) -> &StakeKeys {
&self.keys
}
#[must_use]
pub fn account(&self) -> &BlsPublicKey {
&self.keys.account
}
#[must_use]
pub fn value(&self) -> u64 {
self.value
}
#[must_use]
pub fn chain_id(&self) -> u8 {
self.chain_id
}
#[must_use]
pub fn signature(&self) -> &DoubleSignature {
&self.signature
}
#[must_use]
pub fn signature_message(&self) -> [u8; Self::MESSAGE_SIZE] {
let mut bytes = [0u8; Self::MESSAGE_SIZE];
bytes[0] = self.chain_id;
let mut offset = 1;
bytes[offset..offset + BlsPublicKey::SIZE]
.copy_from_slice(&self.keys.account.to_bytes());
offset += BlsPublicKey::SIZE;
match &self.keys.owner {
StakeFundOwner::Account(key) => bytes
[offset..offset + BlsPublicKey::SIZE]
.copy_from_slice(&key.to_bytes()),
StakeFundOwner::Contract(contract_id) => bytes
[offset..offset + CONTRACT_ID_BYTES]
.copy_from_slice(&contract_id.to_bytes()),
}
offset += BlsPublicKey::SIZE;
bytes[offset..offset + u64::SIZE]
.copy_from_slice(&self.value.to_bytes());
bytes
}
}
#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WithdrawToContract {
account: BlsPublicKey,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
value: u64,
fn_name: String,
#[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
data: Vec<u8>,
}
impl WithdrawToContract {
pub fn new(
account: BlsPublicKey,
value: u64,
fn_name: impl Into<String>,
) -> Self {
Self {
account,
value,
fn_name: fn_name.into(),
data: Vec::new(),
}
}
#[must_use]
pub fn account(&self) -> &BlsPublicKey {
&self.account
}
#[must_use]
pub fn value(&self) -> u64 {
self.value
}
#[must_use]
pub fn fn_name(&self) -> &str {
&self.fn_name
}
#[must_use]
pub fn data(&self) -> &[u8] {
&self.data
}
#[must_use]
pub fn with_data(mut self, data: Vec<u8>) -> Self {
self.data = data;
self
}
}
#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Withdraw {
account: BlsPublicKey,
withdraw: TransferWithdraw,
signature: DoubleSignature,
}
impl Withdraw {
#[must_use]
pub fn new(
account_sk: &BlsSecretKey,
owner_sk: &BlsSecretKey,
withdraw: TransferWithdraw,
) -> Self {
let account = BlsPublicKey::from(account_sk);
let mut stake_withdraw = Withdraw {
account,
withdraw,
signature: DoubleSignature::default(),
};
let msg = stake_withdraw.signature_message();
stake_withdraw.signature = DoubleSignature {
account: account_sk.sign(&msg),
owner: owner_sk.sign(&msg),
};
stake_withdraw
}
#[must_use]
pub fn with_single_key(
sk: &BlsSecretKey,
withdraw: TransferWithdraw,
) -> Self {
Self::new(sk, sk, withdraw)
}
#[must_use]
pub fn account(&self) -> &BlsPublicKey {
&self.account
}
#[must_use]
pub fn transfer_withdraw(&self) -> &TransferWithdraw {
&self.withdraw
}
#[must_use]
pub fn signature(&self) -> &DoubleSignature {
&self.signature
}
#[must_use]
pub fn signature_message(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(self.account.to_bytes());
bytes.extend(self.withdraw.wrapped_signature_message());
bytes
}
}
#[derive(Debug, Clone, Archive, Deserialize, Serialize, PartialEq)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StakeEvent {
pub keys: StakeKeys,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub value: u64,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub locked: u64,
}
impl StakeEvent {
#[must_use]
pub fn new(keys: StakeKeys, value: u64) -> Self {
Self {
keys,
value,
locked: 0,
}
}
#[must_use]
pub fn locked(mut self, locked: u64) -> Self {
self.locked = locked;
self
}
}
#[derive(Debug, Clone, Archive, Deserialize, Serialize, PartialEq)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SlashEvent {
pub account: BlsPublicKey,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub value: u64,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub next_eligibility: u64,
}
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StakeData {
pub amount: Option<StakeAmount>,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub reward: u64,
pub faults: u8,
pub hard_faults: u8,
}
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[archive_attr(derive(CheckBytes))]
pub struct StakeKeys {
pub account: BlsPublicKey,
pub owner: StakeFundOwner,
}
impl StakeKeys {
#[must_use]
pub fn single_key(account: BlsPublicKey) -> Self {
Self::new(account, account)
}
#[must_use]
pub fn new<F: Into<StakeFundOwner>>(
account: BlsPublicKey,
owner: F,
) -> Self {
let owner = owner.into();
Self { account, owner }
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[archive_attr(derive(CheckBytes))]
pub enum StakeFundOwner {
Account(BlsPublicKey),
Contract(ContractId),
}
impl Default for StakeFundOwner {
fn default() -> Self {
BlsPublicKey::default().into()
}
}
impl From<BlsPublicKey> for StakeFundOwner {
fn from(value: BlsPublicKey) -> Self {
Self::Account(value)
}
}
impl From<ContractId> for StakeFundOwner {
fn from(value: ContractId) -> Self {
Self::Contract(value)
}
}
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DoubleSignature {
pub account: BlsSignature,
pub owner: BlsSignature,
}
impl StakeData {
pub const EMPTY: Self = Self {
amount: None,
reward: 0,
faults: 0,
hard_faults: 0,
};
#[must_use]
pub const fn new(value: u64, reward: u64, block_height: u64) -> Self {
let eligibility = Self::eligibility_from_height(block_height);
Self::with_eligibility(value, reward, eligibility)
}
#[must_use]
pub const fn with_eligibility(
value: u64,
reward: u64,
eligibility: u64,
) -> Self {
let amount = match value {
0 => None,
_ => Some(StakeAmount {
value,
eligibility,
locked: 0,
}),
};
Self {
amount,
reward,
faults: 0,
hard_faults: 0,
}
}
#[must_use]
pub fn is_valid(&self, block_height: u64) -> bool {
match &self.amount {
Some(amount) => block_height >= amount.eligibility,
None => false,
}
}
#[must_use]
pub const fn eligibility_from_height(block_height: u64) -> u64 {
StakeAmount::eligibility_from_height(block_height)
}
pub fn is_empty(&self) -> bool {
let stake = self
.amount
.as_ref()
.map(StakeAmount::total_funds)
.unwrap_or_default();
self.reward + stake == 0
}
}
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StakeAmount {
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub value: u64,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub locked: u64,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub eligibility: u64,
}
impl StakeAmount {
#[must_use]
pub const fn new(value: u64, block_height: u64) -> Self {
let eligibility = Self::eligibility_from_height(block_height);
Self::with_eligibility(value, eligibility)
}
#[must_use]
pub const fn with_eligibility(value: u64, eligibility: u64) -> Self {
Self {
value,
eligibility,
locked: 0,
}
}
#[must_use]
pub const fn eligibility_from_height(block_height: u64) -> u64 {
let maturity_blocks = EPOCH;
next_epoch(block_height) + maturity_blocks
}
pub fn lock_amount(&mut self, amount: u64) {
self.value -= amount;
self.locked += amount;
}
#[must_use]
pub fn total_funds(&self) -> u64 {
self.value + self.locked
}
}
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Reward {
pub account: BlsPublicKey,
#[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
pub value: u64,
pub reason: RewardReason,
}
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[archive_attr(derive(CheckBytes))]
pub enum RewardReason {
GeneratorFixed,
GeneratorExtra,
Voter,
Other,
}