dusk_node_data/ledger/
transaction.rs1use std::io;
8
9use dusk_bytes::Serializable as DuskSerializable;
10use dusk_core::signatures::bls::PublicKey as AccountPublicKey;
11use dusk_core::transfer::moonlight::Transaction as MoonlightTransaction;
12use dusk_core::transfer::phoenix::Transaction as PhoenixTransaction;
13use dusk_core::transfer::Transaction as ProtocolTransaction;
14use serde::Serialize;
15use sha3::Digest;
16
17use crate::Serializable;
18
19#[derive(Debug, Clone)]
20pub struct Transaction {
21 pub version: u32,
22 pub r#type: u32,
23 pub inner: ProtocolTransaction,
24 pub(crate) size: Option<usize>,
25}
26
27impl Transaction {
28 pub fn size(&self) -> io::Result<usize> {
29 match self.size {
30 Some(size) => Ok(size),
31 None => {
32 let mut buf = vec![];
33 self.write(&mut buf)?;
34 Ok(buf.len())
35 }
36 }
37 }
38}
39
40impl From<ProtocolTransaction> for Transaction {
41 fn from(value: ProtocolTransaction) -> Self {
42 Self {
43 inner: value,
44 r#type: 1,
45 version: 1,
46 size: None,
47 }
48 }
49}
50
51#[derive(Debug, Clone, Serialize)]
54pub struct SpentTransaction {
55 pub inner: Transaction,
57 pub block_height: u64,
59 pub gas_spent: u64,
62 pub err: Option<String>,
65}
66
67impl SpentTransaction {
68 pub fn public(&self) -> Option<&MoonlightTransaction> {
71 match &self.inner.inner {
72 ProtocolTransaction::Moonlight(public_tx) => Some(public_tx),
73 _ => None,
74 }
75 }
76
77 pub fn shielded(&self) -> Option<&PhoenixTransaction> {
80 match &self.inner.inner {
81 ProtocolTransaction::Phoenix(shielded_tx) => Some(shielded_tx),
82 _ => None,
83 }
84 }
85}
86
87impl Transaction {
88 pub fn digest(&self) -> [u8; 32] {
98 sha3::Sha3_256::digest(self.inner.to_var_bytes()).into()
99 }
100
101 pub fn id(&self) -> [u8; 32] {
112 self.inner.hash().to_bytes()
113 }
114
115 pub fn gas_price(&self) -> u64 {
116 self.inner.gas_price()
117 }
118
119 pub fn to_spend_ids(&self) -> Vec<SpendingId> {
120 match &self.inner {
121 ProtocolTransaction::Phoenix(p) => p
122 .nullifiers()
123 .iter()
124 .map(|n| SpendingId::Nullifier(n.to_bytes()))
125 .collect(),
126 ProtocolTransaction::Moonlight(m) => {
127 vec![SpendingId::AccountNonce(*m.sender(), m.nonce())]
128 }
129 }
130 }
131
132 pub fn next_spending_id(&self) -> Option<SpendingId> {
133 match &self.inner {
134 ProtocolTransaction::Phoenix(_) => None,
135 ProtocolTransaction::Moonlight(m) => {
136 Some(SpendingId::AccountNonce(*m.sender(), m.nonce() + 1))
137 }
138 }
139 }
140}
141
142impl PartialEq<Self> for Transaction {
143 fn eq(&self, other: &Self) -> bool {
144 self.r#type == other.r#type
145 && self.version == other.version
146 && self.id() == other.id()
147 }
148}
149
150impl Eq for Transaction {}
151
152impl PartialEq<Self> for SpentTransaction {
153 fn eq(&self, other: &Self) -> bool {
154 self.inner == other.inner && self.gas_spent == other.gas_spent
155 }
156}
157
158impl Eq for SpentTransaction {}
159
160pub enum SpendingId {
161 Nullifier([u8; 32]),
162 AccountNonce(AccountPublicKey, u64),
163}
164
165impl SpendingId {
166 pub fn to_bytes(&self) -> Vec<u8> {
167 match self {
168 SpendingId::Nullifier(n) => n.to_vec(),
169 SpendingId::AccountNonce(account, nonce) => {
170 let mut id = account.to_bytes().to_vec();
171 id.extend_from_slice(&nonce.to_le_bytes());
172 id
173 }
174 }
175 }
176
177 pub fn next(&self) -> Option<SpendingId> {
178 match self {
179 SpendingId::Nullifier(_) => None,
180 SpendingId::AccountNonce(account, nonce) => {
181 Some(SpendingId::AccountNonce(*account, nonce + 1))
182 }
183 }
184 }
185}
186
187#[cfg(any(feature = "faker", test))]
188pub mod faker {
189 use dusk_core::transfer::data::{ContractCall, TransactionData};
190 use dusk_core::transfer::phoenix::{
191 Fee, Note, Payload as PhoenixPayload, PublicKey as PhoenixPublicKey,
192 SecretKey as PhoenixSecretKey, Transaction as PhoenixTransaction,
193 TxSkeleton,
194 };
195 use dusk_core::{BlsScalar, JubJubScalar};
196 use rand::Rng;
197
198 use super::*;
199 use crate::ledger::Dummy;
200
201 impl<T> Dummy<T> for Transaction {
202 fn dummy_with_rng<R: Rng + ?Sized>(_config: &T, _rng: &mut R) -> Self {
203 gen_dummy_tx(1_000_000)
204 }
205 }
206
207 impl<T> Dummy<T> for SpentTransaction {
208 fn dummy_with_rng<R: Rng + ?Sized>(_config: &T, _rng: &mut R) -> Self {
209 let tx = gen_dummy_tx(1_000_000);
210 SpentTransaction {
211 inner: tx,
212 block_height: 0,
213 gas_spent: 3,
214 err: Some("error".to_string()),
215 }
216 }
217 }
218
219 pub fn gen_dummy_tx(gas_price: u64) -> Transaction {
222 let pk = PhoenixPublicKey::from(&PhoenixSecretKey::new(
223 JubJubScalar::from(42u64),
224 JubJubScalar::from(42u64),
225 ));
226 let gas_limit = 1;
227
228 let fee = Fee::deterministic(
229 &JubJubScalar::from(5u64),
230 &pk,
231 gas_limit,
232 gas_price,
233 &[JubJubScalar::from(9u64), JubJubScalar::from(10u64)],
234 );
235
236 let tx_skeleton = TxSkeleton {
237 root: BlsScalar::from(12345u64),
238 nullifiers: vec![
239 BlsScalar::from(1u64),
240 BlsScalar::from(2u64),
241 BlsScalar::from(3u64),
242 ],
243 outputs: [Note::empty(), Note::empty()],
244 max_fee: gas_price * gas_limit,
245 deposit: 0,
246 };
247
248 let contract_call =
249 ContractCall::new([21; 32], "some_method", &()).unwrap();
250
251 let payload = PhoenixPayload {
252 chain_id: 0xFA,
253 tx_skeleton,
254 fee,
255 data: Some(TransactionData::Call(contract_call)),
256 };
257 let proof = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
258
259 let tx: ProtocolTransaction =
260 PhoenixTransaction::from_payload_and_proof(payload, proof).into();
261
262 tx.into()
263 }
264}