1#[cfg(feature = "serde")]
11use serde_with::{serde_as, DisplayFromStr};
12
13use alloc::vec::Vec;
14
15use bytecheck::CheckBytes;
16use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
17use rkyv::{Archive, Deserialize, Serialize};
18
19use crate::signatures::bls::{
20 PublicKey as AccountPublicKey, SecretKey as AccountSecretKey,
21 Signature as AccountSignature,
22};
23use crate::transfer::data::{
24 BlobData, ContractBytecode, ContractCall, ContractDeploy, TransactionData,
25 MAX_MEMO_SIZE,
26};
27use crate::{BlsScalar, Error};
28
29#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
31#[archive_attr(derive(CheckBytes))]
32#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct AccountData {
35 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
37 pub nonce: u64,
38 #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
40 pub balance: u64,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
45#[archive_attr(derive(CheckBytes))]
46pub struct Transaction {
47 payload: Payload,
48 signature: AccountSignature,
49}
50
51impl Transaction {
52 #[allow(clippy::too_many_arguments)]
58 pub fn new(
59 sender_sk: &AccountSecretKey,
60 receiver: Option<AccountPublicKey>,
61 value: u64,
62 deposit: u64,
63 gas_limit: u64,
64 gas_price: u64,
65 nonce: u64,
66 chain_id: u8,
67 data: Option<impl Into<TransactionData>>,
68 ) -> Result<Self, Error> {
69 let refund_address = AccountPublicKey::from(sender_sk);
70
71 Self::new_with_refund(
72 sender_sk,
73 &refund_address,
74 receiver,
75 value,
76 deposit,
77 gas_limit,
78 gas_price,
79 nonce,
80 chain_id,
81 data,
82 )
83 }
84
85 #[allow(clippy::too_many_arguments)]
92 pub fn new_with_refund(
93 sender_sk: &AccountSecretKey,
94 refund_pk: &AccountPublicKey,
95 receiver: Option<AccountPublicKey>,
96 value: u64,
97 deposit: u64,
98 gas_limit: u64,
99 gas_price: u64,
100 nonce: u64,
101 chain_id: u8,
102 data: Option<impl Into<TransactionData>>,
103 ) -> Result<Self, Error> {
104 let data = data.map(Into::into);
105 let sender = AccountPublicKey::from(sender_sk);
106 let receiver = receiver.unwrap_or(sender);
107
108 let fee = Fee {
109 gas_limit,
110 gas_price,
111 refund_address: *refund_pk,
112 };
113
114 let payload = Payload {
115 chain_id,
116 sender,
117 receiver,
118 value,
119 deposit,
120 fee,
121 nonce,
122 data,
123 };
124
125 Self::sign_payload(sender_sk, payload)
126 }
127
128 pub fn sign_payload(
139 sender_sk: &AccountSecretKey,
140 payload: Payload,
141 ) -> Result<Self, Error> {
142 if let Some(TransactionData::Memo(memo)) = payload.data.as_ref() {
143 if memo.len() > MAX_MEMO_SIZE {
144 return Err(Error::MemoTooLarge(memo.len()));
145 }
146 }
147
148 let digest = payload.signature_message();
149 let signature = sender_sk.sign(&digest);
150
151 Ok(Self { payload, signature })
152 }
153
154 #[must_use]
156 pub fn signature(&self) -> &AccountSignature {
157 &self.signature
158 }
159
160 #[must_use]
162 pub fn sender(&self) -> &AccountPublicKey {
163 &self.payload.sender
164 }
165
166 #[must_use]
168 pub fn refund_address(&self) -> &AccountPublicKey {
169 &self.payload.fee.refund_address
170 }
171
172 #[must_use]
175 pub fn receiver(&self) -> Option<&AccountPublicKey> {
176 if self.payload.sender == self.payload.receiver {
177 None
178 } else {
179 Some(&self.payload.receiver)
180 }
181 }
182
183 #[must_use]
185 pub fn value(&self) -> u64 {
186 self.payload.value
187 }
188
189 #[must_use]
191 pub fn deposit(&self) -> u64 {
192 self.payload.deposit
193 }
194
195 #[must_use]
197 pub fn gas_limit(&self) -> u64 {
198 self.payload.fee.gas_limit
199 }
200
201 #[must_use]
203 pub fn gas_price(&self) -> u64 {
204 self.payload.fee.gas_price
205 }
206
207 #[must_use]
209 pub fn nonce(&self) -> u64 {
210 self.payload.nonce
211 }
212
213 #[must_use]
215 pub fn chain_id(&self) -> u8 {
216 self.payload.chain_id
217 }
218
219 #[must_use]
221 pub fn call(&self) -> Option<&ContractCall> {
222 #[allow(clippy::match_wildcard_for_single_variants)]
223 match self.data()? {
224 TransactionData::Call(ref c) => Some(c),
225 _ => None,
226 }
227 }
228
229 #[must_use]
231 pub fn deploy(&self) -> Option<&ContractDeploy> {
232 #[allow(clippy::match_wildcard_for_single_variants)]
233 match self.data()? {
234 TransactionData::Deploy(ref d) => Some(d),
235 _ => None,
236 }
237 }
238
239 #[must_use]
241 pub fn memo(&self) -> Option<&[u8]> {
242 match self.data()? {
243 TransactionData::Memo(memo) => Some(memo),
244 _ => None,
245 }
246 }
247
248 #[must_use]
250 pub fn blob(&self) -> Option<&Vec<BlobData>> {
251 #[allow(clippy::match_wildcard_for_single_variants)]
252 match self.data()? {
253 TransactionData::Blob(ref d) => Some(d),
254 _ => None,
255 }
256 }
257
258 #[must_use]
260 pub fn blob_mut(&mut self) -> Option<&mut Vec<BlobData>> {
261 #[allow(clippy::match_wildcard_for_single_variants)]
262 match self.data_mut()? {
263 TransactionData::Blob(d) => Some(d),
264 _ => None,
265 }
266 }
267
268 #[must_use]
270 pub fn data(&self) -> Option<&TransactionData> {
271 self.payload.data.as_ref()
272 }
273
274 #[must_use]
276 pub(crate) fn data_mut(&mut self) -> Option<&mut TransactionData> {
277 self.payload.data.as_mut()
278 }
279
280 #[must_use]
284 pub fn strip_off_bytecode(&self) -> Option<Self> {
285 let deploy = self.deploy()?;
286
287 let stripped_deploy = TransactionData::Deploy(ContractDeploy {
288 owner: deploy.owner.clone(),
289 init_args: deploy.init_args.clone(),
290 bytecode: ContractBytecode {
291 hash: deploy.bytecode.hash,
292 bytes: Vec::new(),
293 },
294 nonce: deploy.nonce,
295 });
296
297 let mut stripped_transaction = self.clone();
298 stripped_transaction.payload.data = Some(stripped_deploy);
299
300 Some(stripped_transaction)
301 }
302
303 #[must_use]
309 pub fn blob_to_memo(&self) -> Option<Self> {
310 let data = self.data()?;
311
312 if let TransactionData::Blob(_) = data {
313 let hash = data.signature_message();
314 let memo = TransactionData::Memo(hash);
315 let mut converted_tx = self.clone();
316 converted_tx.payload.data = Some(memo);
317 Some(converted_tx)
318 } else {
319 None
320 }
321 }
322
323 #[must_use]
325 pub fn to_var_bytes(&self) -> Vec<u8> {
326 let mut bytes = Vec::new();
327
328 let payload_bytes = self.payload.to_var_bytes();
329 bytes.extend((payload_bytes.len() as u64).to_bytes());
330 bytes.extend(payload_bytes);
331
332 bytes.extend(self.signature.to_bytes());
333
334 bytes
335 }
336
337 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
342 let mut buf = buf;
343
344 let payload_len = usize::try_from(u64::from_reader(&mut buf)?)
345 .map_err(|_| BytesError::InvalidData)?;
346
347 if buf.len() < payload_len {
348 return Err(BytesError::InvalidData);
349 }
350 let (payload_buf, new_buf) = buf.split_at(payload_len);
351
352 let payload = Payload::from_slice(payload_buf)?;
353 buf = new_buf;
354
355 let signature = AccountSignature::from_bytes(
356 buf.try_into().map_err(|_| BytesError::InvalidData)?,
357 )
358 .map_err(|_| BytesError::InvalidData)?;
359
360 Ok(Self { payload, signature })
361 }
362
363 #[must_use]
368 pub fn to_hash_input_bytes(&self) -> Vec<u8> {
369 let mut bytes = self.payload.signature_message();
370 bytes.extend(self.signature.to_bytes());
371 bytes
372 }
373
374 #[must_use]
377 pub fn signature_message(&self) -> Vec<u8> {
378 self.payload.signature_message()
379 }
380
381 #[must_use]
383 pub fn hash(&self) -> BlsScalar {
384 BlsScalar::hash_to_scalar(&self.to_hash_input_bytes())
385 }
386}
387
388#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
390#[archive_attr(derive(CheckBytes))]
391pub struct Payload {
392 pub chain_id: u8,
394 pub sender: AccountPublicKey,
396 pub receiver: AccountPublicKey,
398 pub value: u64,
400 pub deposit: u64,
402 pub fee: Fee,
404 pub nonce: u64,
410 pub data: Option<TransactionData>,
412}
413
414impl Payload {
415 #[must_use]
417 pub fn to_var_bytes(&self) -> Vec<u8> {
418 let mut bytes = Vec::from([self.chain_id]);
419
420 bytes.extend(self.sender.to_bytes());
421 if self.sender == self.receiver {
424 bytes.push(0);
425 } else {
426 bytes.push(1);
427 bytes.extend(self.receiver.to_bytes());
428 }
429
430 bytes.extend(self.value.to_bytes());
431 bytes.extend(self.deposit.to_bytes());
432
433 bytes.extend(self.fee.gas_limit.to_bytes());
435 bytes.extend(self.fee.gas_price.to_bytes());
436 if self.sender == self.fee.refund_address {
439 bytes.push(0);
440 } else {
441 bytes.push(1);
442 bytes.extend(self.fee.refund_address.to_bytes());
443 }
444
445 bytes.extend(self.nonce.to_bytes());
446
447 bytes.extend(TransactionData::option_to_var_bytes(self.data.as_ref()));
449
450 bytes
451 }
452
453 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
458 let mut buf = buf;
459
460 let chain_id = u8::from_reader(&mut buf)?;
461 let sender = AccountPublicKey::from_reader(&mut buf)?;
462 let receiver = match u8::from_reader(&mut buf)? {
463 0 => sender,
464 1 => AccountPublicKey::from_reader(&mut buf)?,
465 _ => {
466 return Err(BytesError::InvalidData);
467 }
468 };
469 let value = u64::from_reader(&mut buf)?;
470 let deposit = u64::from_reader(&mut buf)?;
471
472 let gas_limit = u64::from_reader(&mut buf)?;
474 let gas_price = u64::from_reader(&mut buf)?;
475 let refund_address = match u8::from_reader(&mut buf)? {
476 0 => sender,
477 1 => AccountPublicKey::from_reader(&mut buf)?,
478 _ => {
479 return Err(BytesError::InvalidData);
480 }
481 };
482 let fee = Fee {
483 gas_limit,
484 gas_price,
485 refund_address,
486 };
487
488 let nonce = u64::from_reader(&mut buf)?;
489
490 let data = TransactionData::from_slice(buf)?;
492
493 Ok(Self {
494 chain_id,
495 sender,
496 receiver,
497 value,
498 deposit,
499 fee,
500 nonce,
501 data,
502 })
503 }
504
505 #[must_use]
510 pub fn signature_message(&self) -> Vec<u8> {
511 let mut bytes = Vec::from([self.chain_id]);
512
513 bytes.extend(self.sender.to_bytes());
514 if self.receiver != self.sender {
515 bytes.extend(self.receiver.to_bytes());
516 }
517 bytes.extend(self.value.to_bytes());
518 bytes.extend(self.deposit.to_bytes());
519 bytes.extend(self.fee.gas_limit.to_bytes());
520 bytes.extend(self.fee.gas_price.to_bytes());
521 if self.fee.refund_address != self.sender {
522 bytes.extend(self.fee.refund_address.to_bytes());
523 }
524 bytes.extend(self.nonce.to_bytes());
525
526 if let Some(data) = &self.data {
527 bytes.extend(data.signature_message());
528 }
529
530 bytes
531 }
532}
533
534#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
536#[archive_attr(derive(CheckBytes))]
537pub struct Fee {
538 pub gas_limit: u64,
540 pub gas_price: u64,
542 pub refund_address: AccountPublicKey,
544}