1#[cfg(feature = "kzg")]
11mod kzg_blob;
12
13#[cfg(feature = "serde")]
14use serde_with::{hex::Hex, serde_as};
15
16use alloc::string::String;
17use alloc::vec::Vec;
18use alloc::{format, vec};
19
20use bytecheck::CheckBytes;
21use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
22use piecrust_uplink::StandardBufSerializer;
23use rkyv::ser::serializers::{
24 BufferScratch, BufferSerializer, CompositeSerializer,
25};
26use rkyv::ser::Serializer;
27use rkyv::validation::validators::DefaultValidator;
28use rkyv::{Archive, Deserialize, Infallible, Serialize};
29use sha2::{Digest, Sha256};
30
31use crate::abi::ContractId;
32use crate::Error;
33
34pub const MAX_MEMO_SIZE: usize = 512;
36
37#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
39#[archive_attr(derive(CheckBytes))]
40#[allow(clippy::large_enum_variant)]
41pub enum TransactionData {
42 Call(ContractCall),
44 Deploy(ContractDeploy),
46 Memo(Vec<u8>),
49 Blob(Vec<BlobData>),
51}
52
53impl TransactionData {
54 const NONE_ID: u8 = 0x00;
55 const CALL_ID: u8 = 0x01;
56 const DEPLOY_ID: u8 = 0x02;
57 const MEMO_ID: u8 = 0x03;
58 const BLOB_ID: u8 = 0x04;
59
60 #[must_use]
65 pub fn signature_message(&self) -> Vec<u8> {
66 let mut bytes = vec![];
67
68 #[allow(clippy::match_same_arms)]
69 match &self {
70 TransactionData::Deploy(d) => {
71 bytes.extend(&d.bytecode.to_hash_input_bytes());
72 bytes.extend(&d.owner);
73 if let Some(init_args) = &d.init_args {
74 bytes.extend(init_args);
75 }
76 }
77 TransactionData::Call(c) => {
78 bytes.extend(c.contract.as_bytes());
79 bytes.extend(c.fn_name.as_bytes());
80 bytes.extend(&c.fn_args);
81 }
82 TransactionData::Memo(m) => {
83 bytes.extend(m);
84 }
85 TransactionData::Blob(blobs) => {
86 for blob in blobs {
95 bytes.extend(blob.to_hash_input_bytes());
96 }
97 }
98 }
99
100 bytes
101 }
102
103 #[must_use]
105 pub fn to_var_bytes(&self) -> Vec<u8> {
106 let mut bytes = Vec::new();
107 match &self {
109 TransactionData::Call(call) => {
110 bytes.push(Self::CALL_ID);
111 bytes.extend(call.to_var_bytes());
112 }
113 TransactionData::Deploy(deploy) => {
114 bytes.push(Self::DEPLOY_ID);
115 bytes.extend(deploy.to_var_bytes());
116 }
117 TransactionData::Memo(memo) => {
118 bytes.push(Self::MEMO_ID);
119 bytes.extend((memo.len() as u64).to_bytes());
120 bytes.extend(memo);
121 }
122 TransactionData::Blob(blobs) => {
123 bytes.push(Self::BLOB_ID);
124 #[allow(clippy::cast_possible_truncation)]
128 bytes.extend((blobs.len() as u8).to_bytes());
129 for blob in blobs {
130 bytes.extend(blob.to_var_bytes());
131 }
132 }
133 }
134
135 bytes
136 }
137
138 #[must_use]
141 pub fn option_to_var_bytes(data: Option<&TransactionData>) -> Vec<u8> {
142 let mut bytes = Vec::new();
143 if let Some(data) = data {
144 bytes.extend(data.to_var_bytes());
145 } else {
146 bytes.push(Self::NONE_ID);
147 }
148 bytes
149 }
150
151 pub fn from_slice(buf: &[u8]) -> Result<Option<Self>, BytesError> {
156 let mut buf = buf;
157
158 let data = match u8::from_reader(&mut buf)? {
160 Self::NONE_ID => None,
161 Self::CALL_ID => {
162 Some(TransactionData::Call(ContractCall::from_slice(buf)?))
163 }
164 Self::DEPLOY_ID => {
165 Some(TransactionData::Deploy(ContractDeploy::from_slice(buf)?))
166 }
167 Self::MEMO_ID => {
168 #[allow(clippy::cast_possible_truncation)]
170 let size = u64::from_reader(&mut buf)? as usize;
171
172 if buf.len() != size || size > MAX_MEMO_SIZE {
173 return Err(BytesError::InvalidData);
174 }
175
176 let memo = buf[..size].to_vec();
177 Some(TransactionData::Memo(memo))
178 }
179 Self::BLOB_ID => {
180 let blobs_len = u8::from_reader(&mut buf)?;
181 let mut blobs = Vec::with_capacity(blobs_len as usize);
182 for _ in 0..blobs_len {
183 let blob = BlobData::from_buf(&mut buf)?;
184 blobs.push(blob);
185 }
186 Some(TransactionData::Blob(blobs))
187 }
188 _ => {
189 return Err(BytesError::InvalidData);
190 }
191 };
192
193 Ok(data)
194 }
195}
196
197impl From<ContractCall> for TransactionData {
198 fn from(c: ContractCall) -> Self {
199 TransactionData::Call(c)
200 }
201}
202
203impl From<ContractDeploy> for TransactionData {
204 fn from(d: ContractDeploy) -> Self {
205 TransactionData::Deploy(d)
206 }
207}
208
209impl From<Vec<u8>> for TransactionData {
210 fn from(d: Vec<u8>) -> Self {
211 TransactionData::Memo(d)
212 }
213}
214
215impl From<String> for TransactionData {
216 fn from(d: String) -> Self {
217 TransactionData::Memo(d.as_bytes().to_vec())
218 }
219}
220
221impl From<Vec<BlobData>> for TransactionData {
222 fn from(blobs: Vec<BlobData>) -> Self {
223 TransactionData::Blob(blobs)
224 }
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
229#[archive_attr(derive(CheckBytes))]
230pub struct ContractDeploy {
231 pub bytecode: ContractBytecode,
233 pub owner: Vec<u8>,
235 pub init_args: Option<Vec<u8>>,
237 pub nonce: u64,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
244#[archive_attr(derive(CheckBytes))]
245#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
246#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
247pub struct BlobData {
248 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
250 pub hash: [u8; 32],
251
252 pub data: Option<BlobSidecar>,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
261#[archive_attr(derive(CheckBytes))]
262#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
263#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
264pub struct BlobSidecar {
265 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
267 pub commitment: [u8; 48],
268
269 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
271 pub proof: [u8; 48],
272
273 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
275 pub data: BlobDataPart,
276}
277
278const BYTES_PER_BLOB: usize = 4096 * 32;
279pub type BlobDataPart = [u8; BYTES_PER_BLOB];
282
283#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
285#[archive_attr(derive(CheckBytes))]
286#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
287#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
288pub struct ContractCall {
289 pub contract: ContractId,
291 pub fn_name: String,
293 #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
295 pub fn_args: Vec<u8>,
296}
297
298impl ContractDeploy {
299 #[must_use]
301 pub fn to_var_bytes(&self) -> Vec<u8> {
302 let mut bytes = Vec::new();
303
304 bytes.extend(&self.bytecode.to_var_bytes());
305
306 bytes.extend((self.owner.len() as u64).to_bytes());
307 bytes.extend(&self.owner);
308
309 match &self.init_args {
310 Some(init_args) => {
311 bytes.push(1);
312 bytes.extend((init_args.len() as u64).to_bytes());
313 bytes.extend(init_args);
314 }
315 None => bytes.push(0),
316 }
317
318 bytes.extend(self.nonce.to_bytes());
319
320 bytes
321 }
322
323 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
328 let mut buf = buf;
329
330 let bytecode = ContractBytecode::from_buf(&mut buf)?;
331
332 let owner = crate::read_vec(&mut buf)?;
333
334 let init_args = match u8::from_reader(&mut buf)? {
335 0 => None,
336 1 => Some(crate::read_vec(&mut buf)?),
337 _ => return Err(BytesError::InvalidData),
338 };
339
340 let nonce = u64::from_reader(&mut buf)?;
341
342 Ok(Self {
343 bytecode,
344 owner,
345 init_args,
346 nonce,
347 })
348 }
349}
350
351impl ContractCall {
352 pub fn new(
363 contract: impl Into<ContractId>,
364 fn_name: impl Into<String>,
365 ) -> Self {
366 Self {
367 contract: contract.into(),
368 fn_name: fn_name.into(),
369 fn_args: vec![],
370 }
371 }
372
373 #[must_use]
382 pub fn with_raw_args(mut self, fn_args: Vec<u8>) -> Self {
383 self.fn_args = fn_args;
384 self
385 }
386
387 pub fn with_args<A>(self, fn_arg: &A) -> Result<Self, Error>
404 where
405 A: for<'b> Serialize<StandardBufSerializer<'b>>,
406 A::Archived: for<'b> CheckBytes<DefaultValidator<'b>>,
407 {
408 const SCRATCH_SPACE: usize = 1024;
410 const PAGE_SIZE: usize = 0x1000;
411
412 let mut sbuf = [0u8; SCRATCH_SPACE];
413 let scratch = BufferScratch::new(&mut sbuf);
414 let mut buffer = [0u8; PAGE_SIZE];
415 let ser = BufferSerializer::new(&mut buffer[..]);
416 let mut ser = CompositeSerializer::new(ser, scratch, Infallible);
417
418 ser.serialize_value(fn_arg)
419 .map_err(|e| Error::Rkyv(format!("{e:?}")))?;
420 let pos = ser.pos();
421
422 let fn_args = buffer[..pos].to_vec();
423
424 Ok(self.with_raw_args(fn_args))
425 }
426
427 #[must_use]
429 pub fn to_var_bytes(&self) -> Vec<u8> {
430 let mut bytes = Vec::new();
431
432 bytes.extend(self.contract.as_bytes());
433
434 let fn_name_bytes = self.fn_name.as_bytes();
435 bytes.extend((fn_name_bytes.len() as u64).to_bytes());
436 bytes.extend(fn_name_bytes);
437
438 bytes.extend((self.fn_args.len() as u64).to_bytes());
439 bytes.extend(&self.fn_args);
440
441 bytes
442 }
443
444 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
449 let mut buf = buf;
450
451 let contract = crate::read_arr::<32>(&mut buf)?;
452
453 let fn_name = crate::read_str(&mut buf)?;
454
455 let fn_args = crate::read_vec(&mut buf)?;
456
457 Ok(Self {
458 contract: contract.into(),
459 fn_name,
460 fn_args,
461 })
462 }
463}
464
465#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
466#[archive_attr(derive(CheckBytes))]
467pub struct ContractBytecode {
469 pub hash: [u8; 32],
471 pub bytes: Vec<u8>,
473}
474
475impl ContractBytecode {
476 #[must_use]
478 pub fn to_hash_input_bytes(&self) -> Vec<u8> {
479 self.hash.to_vec()
480 }
481
482 #[must_use]
484 pub fn to_var_bytes(&self) -> Vec<u8> {
485 let mut bytes = Vec::new();
486 bytes.extend(self.hash);
487 bytes.extend((self.bytes.len() as u64).to_bytes());
488 bytes.extend(&self.bytes);
489 bytes
490 }
491
492 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
498 let hash = crate::read_arr::<32>(buf)?;
499 let bytes = crate::read_vec(buf)?;
500 Ok(Self { hash, bytes })
501 }
502}
503
504impl BlobData {
505 pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
507
508 #[must_use]
510 pub fn to_hash_input_bytes(&self) -> Vec<u8> {
511 self.hash.to_vec()
512 }
513
514 #[must_use]
516 pub fn to_var_bytes(&self) -> Vec<u8> {
517 let mut bytes = Vec::new();
518 bytes.extend(self.hash);
519 if let Some(data) = &self.data {
520 bytes.push(1u8);
521 bytes.extend(data.to_var_bytes());
522 } else {
523 bytes.push(0u8);
524 }
525 bytes
526 }
527
528 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
534 let hash = crate::read_arr(buf)?;
535
536 let data = match u8::from_reader(buf)? {
537 0 => None,
538 1 => Some(BlobSidecar::from_buf(buf)?),
539 _ => return Err(BytesError::InvalidData),
540 };
541
542 Ok(Self { hash, data })
543 }
544
545 #[must_use]
547 pub fn take_sidecar(&mut self) -> Option<BlobSidecar> {
548 self.data.take()
549 }
550
551 #[must_use]
555 pub fn hash_from_commitment(commitment: &[u8]) -> [u8; 32] {
556 let digest = Sha256::digest(commitment);
557 let mut out = [0u8; 32];
558 out[0] = Self::VERSIONED_HASH_VERSION_KZG;
559 out[1..].copy_from_slice(&digest[1..]);
560 out
561 }
562}
563
564impl BlobSidecar {
565 #[must_use]
567 pub fn to_var_bytes(&self) -> Vec<u8> {
568 let mut bytes = Vec::new();
569 bytes.extend(self.commitment);
570 bytes.extend(self.proof);
571 bytes.extend(self.data);
572 bytes
573 }
574
575 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
581 let commitment = crate::read_arr(buf)?;
582 let proof = crate::read_arr(buf)?;
583 let data = crate::read_arr(buf)?;
584
585 Ok(Self {
586 commitment,
587 proof,
588 data,
589 })
590 }
591}