Skip to main content

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