dusk_node_data/events/
transactions.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
7use std::collections::HashMap;
8
9use dusk_core::transfer::RefundAddress;
10
11use super::*;
12use crate::ledger::{Hash, SpentTransaction, Transaction};
13
14/// Represents events related to transactions.
15///
16/// - `Removed(Hash)`
17///
18///     Indicates that a transaction has been removed from the mempool. The
19///     `Hash` represents the unique identifier of the removed transaction.
20///
21///     This event is triggered when a transaction is removed from the mempool
22///     or discarded from the mempool.
23///
24/// - `Included(&'t Transaction)`
25///
26///     A transaction has been included in the mempool.
27///
28/// - `Executed(&'t SpentTransaction)`
29///
30///     Denotes that a transaction has been executed into an accepted block.
31///     Executed transactions also include failed transaction, as they have been
32///     spent and were correctly executed according to any contract logic.
33///     (including logic that triggers panics)
34///
35///     - A "successful" transaction: executed and the `err` field is `None`.
36///     - A "failed" transaction: executed and the `err` field is `Some`.
37#[derive(Clone, Debug)]
38pub enum TransactionEvent<'t> {
39    Removed(Hash),
40    Included(&'t Transaction),
41    Executed(&'t SpentTransaction),
42}
43
44impl EventSource for TransactionEvent<'_> {
45    const COMPONENT: &'static str = "transactions";
46
47    fn topic(&self) -> &'static str {
48        match self {
49            Self::Removed(_) => "removed",
50            Self::Executed(_) => "executed",
51            Self::Included(_) => "included",
52        }
53    }
54    fn data(&self) -> Option<serde_json::Value> {
55        match self {
56            Self::Removed(_) => None,
57            Self::Executed(t) => serde_json::to_value(t).ok(),
58            Self::Included(t) => serde_json::to_value(t).ok(),
59        }
60    }
61    fn entity(&self) -> String {
62        let hash = match self {
63            Self::Removed(hash) => *hash,
64            Self::Executed(tx) => tx.inner.id(),
65            Self::Included(tx) => tx.id(),
66        };
67        hex::encode(hash)
68    }
69}
70use base64::engine::general_purpose::STANDARD as BASE64_ENGINE;
71use base64::Engine;
72use dusk_bytes::Serializable;
73use dusk_core::transfer::Transaction as ProtocolTransaction;
74use serde::ser::{Serialize, SerializeStruct, Serializer};
75
76impl Serialize for Transaction {
77    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
78    where
79        S: Serializer,
80    {
81        let mut state = serializer.serialize_struct("Transaction", 1)?;
82        match &self.inner {
83            ProtocolTransaction::Phoenix(p) => {
84                state.serialize_field("type", "phoenix")?;
85
86                let root = p.root().to_bytes();
87                state.serialize_field("root", &hex::encode(root))?;
88
89                let nullifiers: Vec<_> = p
90                    .nullifiers()
91                    .iter()
92                    .map(|n| hex::encode(n.to_bytes()))
93                    .collect();
94                state.serialize_field("nullifiers", &nullifiers)?;
95            }
96            ProtocolTransaction::Moonlight(m) => {
97                state.serialize_field("type", "moonlight")?;
98
99                let sender = m.sender();
100                let sender = bs58::encode(sender.to_bytes()).into_string();
101                state.serialize_field("sender", &sender)?;
102
103                let receiver = m.receiver().map(|receiver| {
104                    bs58::encode(receiver.to_bytes()).into_string()
105                });
106                state.serialize_field("receiver", &receiver)?;
107
108                state.serialize_field("value", &m.value())?;
109
110                state.serialize_field("nonce", &m.nonce())?;
111            }
112        }
113
114        let tx = &self.inner;
115
116        state.serialize_field("deposit", &tx.deposit())?;
117
118        let notes: Vec<Note> = tx.outputs().iter().map(|n| n.into()).collect();
119
120        if !notes.is_empty() {
121            state.serialize_field("outputs", &notes)?;
122        }
123
124        let fee = {
125            let mut fee = HashMap::new();
126            fee.insert("gas_limit", tx.gas_limit().to_string());
127            fee.insert("gas_price", tx.gas_price().to_string());
128
129            let encoded_address = match tx.refund_address() {
130                RefundAddress::Phoenix(address) => {
131                    bs58::encode(address.to_bytes()).into_string()
132                }
133                RefundAddress::Moonlight(address) => {
134                    bs58::encode(address.to_bytes()).into_string()
135                }
136            };
137            fee.insert("refund_address", encoded_address);
138            if let ProtocolTransaction::Phoenix(tx) = tx {
139                fee.insert(
140                    "phoenix sender",
141                    hex::encode(tx.sender().to_bytes()),
142                );
143            }
144
145            fee
146        };
147
148        state.serialize_field("fee", &fee)?;
149
150        let call = tx.call().map(|c| {
151            let mut call = HashMap::new();
152            call.insert("contract", hex::encode(c.contract));
153            call.insert("fn_name", c.fn_name.to_string());
154            call.insert("fn_args", BASE64_ENGINE.encode(&c.fn_args));
155            call
156        });
157        state.serialize_field("call", &call)?;
158
159        state.serialize_field("is_deploy", &tx.deploy().is_some())?;
160        state.serialize_field("memo", &tx.memo().map(hex::encode))?;
161        state.end()
162    }
163}
164
165struct Note<'a>(&'a dusk_core::transfer::phoenix::Note);
166
167impl<'a> From<&'a dusk_core::transfer::phoenix::Note> for Note<'a> {
168    fn from(value: &'a dusk_core::transfer::phoenix::Note) -> Self {
169        Self(value)
170    }
171}
172
173impl Serialize for Note<'_> {
174    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
175    where
176        S: Serializer,
177    {
178        let mut state = serializer.serialize_struct("Note", 5)?;
179        let n = self.0;
180
181        state.serialize_field("type", &(n.note_type() as u8))?;
182
183        let commitment = [
184            hex::encode(n.value_commitment().get_u().to_bytes()),
185            hex::encode(n.value_commitment().get_v().to_bytes()),
186        ];
187        state.serialize_field("value_commitment", &commitment)?;
188
189        let stealth_address = n.stealth_address().to_bytes();
190        state.serialize_field(
191            "stealth_address",
192            &bs58::encode(stealth_address).into_string(),
193        )?;
194
195        state.serialize_field("value_enc", &hex::encode(n.value_enc()))?;
196        state.serialize_field("sender", &hex::encode(n.sender().to_bytes()))?;
197        state.end()
198    }
199}