1#[cfg(feature = "serde")]
11use serde_with::{hex::Hex, serde_as, DisplayFromStr};
12
13use alloc::string::String;
14use alloc::vec::Vec;
15use core::cmp::max;
16use core::fmt::Debug;
17
18use bytecheck::CheckBytes;
19use dusk_bytes::{DeserializableSlice, Error as BytesError};
20use poseidon_merkle::Opening;
21use rand::{CryptoRng, RngCore};
22use rkyv::{Archive, Deserialize, Serialize};
23
24use crate::abi::ContractId;
25use crate::error::TxPreconditionError;
26use crate::signatures::bls::{
27 PublicKey as AccountPublicKey, SecretKey as AccountSecretKey,
28};
29use crate::{BlsScalar, Error};
30
31use self::data::{
32 BlobData, BlobSidecar, ContractCall, ContractDeploy, TransactionData,
33};
34use self::moonlight::Transaction as MoonlightTransaction;
35use self::phoenix::{
36 Note, Prove, PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey,
37 Sender, StealthAddress, Transaction as PhoenixTransaction,
38 NOTES_TREE_DEPTH,
39};
40use self::withdraw::{Withdraw, WithdrawReceiver};
41
42pub mod data;
43pub mod moonlight;
44pub mod phoenix;
45pub mod withdraw;
46
47pub const TRANSFER_CONTRACT: ContractId = crate::reserved(0x1);
49
50pub const PANIC_NONCE_NOT_READY: &str = "Nonce not ready to be used yet";
52
53pub const MOONLIGHT_TOPIC: &str = "moonlight";
55pub const PHOENIX_TOPIC: &str = "phoenix";
57pub const CONTRACT_TO_CONTRACT_TOPIC: &str = "contract_to_contract";
59pub const CONTRACT_TO_ACCOUNT_TOPIC: &str = "contract_to_account";
61pub const WITHDRAW_TOPIC: &str = "withdraw";
63pub const DEPOSIT_TOPIC: &str = "deposit";
65pub const CONVERT_TOPIC: &str = "convert";
67pub const MINT_TOPIC: &str = "mint";
69pub const MINT_CONTRACT_TOPIC: &str = "mint_c";
71
72#[derive(Debug, Clone, Archive, PartialEq, Eq, Serialize, Deserialize)]
74#[archive_attr(derive(CheckBytes))]
75#[allow(clippy::large_enum_variant)]
76pub enum Transaction {
77 Phoenix(PhoenixTransaction),
79 Moonlight(MoonlightTransaction),
81}
82
83impl Transaction {
84 #[allow(clippy::too_many_arguments)]
94 pub fn phoenix<R: RngCore + CryptoRng, P: Prove>(
95 rng: &mut R,
96 sender_sk: &PhoenixSecretKey,
97 refund_pk: &PhoenixPublicKey,
98 receiver_pk: &PhoenixPublicKey,
99 inputs: Vec<(Note, Opening<(), NOTES_TREE_DEPTH>)>,
100 root: BlsScalar,
101 transfer_value: u64,
102 obfuscated_transaction: bool,
103 deposit: u64,
104 gas_limit: u64,
105 gas_price: u64,
106 chain_id: u8,
107 data: Option<impl Into<TransactionData>>,
108 prover: &P,
109 ) -> Result<Self, Error> {
110 Ok(Self::Phoenix(PhoenixTransaction::new::<R, P>(
111 rng,
112 sender_sk,
113 refund_pk,
114 receiver_pk,
115 inputs,
116 root,
117 transfer_value,
118 obfuscated_transaction,
119 deposit,
120 gas_limit,
121 gas_price,
122 chain_id,
123 data,
124 prover,
125 )?))
126 }
127
128 #[allow(clippy::too_many_arguments)]
134 pub fn moonlight(
135 sender_sk: &AccountSecretKey,
136 receiver: Option<AccountPublicKey>,
137 value: u64,
138 deposit: u64,
139 gas_limit: u64,
140 gas_price: u64,
141 nonce: u64,
142 chain_id: u8,
143 data: Option<impl Into<TransactionData>>,
144 ) -> Result<Self, Error> {
145 Ok(Self::Moonlight(MoonlightTransaction::new(
146 sender_sk, receiver, value, deposit, gas_limit, gas_price, nonce,
147 chain_id, data,
148 )?))
149 }
150
151 #[must_use]
153 pub fn moonlight_sender(&self) -> Option<&AccountPublicKey> {
154 match self {
155 Self::Phoenix(_) => None,
156 Self::Moonlight(tx) => Some(tx.sender()),
157 }
158 }
159
160 #[must_use]
171 pub fn moonlight_receiver(&self) -> Option<&AccountPublicKey> {
172 match self {
173 Self::Phoenix(_) => None,
174 Self::Moonlight(tx) => tx.receiver(),
175 }
176 }
177
178 #[must_use]
180 pub fn value(&self) -> Option<u64> {
181 match self {
182 Self::Phoenix(_) => None,
183 Self::Moonlight(tx) => Some(tx.value()),
184 }
185 }
186
187 #[must_use]
190 pub fn nullifiers(&self) -> &[BlsScalar] {
191 match self {
192 Self::Phoenix(tx) => tx.nullifiers(),
193 Self::Moonlight(_) => &[],
194 }
195 }
196
197 #[must_use]
199 pub fn root(&self) -> Option<&BlsScalar> {
200 match self {
201 Self::Phoenix(tx) => Some(tx.root()),
202 Self::Moonlight(_) => None,
203 }
204 }
205
206 #[must_use]
208 pub fn outputs(&self) -> &[Note] {
209 match self {
210 Self::Phoenix(tx) => &tx.outputs()[..],
211 Self::Moonlight(_) => &[],
212 }
213 }
214
215 #[must_use]
217 pub fn phoenix_sender(&self) -> Option<&Sender> {
218 match self {
219 Self::Phoenix(tx) => Some(tx.sender()),
220 Self::Moonlight(_) => None,
221 }
222 }
223
224 #[must_use]
226 pub fn deposit(&self) -> u64 {
227 match self {
228 Self::Phoenix(tx) => tx.deposit(),
229 Self::Moonlight(tx) => tx.deposit(),
230 }
231 }
232
233 #[must_use]
235 pub fn gas_limit(&self) -> u64 {
236 match self {
237 Self::Phoenix(tx) => tx.gas_limit(),
238 Self::Moonlight(tx) => tx.gas_limit(),
239 }
240 }
241
242 #[must_use]
244 pub fn gas_price(&self) -> u64 {
245 match self {
246 Self::Phoenix(tx) => tx.gas_price(),
247 Self::Moonlight(tx) => tx.gas_price(),
248 }
249 }
250
251 #[must_use]
253 pub fn refund_address(&self) -> RefundAddress {
254 match self {
255 Self::Phoenix(tx) => RefundAddress::Phoenix(tx.stealth_address()),
256 Self::Moonlight(tx) => {
257 RefundAddress::Moonlight(tx.refund_address())
258 }
259 }
260 }
261
262 #[must_use]
273 pub fn call(&self) -> Option<&ContractCall> {
274 match self {
275 Self::Phoenix(tx) => tx.call(),
276 Self::Moonlight(tx) => tx.call(),
277 }
278 }
279
280 #[must_use]
282 pub fn deploy(&self) -> Option<&ContractDeploy> {
283 match self {
284 Self::Phoenix(tx) => tx.deploy(),
285 Self::Moonlight(tx) => tx.deploy(),
286 }
287 }
288
289 #[must_use]
291 pub fn memo(&self) -> Option<&[u8]> {
292 match self {
293 Self::Phoenix(tx) => tx.memo(),
294 Self::Moonlight(tx) => tx.memo(),
295 }
296 }
297
298 #[must_use]
300 pub fn blob(&self) -> Option<&Vec<BlobData>> {
301 match self {
302 Self::Phoenix(tx) => tx.blob(),
303 Self::Moonlight(tx) => tx.blob(),
304 }
305 }
306
307 #[must_use]
309 pub fn blob_mut(&mut self) -> Option<&mut Vec<BlobData>> {
310 match self {
311 Self::Phoenix(tx) => tx.blob_mut(),
312 Self::Moonlight(tx) => tx.blob_mut(),
313 }
314 }
315
316 #[must_use]
326 pub fn strip_blobs(&mut self) -> Option<Vec<([u8; 32], BlobSidecar)>> {
327 let blob = match self {
328 Self::Phoenix(tx) => tx.blob_mut(),
329 Self::Moonlight(tx) => tx.blob_mut(),
330 }?;
331
332 let ret = blob
333 .iter_mut()
334 .filter_map(|b| b.take_sidecar().map(|d| (b.hash, d)))
335 .collect::<Vec<_>>();
336
337 Some(ret)
338 }
339
340 #[must_use]
346 pub fn blob_to_memo(&self) -> Option<Self> {
347 Some(match self {
348 Transaction::Phoenix(tx) => {
349 Transaction::Phoenix(tx.blob_to_memo()?)
350 }
351 Transaction::Moonlight(tx) => {
352 Transaction::Moonlight(tx.blob_to_memo()?)
353 }
354 })
355 }
356
357 #[must_use]
361 pub fn strip_off_bytecode(&self) -> Option<Self> {
362 Some(match self {
363 Transaction::Phoenix(tx) => {
364 Transaction::Phoenix(tx.strip_off_bytecode()?)
365 }
366 Transaction::Moonlight(tx) => {
367 Transaction::Moonlight(tx.strip_off_bytecode()?)
368 }
369 })
370 }
371
372 #[must_use]
374 pub fn to_var_bytes(&self) -> Vec<u8> {
375 let mut bytes = Vec::new();
376
377 match self {
378 Self::Phoenix(tx) => {
379 bytes.push(0);
380 bytes.extend(tx.to_var_bytes());
381 }
382 Self::Moonlight(tx) => {
383 bytes.push(1);
384 bytes.extend(tx.to_var_bytes());
385 }
386 }
387
388 bytes
389 }
390
391 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
396 let mut buf = buf;
397
398 Ok(match u8::from_reader(&mut buf)? {
399 0 => Self::Phoenix(PhoenixTransaction::from_slice(buf)?),
400 1 => Self::Moonlight(MoonlightTransaction::from_slice(buf)?),
401 _ => return Err(BytesError::InvalidData),
402 })
403 }
404
405 #[must_use]
410 pub fn to_hash_input_bytes(&self) -> Vec<u8> {
411 match self {
412 Self::Phoenix(tx) => tx.to_hash_input_bytes(),
413 Self::Moonlight(tx) => tx.to_hash_input_bytes(),
414 }
415 }
416
417 #[must_use]
419 pub fn hash(&self) -> BlsScalar {
420 match self {
421 Self::Phoenix(tx) => tx.hash(),
422 Self::Moonlight(tx) => tx.hash(),
423 }
424 }
425
426 #[must_use]
431 pub fn deploy_charge(
432 &self,
433 gas_per_deploy_byte: u64,
434 min_deploy_points: u64,
435 ) -> u64 {
436 if let Some(deploy) = self.deploy() {
437 let bytecode_len = deploy.bytecode.bytes.len() as u64;
438 max(bytecode_len * gas_per_deploy_byte, min_deploy_points)
439 } else {
440 0
441 }
442 }
443
444 #[must_use]
447 pub fn blob_charge(&self, gas_per_blob: u64) -> Option<u64> {
448 self.blob().map(|blobs| blobs.len() as u64 * gas_per_blob)
449 }
450
451 pub fn deploy_check(
459 &self,
460 gas_per_deploy_byte: u64,
461 min_deploy_gas_price: u64,
462 min_deploy_points: u64,
463 ) -> Result<(), TxPreconditionError> {
464 if self.deploy().is_some() {
465 let deploy_charge =
466 self.deploy_charge(gas_per_deploy_byte, min_deploy_points);
467
468 if self.gas_price() < min_deploy_gas_price {
469 return Err(TxPreconditionError::DeployLowPrice(
470 min_deploy_gas_price,
471 ));
472 }
473 if self.gas_limit() < deploy_charge {
474 return Err(TxPreconditionError::DeployLowLimit(deploy_charge));
475 }
476 }
477
478 Ok(())
479 }
480
481 pub fn blob_check(
495 &self,
496 gas_per_blob: u64,
497 ) -> Result<Option<u64>, TxPreconditionError> {
498 if let Some(blobs) = self.blob() {
499 match blobs.len() {
500 0 => Err(TxPreconditionError::BlobEmpty),
501 n if n > 6 => Err(TxPreconditionError::BlobTooMany(n)),
502 _ => Ok(()),
503 }?;
504 } else {
505 return Ok(None);
506 }
507
508 let min_charge = self.blob_charge(gas_per_blob);
509 if let Some(min_charge) = min_charge {
510 if self.gas_limit() < min_charge {
511 return Err(TxPreconditionError::BlobLowLimit(min_charge));
512 }
513 }
514 Ok(min_charge)
515 }
516}
517
518impl From<PhoenixTransaction> for Transaction {
519 fn from(tx: PhoenixTransaction) -> Self {
520 Self::Phoenix(tx)
521 }
522}
523
524impl From<MoonlightTransaction> for Transaction {
525 fn from(tx: MoonlightTransaction) -> Self {
526 Self::Moonlight(tx)
527 }
528}
529
530pub enum RefundAddress<'a> {
533 Phoenix(&'a StealthAddress),
535 Moonlight(&'a AccountPublicKey),
537}
538
539#[derive(Debug, Clone, Archive, PartialEq, Eq, Serialize, Deserialize)]
542#[archive_attr(derive(CheckBytes))]
543#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
544#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
545pub struct ContractToContract {
546 pub contract: ContractId,
548 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
550 pub value: u64,
551 pub fn_name: String,
553 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
555 pub data: Vec<u8>,
556}
557
558#[derive(Debug, Clone, Archive, PartialEq, Eq, Serialize, Deserialize)]
561#[archive_attr(derive(CheckBytes))]
562#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
563#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
564pub struct ReceiveFromContract {
565 pub contract: ContractId,
567 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
569 pub value: u64,
570 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
572 pub data: Vec<u8>,
573}
574
575#[derive(Debug, Clone, Archive, PartialEq, Eq, Serialize, Deserialize)]
578#[archive_attr(derive(CheckBytes))]
579#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
580#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
581pub struct ContractToAccount {
582 pub account: AccountPublicKey,
584 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
586 pub value: u64,
587}
588
589#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
591#[archive_attr(derive(CheckBytes))]
592#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
593#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
594pub struct WithdrawEvent {
595 pub sender: ContractId,
597 pub receiver: WithdrawReceiver,
599 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
601 pub value: u64,
602}
603
604impl From<Withdraw> for WithdrawEvent {
605 fn from(w: Withdraw) -> Self {
606 Self {
607 sender: w.contract,
608 receiver: w.receiver,
609 value: w.value,
610 }
611 }
612}
613
614#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
617#[archive_attr(derive(CheckBytes))]
618#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
619#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
620pub struct ConvertEvent {
621 pub sender: Option<AccountPublicKey>,
625 pub receiver: WithdrawReceiver,
627 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
629 pub value: u64,
630}
631
632impl ConvertEvent {
633 #[must_use]
635 pub fn from_withdraw_and_sender(
636 sender: Option<AccountPublicKey>,
637 withdraw: &Withdraw,
638 ) -> Self {
639 Self {
640 sender,
641 receiver: withdraw.receiver,
642 value: withdraw.value,
643 }
644 }
645}
646
647#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
649#[archive_attr(derive(CheckBytes))]
650#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
651#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
652pub struct DepositEvent {
653 pub sender: Option<AccountPublicKey>,
657 pub receiver: ContractId,
659 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
661 pub value: u64,
662}
663
664#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
666#[archive_attr(derive(CheckBytes))]
667#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
668#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
669pub struct ContractToContractEvent {
670 pub sender: ContractId,
672 pub receiver: ContractId,
674 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
676 pub value: u64,
677}
678
679#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
681#[archive_attr(derive(CheckBytes))]
682#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
683#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
684pub struct ContractToAccountEvent {
685 pub sender: ContractId,
687 pub receiver: AccountPublicKey,
689 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
691 pub value: u64,
692}
693
694#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
696#[archive_attr(derive(CheckBytes))]
697#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
698#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
699pub struct PhoenixTransactionEvent {
700 pub nullifiers: Vec<BlsScalar>,
702 pub notes: Vec<Note>,
704 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
706 pub memo: Vec<u8>,
707 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
709 pub gas_spent: u64,
710
711 pub refund_note: Option<Note>,
713}
714
715#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
717#[archive_attr(derive(CheckBytes))]
718#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
719#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
720pub struct MoonlightTransactionEvent {
721 pub sender: AccountPublicKey,
723 pub receiver: Option<AccountPublicKey>,
725 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
727 pub value: u64,
728 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
730 pub memo: Vec<u8>,
731 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
733 pub gas_spent: u64,
734 #[cfg_attr(
737 feature = "serde",
738 serde_as(as = "Option<(serde_with::Same, DisplayFromStr)>")
739 )]
740 pub refund_info: Option<(AccountPublicKey, u64)>,
741}