dusk_core/transfer/
withdraw.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7//! Types related to withdrawing funds into moonlight of phoenix Dusk.
8
9#[cfg(feature = "serde")]
10use serde_with::{serde_as, DisplayFromStr};
11
12use alloc::vec::Vec;
13
14use bytecheck::CheckBytes;
15use dusk_bytes::Serializable;
16use rand::{CryptoRng, RngCore};
17use rkyv::{Archive, Deserialize, Serialize};
18
19use crate::abi::ContractId;
20use crate::signatures::bls::{
21    PublicKey as AccountPublicKey, SecretKey as AccountSecretKey,
22    Signature as AccountSignature,
23};
24use crate::signatures::schnorr::{
25    SecretKey as NoteSecretKey, Signature as NoteSignature,
26};
27use crate::transfer::phoenix::StealthAddress;
28use crate::BlsScalar;
29
30/// Withdrawal information, proving the intent of a user to withdraw from a
31/// contract.
32///
33/// This structure is meant to be passed to a contract by a caller. The contract
34/// is then responsible for calling `withdraw` in the transfer contract to
35/// settle it, if it wants to allow the withdrawal.
36///
37/// e.g. the stake contract uses it as a call argument for the `unstake`
38/// function
39#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
40#[archive_attr(derive(CheckBytes))]
41#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub struct Withdraw {
44    pub(super) contract: ContractId,
45    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
46    pub(super) value: u64,
47    pub(super) receiver: WithdrawReceiver,
48    token: WithdrawReplayToken,
49    signature: WithdrawSignature,
50}
51
52impl Withdraw {
53    /// Create a new contract withdrawal.
54    ///
55    /// # Panics
56    /// When the receiver does not match the secret key passed.
57    #[must_use]
58    pub fn new<'a, R: RngCore + CryptoRng>(
59        rng: &mut R,
60        sk: impl Into<WithdrawSecretKey<'a>>,
61        contract: ContractId,
62        value: u64,
63        receiver: WithdrawReceiver,
64        token: WithdrawReplayToken,
65    ) -> Self {
66        let mut withdraw = Self {
67            contract,
68            value,
69            receiver,
70            token,
71            signature: WithdrawSignature::Moonlight(AccountSignature::default()),
72        };
73
74        let sk = sk.into();
75
76        match (&sk, &receiver) {
77            (WithdrawSecretKey::Phoenix(_), WithdrawReceiver::Moonlight(_)) => {
78                panic!("Moonlight receiver with phoenix signer");
79            }
80            (WithdrawSecretKey::Moonlight(_), WithdrawReceiver::Phoenix(_)) => {
81                panic!("Phoenix receiver with moonlight signer");
82            }
83            _ => {}
84        }
85
86        let msg = withdraw.signature_message();
87
88        match sk {
89            WithdrawSecretKey::Phoenix(sk) => {
90                let digest = BlsScalar::hash_to_scalar(&msg);
91                let signature = sk.sign(rng, digest);
92                withdraw.signature = signature.into();
93            }
94            WithdrawSecretKey::Moonlight(sk) => {
95                let signature = sk.sign(&msg);
96                withdraw.signature = signature.into();
97            }
98        }
99
100        withdraw
101    }
102
103    /// The contract to withraw from.
104    #[must_use]
105    pub fn contract(&self) -> &ContractId {
106        &self.contract
107    }
108
109    /// The amount to withdraw.
110    #[must_use]
111    pub fn value(&self) -> u64 {
112        self.value
113    }
114
115    /// The receiver of the value.
116    #[must_use]
117    pub fn receiver(&self) -> &WithdrawReceiver {
118        &self.receiver
119    }
120
121    /// The unique token to prevent replay.
122    #[must_use]
123    pub fn token(&self) -> &WithdrawReplayToken {
124        &self.token
125    }
126
127    /// Signature of the withdrawal.
128    #[must_use]
129    pub fn signature(&self) -> &WithdrawSignature {
130        &self.signature
131    }
132
133    /// Return the message that is used as the input to the signature.
134    #[must_use]
135    pub fn signature_message(&self) -> Vec<u8> {
136        let mut bytes = Vec::new();
137
138        bytes.extend(self.contract.as_bytes());
139        bytes.extend(self.value.to_bytes());
140
141        match self.receiver {
142            WithdrawReceiver::Phoenix(address) => {
143                bytes.extend(address.to_bytes());
144            }
145            WithdrawReceiver::Moonlight(account) => {
146                bytes.extend(account.to_bytes());
147            }
148        }
149
150        match &self.token {
151            WithdrawReplayToken::Phoenix(nullifiers) => {
152                for n in nullifiers {
153                    bytes.extend(n.to_bytes());
154                }
155            }
156            WithdrawReplayToken::Moonlight(nonce) => {
157                bytes.extend(nonce.to_bytes());
158            }
159        }
160
161        bytes
162    }
163
164    /// Returns the message that should be "mixed in" as input for a signature
165    /// of an item that wraps a [`Withdraw`].
166    ///
167    /// One example of this is [`crate::stake::Withdraw`].
168    #[must_use]
169    pub fn wrapped_signature_message(&self) -> Vec<u8> {
170        let mut bytes = self.signature_message();
171        bytes.extend(self.signature.to_var_bytes());
172        bytes
173    }
174}
175
176/// The receiver of the [`Withdraw`] value.
177#[derive(Debug, Clone, Copy, PartialEq, Archive, Serialize, Deserialize)]
178#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
179#[archive_attr(derive(CheckBytes))]
180pub enum WithdrawReceiver {
181    /// The stealth address to withdraw to, when the withdrawal is into Phoenix
182    /// notes.
183    Phoenix(StealthAddress),
184    /// The account to withdraw to, when the withdrawal is to a Moonlight
185    /// account.
186    Moonlight(AccountPublicKey),
187}
188
189/// The token used for replay protection in a [`Withdraw`]. This is the same as
190/// the encapsulating transaction's fields.
191#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
192#[archive_attr(derive(CheckBytes))]
193#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
195pub enum WithdrawReplayToken {
196    /// The nullifiers of the encapsulating Phoenix transaction, when the
197    /// transaction is paid for using Phoenix notes.
198    Phoenix(Vec<BlsScalar>),
199    /// The nonce of the encapsulating Moonlight transaction, when the
200    /// transaction is paid for using a Moonlight account.
201    Moonlight(
202        #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))] u64,
203    ),
204}
205
206/// The secret key used for signing a [`Withdraw`].
207///
208/// When the withdrawal is into Phoenix notes, a [`NoteSecretKey`] should be
209/// used. When the withdrawal is into a Moonlight account an
210/// [`AccountSecretKey`] should be used.
211#[derive(Debug, Clone, PartialEq)]
212pub enum WithdrawSecretKey<'a> {
213    /// The secret key used to sign a withdrawal into Phoenix notes.
214    Phoenix(&'a NoteSecretKey),
215    /// The secret key used to sign a withdrawal into a Moonlight account.
216    Moonlight(&'a AccountSecretKey),
217}
218
219impl<'a> From<&'a NoteSecretKey> for WithdrawSecretKey<'a> {
220    fn from(sk: &'a NoteSecretKey) -> Self {
221        Self::Phoenix(sk)
222    }
223}
224
225impl<'a> From<&'a AccountSecretKey> for WithdrawSecretKey<'a> {
226    fn from(sk: &'a AccountSecretKey) -> Self {
227        Self::Moonlight(sk)
228    }
229}
230
231/// The signature used for a [`Withdraw`].
232#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
233#[archive_attr(derive(CheckBytes))]
234#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
235pub enum WithdrawSignature {
236    /// A transaction withdrawing to Phoenix must sign using their
237    /// [`NoteSecretKey`] which can only be generated by the note-owner's
238    /// [`crate::transfer::phoenix::SecretKey`].
239    Phoenix(NoteSignature),
240    /// A transaction withdrawing to Moonlight - must sign using their
241    /// [`AccountSecretKey`].
242    Moonlight(AccountSignature),
243}
244
245impl WithdrawSignature {
246    fn to_var_bytes(&self) -> Vec<u8> {
247        match self {
248            WithdrawSignature::Phoenix(sig) => sig.to_bytes().to_vec(),
249            WithdrawSignature::Moonlight(sig) => sig.to_bytes().to_vec(),
250        }
251    }
252}
253
254impl From<NoteSignature> for WithdrawSignature {
255    fn from(sig: NoteSignature) -> Self {
256        Self::Phoenix(sig)
257    }
258}
259
260impl From<AccountSignature> for WithdrawSignature {
261    fn from(sig: AccountSignature) -> Self {
262        Self::Moonlight(sig)
263    }
264}