1use alloc::string::String;
10use alloc::vec::Vec;
11
12use bytecheck::CheckBytes;
13use dusk_bytes::Serializable;
14use piecrust_uplink::CONTRACT_ID_BYTES;
15use rkyv::{Archive, Deserialize, Serialize};
16#[cfg(feature = "serde")]
17use serde_with::hex::Hex;
18#[cfg(feature = "serde")]
19use serde_with::{As, DisplayFromStr};
20
21use crate::abi::ContractId;
22use crate::signatures::bls::{
23 PublicKey as BlsPublicKey, SecretKey as BlsSecretKey,
24 Signature as BlsSignature,
25};
26use crate::transfer::withdraw::Withdraw as TransferWithdraw;
27use crate::{Dusk, dusk};
28
29pub const STAKE_CONTRACT: ContractId = crate::reserved(0x2);
31
32pub const EPOCH: u64 = 2160;
34
35pub const DEFAULT_STAKE_WARNINGS: u8 = 1;
37
38#[deprecated(
40 since = "0.1.0",
41 note = "please use `DEFAULT_MINIMUM_STAKE` instead"
42)]
43pub const MINIMUM_STAKE: Dusk = DEFAULT_MINIMUM_STAKE;
44
45pub const DEFAULT_MINIMUM_STAKE: Dusk = dusk(1_000.0);
47
48#[derive(Debug, Clone, Archive, Serialize, Deserialize)]
50#[archive_attr(derive(CheckBytes))]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct StakeConfig {
53 pub warnings: u8,
55 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
57 pub minimum_stake: Dusk,
58}
59
60impl StakeConfig {
61 #[must_use]
63 pub const fn new() -> Self {
64 Self {
65 warnings: DEFAULT_STAKE_WARNINGS,
66 minimum_stake: DEFAULT_MINIMUM_STAKE,
67 }
68 }
69}
70
71impl Default for StakeConfig {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77#[must_use]
79pub const fn next_epoch(block_height: u64) -> u64 {
80 let to_next_epoch = EPOCH - (block_height % EPOCH);
81 block_height + to_next_epoch
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
86#[archive_attr(derive(CheckBytes))]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88pub struct Stake {
89 chain_id: u8,
90 keys: StakeKeys,
91 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
92 value: u64,
93 signature: DoubleSignature,
94}
95
96impl Stake {
97 const MESSAGE_SIZE: usize =
98 1 + BlsPublicKey::SIZE + BlsPublicKey::SIZE + u64::SIZE;
99
100 #[must_use]
102 pub fn new(
103 account_sk: &BlsSecretKey,
104 owner_sk: &BlsSecretKey,
105 value: u64,
106 chain_id: u8,
107 ) -> Self {
108 let account = BlsPublicKey::from(account_sk);
109 let owner = BlsPublicKey::from(owner_sk);
110
111 let keys = StakeKeys::new(account, owner);
112
113 let mut stake = Stake {
114 chain_id,
115 keys,
116 value,
117 signature: DoubleSignature::default(),
118 };
119
120 let msg = stake.signature_message();
121
122 stake.signature = DoubleSignature {
123 account: account_sk.sign(&msg),
124 owner: owner_sk.sign(&msg),
125 };
126
127 stake
128 }
129
130 #[must_use]
132 pub fn new_from_contract(
133 sk: &BlsSecretKey,
134 contract: ContractId,
135 value: u64,
136 chain_id: u8,
137 ) -> Self {
138 let key = BlsPublicKey::from(sk);
139
140 let keys = StakeKeys::new(key, contract);
141
142 let mut stake = Stake {
143 chain_id,
144 keys,
145 value,
146 signature: DoubleSignature::default(),
147 };
148
149 let msg = stake.signature_message();
150
151 stake.signature = DoubleSignature {
152 account: sk.sign(&msg),
153 owner: sk.sign(&msg),
154 };
155
156 stake
157 }
158
159 #[must_use]
161 pub fn keys(&self) -> &StakeKeys {
162 &self.keys
163 }
164
165 #[must_use]
167 pub fn account(&self) -> &BlsPublicKey {
168 &self.keys.account
169 }
170
171 #[must_use]
173 pub fn value(&self) -> u64 {
174 self.value
175 }
176
177 #[must_use]
179 pub fn chain_id(&self) -> u8 {
180 self.chain_id
181 }
182
183 #[must_use]
185 pub fn signature(&self) -> &DoubleSignature {
186 &self.signature
187 }
188
189 #[must_use]
191 pub fn signature_message(&self) -> [u8; Self::MESSAGE_SIZE] {
192 let mut bytes = [0u8; Self::MESSAGE_SIZE];
193
194 bytes[0] = self.chain_id;
195 let mut offset = 1;
196
197 bytes[offset..offset + BlsPublicKey::SIZE]
198 .copy_from_slice(&self.keys.account.to_bytes());
199 offset += BlsPublicKey::SIZE;
200
201 match &self.keys.owner {
202 StakeFundOwner::Account(key) => bytes
203 [offset..offset + BlsPublicKey::SIZE]
204 .copy_from_slice(&key.to_bytes()),
205 StakeFundOwner::Contract(contract_id) => bytes
206 [offset..offset + CONTRACT_ID_BYTES]
207 .copy_from_slice(&contract_id.to_bytes()),
208 }
209
210 offset += BlsPublicKey::SIZE;
211
212 bytes[offset..offset + u64::SIZE]
213 .copy_from_slice(&self.value.to_bytes());
214
215 bytes
216 }
217}
218
219#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
225#[archive_attr(derive(CheckBytes))]
226#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
227pub struct WithdrawToContract {
228 account: BlsPublicKey,
229 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
230 value: u64,
231 fn_name: String,
232 #[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
233 data: Vec<u8>,
234}
235
236impl WithdrawToContract {
237 pub fn new(
250 account: BlsPublicKey,
251 value: u64,
252 fn_name: impl Into<String>,
253 ) -> Self {
254 Self {
255 account,
256 value,
257 fn_name: fn_name.into(),
258 data: Vec::new(),
259 }
260 }
261
262 #[must_use]
264 pub fn account(&self) -> &BlsPublicKey {
265 &self.account
266 }
267
268 #[must_use]
270 pub fn value(&self) -> u64 {
271 self.value
272 }
273
274 #[must_use]
277 pub fn fn_name(&self) -> &str {
278 &self.fn_name
279 }
280
281 #[must_use]
283 pub fn data(&self) -> &[u8] {
284 &self.data
285 }
286
287 #[must_use]
289 pub fn with_data(mut self, data: Vec<u8>) -> Self {
290 self.data = data;
291 self
292 }
293}
294
295#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
299#[archive_attr(derive(CheckBytes))]
300#[allow(clippy::struct_field_names)]
302#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
303pub struct Withdraw {
304 account: BlsPublicKey,
305 withdraw: TransferWithdraw,
306 signature: DoubleSignature,
307}
308
309impl Withdraw {
310 #[must_use]
312 pub fn new(
313 account_sk: &BlsSecretKey,
314 owner_sk: &BlsSecretKey,
315 withdraw: TransferWithdraw,
316 ) -> Self {
317 let account = BlsPublicKey::from(account_sk);
318
319 let mut stake_withdraw = Withdraw {
320 account,
321 withdraw,
322 signature: DoubleSignature::default(),
323 };
324
325 let msg = stake_withdraw.signature_message();
326
327 stake_withdraw.signature = DoubleSignature {
328 account: account_sk.sign(&msg),
329 owner: owner_sk.sign(&msg),
330 };
331
332 stake_withdraw
333 }
334
335 #[must_use]
337 pub fn with_single_key(
338 sk: &BlsSecretKey,
339 withdraw: TransferWithdraw,
340 ) -> Self {
341 Self::new(sk, sk, withdraw)
342 }
343
344 #[must_use]
346 pub fn account(&self) -> &BlsPublicKey {
347 &self.account
348 }
349
350 #[must_use]
352 pub fn transfer_withdraw(&self) -> &TransferWithdraw {
353 &self.withdraw
354 }
355
356 #[must_use]
358 pub fn signature(&self) -> &DoubleSignature {
359 &self.signature
360 }
361
362 #[must_use]
364 pub fn signature_message(&self) -> Vec<u8> {
365 let mut bytes = Vec::new();
366
367 bytes.extend(self.account.to_bytes());
368 bytes.extend(self.withdraw.wrapped_signature_message());
369
370 bytes
371 }
372}
373
374#[derive(Debug, Clone, Archive, Deserialize, Serialize, PartialEq)]
376#[archive_attr(derive(CheckBytes))]
377#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
378pub struct StakeEvent {
379 pub keys: StakeKeys,
381 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
384 pub value: u64,
385 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
389 pub locked: u64,
390}
391
392impl StakeEvent {
393 #[must_use]
402 pub fn new(keys: StakeKeys, value: u64) -> Self {
403 Self {
404 keys,
405 value,
406 locked: 0,
407 }
408 }
409 #[must_use]
414 pub fn locked(mut self, locked: u64) -> Self {
415 self.locked = locked;
416 self
417 }
418}
419
420#[derive(Debug, Clone, Archive, Deserialize, Serialize, PartialEq)]
422#[archive_attr(derive(CheckBytes))]
423#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
424pub struct SlashEvent {
425 pub account: BlsPublicKey,
427 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
429 pub value: u64,
430 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
432 pub next_eligibility: u64,
433}
434
435#[derive(
447 Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
448)]
449#[archive_attr(derive(CheckBytes))]
450#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
451pub struct StakeData {
452 pub amount: Option<StakeAmount>,
454 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
456 pub reward: u64,
457 pub faults: u8,
459 pub hard_faults: u8,
461}
462
463#[derive(
465 Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
466)]
467#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
468#[archive_attr(derive(CheckBytes))]
469pub struct StakeKeys {
470 pub account: BlsPublicKey,
472
473 pub owner: StakeFundOwner,
480}
481
482impl StakeKeys {
483 #[must_use]
486 pub fn single_key(account: BlsPublicKey) -> Self {
487 Self::new(account, account)
488 }
489
490 #[must_use]
501 pub fn new<F: Into<StakeFundOwner>>(
502 account: BlsPublicKey,
503 owner: F,
504 ) -> Self {
505 let owner = owner.into();
506 Self { account, owner }
507 }
508}
509
510#[derive(
512 Debug, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
513)]
514#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
515#[archive_attr(derive(CheckBytes))]
516pub enum StakeFundOwner {
517 Account(BlsPublicKey),
522
523 Contract(ContractId),
528}
529
530impl Default for StakeFundOwner {
531 fn default() -> Self {
532 BlsPublicKey::default().into()
533 }
534}
535
536impl From<BlsPublicKey> for StakeFundOwner {
537 fn from(value: BlsPublicKey) -> Self {
538 Self::Account(value)
539 }
540}
541
542impl From<ContractId> for StakeFundOwner {
543 fn from(value: ContractId) -> Self {
544 Self::Contract(value)
545 }
546}
547
548#[derive(
550 Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
551)]
552#[archive_attr(derive(CheckBytes))]
553#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
554pub struct DoubleSignature {
555 pub account: BlsSignature,
557 pub owner: BlsSignature,
559}
560
561impl StakeData {
562 pub const EMPTY: Self = Self {
564 amount: None,
565 reward: 0,
566 faults: 0,
567 hard_faults: 0,
568 };
569
570 #[must_use]
573 pub const fn new(value: u64, reward: u64, block_height: u64) -> Self {
574 let eligibility = Self::eligibility_from_height(block_height);
575 Self::with_eligibility(value, reward, eligibility)
576 }
577
578 #[must_use]
581 pub const fn with_eligibility(
582 value: u64,
583 reward: u64,
584 eligibility: u64,
585 ) -> Self {
586 let amount = match value {
587 0 => None,
588 _ => Some(StakeAmount {
589 value,
590 eligibility,
591 locked: 0,
592 }),
593 };
594
595 Self {
596 amount,
597 reward,
598 faults: 0,
599 hard_faults: 0,
600 }
601 }
602
603 #[must_use]
607 pub fn is_valid(&self, block_height: u64) -> bool {
608 match &self.amount {
609 Some(amount) => block_height >= amount.eligibility,
610 None => false,
611 }
612 }
613
614 #[must_use]
616 pub const fn eligibility_from_height(block_height: u64) -> u64 {
617 StakeAmount::eligibility_from_height(block_height)
618 }
619
620 pub fn is_empty(&self) -> bool {
624 let stake = self
625 .amount
626 .as_ref()
627 .map(StakeAmount::total_funds)
628 .unwrap_or_default();
629 self.reward + stake == 0
630 }
631}
632
633#[derive(
635 Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
636)]
637#[archive_attr(derive(CheckBytes))]
638#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
639pub struct StakeAmount {
640 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
642 pub value: u64,
643 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
645 pub locked: u64,
646 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
648 pub eligibility: u64,
649}
650
651impl StakeAmount {
652 #[must_use]
654 pub const fn new(value: u64, block_height: u64) -> Self {
655 let eligibility = Self::eligibility_from_height(block_height);
656 Self::with_eligibility(value, eligibility)
657 }
658
659 #[must_use]
662 pub const fn with_eligibility(value: u64, eligibility: u64) -> Self {
663 Self {
664 value,
665 eligibility,
666 locked: 0,
667 }
668 }
669
670 #[must_use]
672 pub const fn eligibility_from_height(block_height: u64) -> u64 {
673 let maturity_blocks = EPOCH;
674 next_epoch(block_height) + maturity_blocks
675 }
676
677 pub fn lock_amount(&mut self, amount: u64) {
679 self.value -= amount;
680 self.locked += amount;
681 }
682
683 #[must_use]
685 pub fn total_funds(&self) -> u64 {
686 self.value + self.locked
687 }
688}
689
690#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
693#[archive_attr(derive(CheckBytes))]
694#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
695pub struct Reward {
696 pub account: BlsPublicKey,
698 #[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
700 pub value: u64,
701 pub reason: RewardReason,
703}
704
705#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
707#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
708#[archive_attr(derive(CheckBytes))]
709pub enum RewardReason {
710 GeneratorFixed,
712 GeneratorExtra,
714 Voter,
716 Other,
718}