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))]
286pub struct ContractCall {
287 pub contract: ContractId,
289 pub fn_name: String,
291 pub fn_args: Vec<u8>,
293}
294
295impl ContractDeploy {
296 #[must_use]
298 pub fn to_var_bytes(&self) -> Vec<u8> {
299 let mut bytes = Vec::new();
300
301 bytes.extend(&self.bytecode.to_var_bytes());
302
303 bytes.extend((self.owner.len() as u64).to_bytes());
304 bytes.extend(&self.owner);
305
306 match &self.init_args {
307 Some(init_args) => {
308 bytes.push(1);
309 bytes.extend((init_args.len() as u64).to_bytes());
310 bytes.extend(init_args);
311 }
312 None => bytes.push(0),
313 }
314
315 bytes.extend(self.nonce.to_bytes());
316
317 bytes
318 }
319
320 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
325 let mut buf = buf;
326
327 let bytecode = ContractBytecode::from_buf(&mut buf)?;
328
329 let owner = crate::read_vec(&mut buf)?;
330
331 let init_args = match u8::from_reader(&mut buf)? {
332 0 => None,
333 1 => Some(crate::read_vec(&mut buf)?),
334 _ => return Err(BytesError::InvalidData),
335 };
336
337 let nonce = u64::from_reader(&mut buf)?;
338
339 Ok(Self {
340 bytecode,
341 owner,
342 init_args,
343 nonce,
344 })
345 }
346}
347
348impl ContractCall {
349 pub fn new(
360 contract: impl Into<ContractId>,
361 fn_name: impl Into<String>,
362 ) -> Self {
363 Self {
364 contract: contract.into(),
365 fn_name: fn_name.into(),
366 fn_args: vec![],
367 }
368 }
369
370 #[must_use]
379 pub fn with_raw_args(mut self, fn_args: Vec<u8>) -> Self {
380 self.fn_args = fn_args;
381 self
382 }
383
384 pub fn with_args<A>(self, fn_arg: &A) -> Result<Self, Error>
401 where
402 A: for<'b> Serialize<StandardBufSerializer<'b>>,
403 A::Archived: for<'b> CheckBytes<DefaultValidator<'b>>,
404 {
405 const SCRATCH_SPACE: usize = 1024;
407 const PAGE_SIZE: usize = 0x1000;
408
409 let mut sbuf = [0u8; SCRATCH_SPACE];
410 let scratch = BufferScratch::new(&mut sbuf);
411 let mut buffer = [0u8; PAGE_SIZE];
412 let ser = BufferSerializer::new(&mut buffer[..]);
413 let mut ser = CompositeSerializer::new(ser, scratch, Infallible);
414
415 ser.serialize_value(fn_arg)
416 .map_err(|e| Error::Rkyv(format!("{e:?}")))?;
417 let pos = ser.pos();
418
419 let fn_args = buffer[..pos].to_vec();
420
421 Ok(self.with_raw_args(fn_args))
422 }
423
424 #[must_use]
426 pub fn to_var_bytes(&self) -> Vec<u8> {
427 let mut bytes = Vec::new();
428
429 bytes.extend(self.contract.as_bytes());
430
431 let fn_name_bytes = self.fn_name.as_bytes();
432 bytes.extend((fn_name_bytes.len() as u64).to_bytes());
433 bytes.extend(fn_name_bytes);
434
435 bytes.extend((self.fn_args.len() as u64).to_bytes());
436 bytes.extend(&self.fn_args);
437
438 bytes
439 }
440
441 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
446 let mut buf = buf;
447
448 let contract = crate::read_arr::<32>(&mut buf)?;
449
450 let fn_name = crate::read_str(&mut buf)?;
451
452 let fn_args = crate::read_vec(&mut buf)?;
453
454 Ok(Self {
455 contract: contract.into(),
456 fn_name,
457 fn_args,
458 })
459 }
460}
461
462#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
463#[archive_attr(derive(CheckBytes))]
464pub struct ContractBytecode {
466 pub hash: [u8; 32],
468 pub bytes: Vec<u8>,
470}
471
472impl ContractBytecode {
473 #[must_use]
475 pub fn to_hash_input_bytes(&self) -> Vec<u8> {
476 self.hash.to_vec()
477 }
478
479 #[must_use]
481 pub fn to_var_bytes(&self) -> Vec<u8> {
482 let mut bytes = Vec::new();
483 bytes.extend(self.hash);
484 bytes.extend((self.bytes.len() as u64).to_bytes());
485 bytes.extend(&self.bytes);
486 bytes
487 }
488
489 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
495 let hash = crate::read_arr::<32>(buf)?;
496 let bytes = crate::read_vec(buf)?;
497 Ok(Self { hash, bytes })
498 }
499}
500
501impl BlobData {
502 pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
504
505 #[must_use]
507 pub fn to_hash_input_bytes(&self) -> Vec<u8> {
508 self.hash.to_vec()
509 }
510
511 #[must_use]
513 pub fn to_var_bytes(&self) -> Vec<u8> {
514 let mut bytes = Vec::new();
515 bytes.extend(self.hash);
516 if let Some(data) = &self.data {
517 bytes.push(1u8);
518 bytes.extend(data.to_var_bytes());
519 } else {
520 bytes.push(0u8);
521 }
522 bytes
523 }
524
525 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
531 let hash = crate::read_arr(buf)?;
532
533 let data = match u8::from_reader(buf)? {
534 0 => None,
535 1 => Some(BlobSidecar::from_buf(buf)?),
536 _ => return Err(BytesError::InvalidData),
537 };
538
539 Ok(Self { hash, data })
540 }
541
542 #[must_use]
544 pub fn take_sidecar(&mut self) -> Option<BlobSidecar> {
545 self.data.take()
546 }
547
548 #[must_use]
552 pub fn hash_from_commitment(commitment: &[u8]) -> [u8; 32] {
553 let digest = Sha256::digest(commitment);
554 let mut out = [0u8; 32];
555 out[0] = Self::VERSIONED_HASH_VERSION_KZG;
556 out[1..].copy_from_slice(&digest[1..]);
557 out
558 }
559}
560
561impl BlobSidecar {
562 #[must_use]
564 pub fn to_var_bytes(&self) -> Vec<u8> {
565 let mut bytes = Vec::new();
566 bytes.extend(self.commitment);
567 bytes.extend(self.proof);
568 bytes.extend(self.data);
569 bytes
570 }
571
572 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
578 let commitment = crate::read_arr(buf)?;
579 let proof = crate::read_arr(buf)?;
580 let data = crate::read_arr(buf)?;
581
582 Ok(Self {
583 commitment,
584 proof,
585 data,
586 })
587 }
588}