1#[cfg(feature = "serde")]
10use serde_with::{hex::Hex, serde_as, DisplayFromStr};
11
12use alloc::string::String;
13use alloc::vec::Vec;
14
15use bytecheck::CheckBytes;
16use dusk_bytes::Serializable;
17use piecrust_uplink::CONTRACT_ID_BYTES;
18use rkyv::{Archive, Deserialize, Serialize};
19
20use crate::abi::ContractId;
21use crate::signatures::bls::{
22 PublicKey as BlsPublicKey, SecretKey as BlsSecretKey,
23 Signature as BlsSignature,
24};
25use crate::transfer::withdraw::Withdraw as TransferWithdraw;
26use crate::{dusk, Dusk};
27
28pub const STAKE_CONTRACT: ContractId = crate::reserved(0x2);
30
31pub const EPOCH: u64 = 2160;
33
34pub const DEFAULT_STAKE_WARNINGS: u8 = 1;
36
37#[deprecated(
39 since = "0.1.0",
40 note = "please use `DEFAULT_MINIMUM_STAKE` instead"
41)]
42pub const MINIMUM_STAKE: Dusk = DEFAULT_MINIMUM_STAKE;
43
44pub const DEFAULT_MINIMUM_STAKE: Dusk = dusk(1_000.0);
46
47#[derive(Debug, Clone, Archive, Serialize, Deserialize)]
49#[archive_attr(derive(CheckBytes))]
50#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct StakeConfig {
53 pub warnings: u8,
55 #[cfg_attr(feature = "serde", serde_as(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", cfg_eval, serde_as)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89pub struct Stake {
90 chain_id: u8,
91 keys: StakeKeys,
92 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
93 value: u64,
94 signature: DoubleSignature,
95}
96
97impl Stake {
98 const MESSAGE_SIZE: usize =
99 1 + BlsPublicKey::SIZE + BlsPublicKey::SIZE + u64::SIZE;
100
101 #[must_use]
103 pub fn new(
104 account_sk: &BlsSecretKey,
105 owner_sk: &BlsSecretKey,
106 value: u64,
107 chain_id: u8,
108 ) -> Self {
109 let account = BlsPublicKey::from(account_sk);
110 let owner = BlsPublicKey::from(owner_sk);
111
112 let keys = StakeKeys::new(account, owner);
113
114 let mut stake = Stake {
115 chain_id,
116 keys,
117 value,
118 signature: DoubleSignature::default(),
119 };
120
121 let msg = stake.signature_message();
122
123 stake.signature = DoubleSignature {
124 account: account_sk.sign(&msg),
125 owner: owner_sk.sign(&msg),
126 };
127
128 stake
129 }
130
131 #[must_use]
133 pub fn new_from_contract(
134 sk: &BlsSecretKey,
135 contract: ContractId,
136 value: u64,
137 chain_id: u8,
138 ) -> Self {
139 let key = BlsPublicKey::from(sk);
140
141 let keys = StakeKeys::new(key, contract);
142
143 let mut stake = Stake {
144 chain_id,
145 keys,
146 value,
147 signature: DoubleSignature::default(),
148 };
149
150 let msg = stake.signature_message();
151
152 stake.signature = DoubleSignature {
153 account: sk.sign(&msg),
154 owner: sk.sign(&msg),
155 };
156
157 stake
158 }
159
160 #[must_use]
162 pub fn keys(&self) -> &StakeKeys {
163 &self.keys
164 }
165
166 #[must_use]
168 pub fn account(&self) -> &BlsPublicKey {
169 &self.keys.account
170 }
171
172 #[must_use]
174 pub fn value(&self) -> u64 {
175 self.value
176 }
177
178 #[must_use]
180 pub fn chain_id(&self) -> u8 {
181 self.chain_id
182 }
183
184 #[must_use]
186 pub fn signature(&self) -> &DoubleSignature {
187 &self.signature
188 }
189
190 #[must_use]
192 pub fn signature_message(&self) -> [u8; Self::MESSAGE_SIZE] {
193 let mut bytes = [0u8; Self::MESSAGE_SIZE];
194
195 bytes[0] = self.chain_id;
196 let mut offset = 1;
197
198 bytes[offset..offset + BlsPublicKey::SIZE]
199 .copy_from_slice(&self.keys.account.to_bytes());
200 offset += BlsPublicKey::SIZE;
201
202 match &self.keys.owner {
203 StakeFundOwner::Account(key) => bytes
204 [offset..offset + BlsPublicKey::SIZE]
205 .copy_from_slice(&key.to_bytes()),
206 StakeFundOwner::Contract(contract_id) => bytes
207 [offset..offset + CONTRACT_ID_BYTES]
208 .copy_from_slice(&contract_id.to_bytes()),
209 }
210
211 offset += BlsPublicKey::SIZE;
212
213 bytes[offset..offset + u64::SIZE]
214 .copy_from_slice(&self.value.to_bytes());
215
216 bytes
217 }
218}
219
220#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
226#[archive_attr(derive(CheckBytes))]
227#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229pub struct WithdrawToContract {
230 account: BlsPublicKey,
231 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
232 value: u64,
233 fn_name: String,
234 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
235 data: Vec<u8>,
236}
237
238impl WithdrawToContract {
239 pub fn new(
252 account: BlsPublicKey,
253 value: u64,
254 fn_name: impl Into<String>,
255 ) -> Self {
256 Self {
257 account,
258 value,
259 fn_name: fn_name.into(),
260 data: Vec::new(),
261 }
262 }
263
264 #[must_use]
266 pub fn account(&self) -> &BlsPublicKey {
267 &self.account
268 }
269
270 #[must_use]
272 pub fn value(&self) -> u64 {
273 self.value
274 }
275
276 #[must_use]
279 pub fn fn_name(&self) -> &str {
280 &self.fn_name
281 }
282
283 #[must_use]
285 pub fn data(&self) -> &[u8] {
286 &self.data
287 }
288
289 #[must_use]
291 pub fn with_data(mut self, data: Vec<u8>) -> Self {
292 self.data = data;
293 self
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
301#[archive_attr(derive(CheckBytes))]
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", cfg_eval, serde_as)]
378#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
379pub struct StakeEvent {
380 pub keys: StakeKeys,
382 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
385 pub value: u64,
386 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
390 pub locked: u64,
391}
392
393impl StakeEvent {
394 #[must_use]
403 pub fn new(keys: StakeKeys, value: u64) -> Self {
404 Self {
405 keys,
406 value,
407 locked: 0,
408 }
409 }
410 #[must_use]
415 pub fn locked(mut self, locked: u64) -> Self {
416 self.locked = locked;
417 self
418 }
419}
420
421#[derive(Debug, Clone, Archive, Deserialize, Serialize, PartialEq)]
423#[archive_attr(derive(CheckBytes))]
424#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
425#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
426pub struct SlashEvent {
427 pub account: BlsPublicKey,
429 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
431 pub value: u64,
432 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
434 pub next_eligibility: u64,
435}
436
437#[derive(
449 Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
450)]
451#[archive_attr(derive(CheckBytes))]
452#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
453#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
454pub struct StakeData {
455 pub amount: Option<StakeAmount>,
457 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
459 pub reward: u64,
460 pub faults: u8,
462 pub hard_faults: u8,
464}
465
466#[derive(
468 Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
469)]
470#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
471#[archive_attr(derive(CheckBytes))]
472pub struct StakeKeys {
473 pub account: BlsPublicKey,
475
476 pub owner: StakeFundOwner,
483}
484
485impl StakeKeys {
486 #[must_use]
489 pub fn single_key(account: BlsPublicKey) -> Self {
490 Self::new(account, account)
491 }
492
493 #[must_use]
504 pub fn new<F: Into<StakeFundOwner>>(
505 account: BlsPublicKey,
506 owner: F,
507 ) -> Self {
508 let owner = owner.into();
509 Self { account, owner }
510 }
511}
512
513#[derive(
515 Debug, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
516)]
517#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
518#[archive_attr(derive(CheckBytes))]
519pub enum StakeFundOwner {
520 Account(BlsPublicKey),
525
526 Contract(ContractId),
531}
532
533impl Default for StakeFundOwner {
534 fn default() -> Self {
535 BlsPublicKey::default().into()
536 }
537}
538
539impl From<BlsPublicKey> for StakeFundOwner {
540 fn from(value: BlsPublicKey) -> Self {
541 Self::Account(value)
542 }
543}
544
545impl From<ContractId> for StakeFundOwner {
546 fn from(value: ContractId) -> Self {
547 Self::Contract(value)
548 }
549}
550
551#[derive(
553 Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
554)]
555#[archive_attr(derive(CheckBytes))]
556#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
557pub struct DoubleSignature {
558 pub account: BlsSignature,
560 pub owner: BlsSignature,
562}
563
564impl StakeData {
565 pub const EMPTY: Self = Self {
567 amount: None,
568 reward: 0,
569 faults: 0,
570 hard_faults: 0,
571 };
572
573 #[must_use]
576 pub const fn new(value: u64, reward: u64, block_height: u64) -> Self {
577 let eligibility = Self::eligibility_from_height(block_height);
578 Self::with_eligibility(value, reward, eligibility)
579 }
580
581 #[must_use]
584 pub const fn with_eligibility(
585 value: u64,
586 reward: u64,
587 eligibility: u64,
588 ) -> Self {
589 let amount = match value {
590 0 => None,
591 _ => Some(StakeAmount {
592 value,
593 eligibility,
594 locked: 0,
595 }),
596 };
597
598 Self {
599 amount,
600 reward,
601 faults: 0,
602 hard_faults: 0,
603 }
604 }
605
606 #[must_use]
610 pub fn is_valid(&self, block_height: u64) -> bool {
611 match &self.amount {
612 Some(amount) => block_height >= amount.eligibility,
613 None => false,
614 }
615 }
616
617 #[must_use]
619 pub const fn eligibility_from_height(block_height: u64) -> u64 {
620 StakeAmount::eligibility_from_height(block_height)
621 }
622
623 pub fn is_empty(&self) -> bool {
627 let stake = self
628 .amount
629 .as_ref()
630 .map(StakeAmount::total_funds)
631 .unwrap_or_default();
632 self.reward + stake == 0
633 }
634}
635
636#[derive(
638 Debug, Default, Clone, Copy, PartialEq, Eq, Archive, Deserialize, Serialize,
639)]
640#[archive_attr(derive(CheckBytes))]
641#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
642#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
643pub struct StakeAmount {
644 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
646 pub value: u64,
647 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
649 pub locked: u64,
650 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
652 pub eligibility: u64,
653}
654
655impl StakeAmount {
656 #[must_use]
658 pub const fn new(value: u64, block_height: u64) -> Self {
659 let eligibility = Self::eligibility_from_height(block_height);
660 Self::with_eligibility(value, eligibility)
661 }
662
663 #[must_use]
666 pub const fn with_eligibility(value: u64, eligibility: u64) -> Self {
667 Self {
668 value,
669 eligibility,
670 locked: 0,
671 }
672 }
673
674 #[must_use]
676 pub const fn eligibility_from_height(block_height: u64) -> u64 {
677 let maturity_blocks = EPOCH;
678 next_epoch(block_height) + maturity_blocks
679 }
680
681 pub fn lock_amount(&mut self, amount: u64) {
683 self.value -= amount;
684 self.locked += amount;
685 }
686
687 #[must_use]
689 pub fn total_funds(&self) -> u64 {
690 self.value + self.locked
691 }
692}
693
694#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
697#[archive_attr(derive(CheckBytes))]
698#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
699#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
700pub struct Reward {
701 pub account: BlsPublicKey,
703 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
705 pub value: u64,
706 pub reason: RewardReason,
708}
709
710#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
712#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
713#[archive_attr(derive(CheckBytes))]
714pub enum RewardReason {
715 GeneratorFixed,
717 GeneratorExtra,
719 Voter,
721 Other,
723}