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