1#[cfg(feature = "kzg")]
11mod kzg_blob;
12
13use alloc::string::String;
14use alloc::vec::Vec;
15use alloc::{format, vec};
16
17use blake2b_simd::Params;
18use bytecheck::CheckBytes;
19use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
20use piecrust_uplink::StandardBufSerializer;
21use rkyv::ser::Serializer;
22use rkyv::ser::serializers::{
23 BufferScratch, BufferSerializer, CompositeSerializer,
24};
25use rkyv::validation::validators::DefaultValidator;
26use rkyv::{Archive, Deserialize, Infallible, Serialize};
27#[cfg(feature = "serde")]
28use serde_with::As;
29#[cfg(feature = "serde")]
30use serde_with::hex::Hex;
31use sha2::{Digest, Sha256};
32
33use crate::Error;
34use crate::abi::{CONTRACT_ID_BYTES, ContractId};
35
36pub const MAX_MEMO_SIZE: usize = 512;
38
39#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
41#[archive_attr(derive(CheckBytes))]
42#[allow(clippy::large_enum_variant)]
43pub enum TransactionData {
44 Call(ContractCall),
46 Deploy(ContractDeploy),
48 Memo(Vec<u8>),
51 Blob(Vec<BlobData>),
53}
54
55impl TransactionData {
56 const NONE_ID: u8 = 0x00;
57 const CALL_ID: u8 = 0x01;
58 const DEPLOY_ID: u8 = 0x02;
59 const MEMO_ID: u8 = 0x03;
60 const BLOB_ID: u8 = 0x04;
61
62 #[must_use]
67 pub fn signature_message(&self) -> Vec<u8> {
68 let mut bytes = vec![];
69
70 #[allow(clippy::match_same_arms)]
71 match &self {
72 TransactionData::Deploy(d) => {
73 bytes.extend(&d.bytecode.to_hash_input_bytes());
74 bytes.extend(&d.owner);
75 if let Some(init_args) = &d.init_args {
76 bytes.extend(init_args);
77 }
78 }
79 TransactionData::Call(c) => {
80 bytes.extend(c.contract.as_bytes());
81 bytes.extend(c.fn_name.as_bytes());
82 bytes.extend(&c.fn_args);
83 }
84 TransactionData::Memo(m) => {
85 bytes.extend(m);
86 }
87 TransactionData::Blob(blobs) => {
88 for blob in blobs {
97 bytes.extend(blob.to_hash_input_bytes());
98 }
99 }
100 }
101
102 bytes
103 }
104
105 #[must_use]
107 pub fn to_var_bytes(&self) -> Vec<u8> {
108 let mut bytes = Vec::new();
109 match &self {
111 TransactionData::Call(call) => {
112 bytes.push(Self::CALL_ID);
113 bytes.extend(call.to_var_bytes());
114 }
115 TransactionData::Deploy(deploy) => {
116 bytes.push(Self::DEPLOY_ID);
117 bytes.extend(deploy.to_var_bytes());
118 }
119 TransactionData::Memo(memo) => {
120 bytes.push(Self::MEMO_ID);
121 bytes.extend((memo.len() as u64).to_bytes());
122 bytes.extend(memo);
123 }
124 TransactionData::Blob(blobs) => {
125 bytes.push(Self::BLOB_ID);
126 #[allow(clippy::cast_possible_truncation)]
130 bytes.extend((blobs.len() as u8).to_bytes());
131 for blob in blobs {
132 bytes.extend(blob.to_var_bytes());
133 }
134 }
135 }
136
137 bytes
138 }
139
140 #[must_use]
143 pub fn option_to_var_bytes(data: Option<&TransactionData>) -> Vec<u8> {
144 let mut bytes = Vec::new();
145 if let Some(data) = data {
146 bytes.extend(data.to_var_bytes());
147 } else {
148 bytes.push(Self::NONE_ID);
149 }
150 bytes
151 }
152
153 pub fn from_slice(buf: &[u8]) -> Result<Option<Self>, BytesError> {
158 let mut buf = buf;
159
160 match u8::from_reader(&mut buf)? {
162 Self::NONE_ID => {
163 if !buf.is_empty() {
164 return Err(BytesError::InvalidData);
165 }
166 Ok(None)
167 }
168 Self::CALL_ID => {
169 let call = ContractCall::from_slice(buf)?;
170 Ok(Some(TransactionData::Call(call)))
171 }
172 Self::DEPLOY_ID => {
173 let deploy = ContractDeploy::from_slice(buf)?;
174 Ok(Some(TransactionData::Deploy(deploy)))
175 }
176 Self::MEMO_ID => {
177 #[allow(clippy::cast_possible_truncation)]
179 let size = u64::from_reader(&mut buf)? as usize;
180
181 if buf.len() != size || size > MAX_MEMO_SIZE {
182 return Err(BytesError::InvalidData);
183 }
184
185 let memo = buf[..size].to_vec();
186 Ok(Some(TransactionData::Memo(memo)))
187 }
188 Self::BLOB_ID => {
189 let blobs_len = u8::from_reader(&mut buf)?;
190 let mut blobs = Vec::with_capacity(blobs_len as usize);
191 for _ in 0..blobs_len {
192 let blob = BlobData::from_buf(&mut buf)?;
193 blobs.push(blob);
194 }
195
196 if !buf.is_empty() {
197 return Err(BytesError::InvalidData);
198 }
199
200 Ok(Some(TransactionData::Blob(blobs)))
201 }
202 _ => Err(BytesError::InvalidData),
203 }
204 }
205}
206
207impl From<ContractCall> for TransactionData {
208 fn from(c: ContractCall) -> Self {
209 TransactionData::Call(c)
210 }
211}
212
213impl From<ContractDeploy> for TransactionData {
214 fn from(d: ContractDeploy) -> Self {
215 TransactionData::Deploy(d)
216 }
217}
218
219impl From<Vec<u8>> for TransactionData {
220 fn from(d: Vec<u8>) -> Self {
221 TransactionData::Memo(d)
222 }
223}
224
225impl From<String> for TransactionData {
226 fn from(d: String) -> Self {
227 TransactionData::Memo(d.as_bytes().to_vec())
228 }
229}
230
231impl From<Vec<BlobData>> for TransactionData {
232 fn from(blobs: Vec<BlobData>) -> Self {
233 TransactionData::Blob(blobs)
234 }
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
239#[archive_attr(derive(CheckBytes))]
240pub struct ContractDeploy {
241 pub bytecode: ContractBytecode,
243 pub owner: Vec<u8>,
245 pub init_args: Option<Vec<u8>>,
247 pub nonce: u64,
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
254#[archive_attr(derive(CheckBytes))]
255#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
256pub struct BlobData {
257 #[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
259 pub hash: [u8; 32],
260
261 pub data: Option<BlobSidecar>,
265}
266
267#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
270#[archive_attr(derive(CheckBytes))]
271#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
272pub struct BlobSidecar {
273 #[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
275 pub commitment: [u8; 48],
276
277 #[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
279 pub proof: [u8; 48],
280
281 #[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
283 pub data: BlobDataPart,
284}
285
286const BYTES_PER_BLOB: usize = 4096 * 32;
287pub type BlobDataPart = [u8; BYTES_PER_BLOB];
290
291#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
293#[archive_attr(derive(CheckBytes))]
294#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
295pub struct ContractCall {
296 pub contract: ContractId,
298 pub fn_name: String,
300 #[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
302 pub fn_args: Vec<u8>,
303}
304
305impl ContractDeploy {
306 #[must_use]
308 pub fn new(
309 bytecode: impl Into<Vec<u8>>,
310 owner: impl Into<Vec<u8>>,
311 init_args: Option<Vec<u8>>,
312 nonce: u64,
313 ) -> Self {
314 Self {
315 bytecode: ContractBytecode::new(bytecode),
316 owner: owner.into(),
317 init_args,
318 nonce,
319 }
320 }
321
322 #[must_use]
324 pub fn contract_id(&self) -> ContractId {
325 gen_contract_id(&self.bytecode.bytes, self.nonce, &self.owner)
326 }
327
328 #[must_use]
330 pub fn to_var_bytes(&self) -> Vec<u8> {
331 let mut bytes = Vec::new();
332
333 bytes.extend(&self.bytecode.to_var_bytes());
334
335 bytes.extend((self.owner.len() as u64).to_bytes());
336 bytes.extend(&self.owner);
337
338 match &self.init_args {
339 Some(init_args) => {
340 bytes.push(1);
341 bytes.extend((init_args.len() as u64).to_bytes());
342 bytes.extend(init_args);
343 }
344 None => bytes.push(0),
345 }
346
347 bytes.extend(self.nonce.to_bytes());
348
349 bytes
350 }
351
352 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
357 let mut buf = buf;
358
359 let bytecode = ContractBytecode::from_buf(&mut buf)?;
360
361 let owner = crate::read_vec(&mut buf)?;
362
363 let init_args = match u8::from_reader(&mut buf)? {
364 0 => None,
365 1 => Some(crate::read_vec(&mut buf)?),
366 _ => return Err(BytesError::InvalidData),
367 };
368
369 let nonce = u64::from_reader(&mut buf)?;
370
371 if !buf.is_empty() {
372 return Err(BytesError::InvalidData);
373 }
374
375 Ok(Self {
376 bytecode,
377 owner,
378 init_args,
379 nonce,
380 })
381 }
382}
383
384impl ContractCall {
385 pub fn new(
396 contract: impl Into<ContractId>,
397 fn_name: impl Into<String>,
398 ) -> Self {
399 Self {
400 contract: contract.into(),
401 fn_name: fn_name.into(),
402 fn_args: vec![],
403 }
404 }
405
406 #[must_use]
415 pub fn with_raw_args(mut self, fn_args: Vec<u8>) -> Self {
416 self.fn_args = fn_args;
417 self
418 }
419
420 pub fn with_args<A>(self, fn_arg: &A) -> Result<Self, Error>
437 where
438 A: for<'b> Serialize<StandardBufSerializer<'b>>,
439 A::Archived: for<'b> CheckBytes<DefaultValidator<'b>>,
440 {
441 const SCRATCH_SPACE: usize = 1024;
443 const PAGE_SIZE: usize = 0x1000;
444
445 let mut sbuf = [0u8; SCRATCH_SPACE];
446 let scratch = BufferScratch::new(&mut sbuf);
447 let mut buffer = [0u8; PAGE_SIZE];
448 let ser = BufferSerializer::new(&mut buffer[..]);
449 let mut ser = CompositeSerializer::new(ser, scratch, Infallible);
450
451 ser.serialize_value(fn_arg)
452 .map_err(|e| Error::Rkyv(format!("{e:?}")))?;
453 let pos = ser.pos();
454
455 let fn_args = buffer[..pos].to_vec();
456
457 Ok(self.with_raw_args(fn_args))
458 }
459
460 #[must_use]
462 pub fn to_var_bytes(&self) -> Vec<u8> {
463 let mut bytes = Vec::new();
464
465 bytes.extend(self.contract.as_bytes());
466
467 let fn_name_bytes = self.fn_name.as_bytes();
468 bytes.extend((fn_name_bytes.len() as u64).to_bytes());
469 bytes.extend(fn_name_bytes);
470
471 bytes.extend((self.fn_args.len() as u64).to_bytes());
472 bytes.extend(&self.fn_args);
473
474 bytes
475 }
476
477 pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
482 let mut buf = buf;
483
484 let contract = crate::read_arr::<32>(&mut buf)?;
485
486 let fn_name = crate::read_str(&mut buf)?;
487
488 let fn_args = crate::read_vec(&mut buf)?;
489
490 if !buf.is_empty() {
491 return Err(BytesError::InvalidData);
492 }
493
494 Ok(Self {
495 contract: contract.into(),
496 fn_name,
497 fn_args,
498 })
499 }
500}
501
502#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
503#[archive_attr(derive(CheckBytes))]
504pub struct ContractBytecode {
506 pub hash: [u8; 32],
508 pub bytes: Vec<u8>,
510}
511
512impl ContractBytecode {
513 #[must_use]
515 pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
516 let bytes = bytes.into();
517
518 Self {
519 hash: blake3::hash(&bytes).into(),
520 bytes,
521 }
522 }
523
524 #[must_use]
526 pub fn to_hash_input_bytes(&self) -> Vec<u8> {
527 self.hash.to_vec()
528 }
529
530 #[must_use]
532 pub fn to_var_bytes(&self) -> Vec<u8> {
533 let mut bytes = Vec::new();
534 bytes.extend(self.hash);
535 bytes.extend((self.bytes.len() as u64).to_bytes());
536 bytes.extend(&self.bytes);
537 bytes
538 }
539
540 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
546 let hash = crate::read_arr::<32>(buf)?;
547 let bytes = crate::read_vec(buf)?;
548 Ok(Self { hash, bytes })
549 }
550}
551
552#[must_use]
561pub fn gen_contract_id(
562 bytes: impl AsRef<[u8]>,
563 nonce: u64,
564 owner: impl AsRef<[u8]>,
565) -> ContractId {
566 let mut hasher = Params::new().hash_length(CONTRACT_ID_BYTES).to_state();
567 hasher.update(bytes.as_ref());
568 hasher.update(&nonce.to_le_bytes());
569 hasher.update(owner.as_ref());
570
571 let hash_bytes: [u8; CONTRACT_ID_BYTES] = hasher
572 .finalize()
573 .as_bytes()
574 .try_into()
575 .expect("the hash result is exactly `CONTRACT_ID_BYTES` long");
576
577 ContractId::from_bytes(hash_bytes)
578}
579
580impl BlobData {
581 pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
583
584 #[must_use]
586 pub fn to_hash_input_bytes(&self) -> Vec<u8> {
587 self.hash.to_vec()
588 }
589
590 #[must_use]
592 pub fn to_var_bytes(&self) -> Vec<u8> {
593 let mut bytes = Vec::new();
594 bytes.extend(self.hash);
595 if let Some(data) = &self.data {
596 bytes.push(1u8);
597 bytes.extend(data.to_var_bytes());
598 } else {
599 bytes.push(0u8);
600 }
601 bytes
602 }
603
604 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
610 let hash = crate::read_arr(buf)?;
611
612 let data = match u8::from_reader(buf)? {
613 0 => None,
614 1 => Some(BlobSidecar::from_buf(buf)?),
615 _ => return Err(BytesError::InvalidData),
616 };
617
618 Ok(Self { hash, data })
619 }
620
621 #[must_use]
623 pub fn take_sidecar(&mut self) -> Option<BlobSidecar> {
624 self.data.take()
625 }
626
627 #[must_use]
631 pub fn hash_from_commitment(commitment: &[u8]) -> [u8; 32] {
632 let digest = Sha256::digest(commitment);
633 let mut out = [0u8; 32];
634 out[0] = Self::VERSIONED_HASH_VERSION_KZG;
635 out[1..].copy_from_slice(&digest[1..]);
636 out
637 }
638}
639
640impl BlobSidecar {
641 #[must_use]
643 pub fn to_var_bytes(&self) -> Vec<u8> {
644 let mut bytes = Vec::new();
645 bytes.extend(self.commitment);
646 bytes.extend(self.proof);
647 bytes.extend(self.data);
648 bytes
649 }
650
651 pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
657 let commitment = crate::read_arr(buf)?;
658 let proof = crate::read_arr(buf)?;
659 let data = crate::read_arr(buf)?;
660
661 Ok(Self {
662 commitment,
663 proof,
664 data,
665 })
666 }
667}