#[cfg(feature = "kzg")]
mod kzg_blob;
use alloc::string::String;
use alloc::vec::Vec;
use alloc::{format, vec};
use bytecheck::CheckBytes;
use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
use piecrust_uplink::StandardBufSerializer;
use rkyv::ser::Serializer;
use rkyv::ser::serializers::{
BufferScratch, BufferSerializer, CompositeSerializer,
};
use rkyv::validation::validators::DefaultValidator;
use rkyv::{Archive, Deserialize, Infallible, Serialize};
#[cfg(feature = "serde")]
use serde_with::As;
#[cfg(feature = "serde")]
use serde_with::hex::Hex;
use sha2::{Digest, Sha256};
use crate::Error;
use crate::abi::ContractId;
pub const MAX_MEMO_SIZE: usize = 512;
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[allow(clippy::large_enum_variant)]
pub enum TransactionData {
Call(ContractCall),
Deploy(ContractDeploy),
Memo(Vec<u8>),
Blob(Vec<BlobData>),
}
impl TransactionData {
const NONE_ID: u8 = 0x00;
const CALL_ID: u8 = 0x01;
const DEPLOY_ID: u8 = 0x02;
const MEMO_ID: u8 = 0x03;
const BLOB_ID: u8 = 0x04;
#[must_use]
pub fn signature_message(&self) -> Vec<u8> {
let mut bytes = vec![];
#[allow(clippy::match_same_arms)]
match &self {
TransactionData::Deploy(d) => {
bytes.extend(&d.bytecode.to_hash_input_bytes());
bytes.extend(&d.owner);
if let Some(init_args) = &d.init_args {
bytes.extend(init_args);
}
}
TransactionData::Call(c) => {
bytes.extend(c.contract.as_bytes());
bytes.extend(c.fn_name.as_bytes());
bytes.extend(&c.fn_args);
}
TransactionData::Memo(m) => {
bytes.extend(m);
}
TransactionData::Blob(blobs) => {
for blob in blobs {
bytes.extend(blob.to_hash_input_bytes());
}
}
}
bytes
}
#[must_use]
pub fn to_var_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
match &self {
TransactionData::Call(call) => {
bytes.push(Self::CALL_ID);
bytes.extend(call.to_var_bytes());
}
TransactionData::Deploy(deploy) => {
bytes.push(Self::DEPLOY_ID);
bytes.extend(deploy.to_var_bytes());
}
TransactionData::Memo(memo) => {
bytes.push(Self::MEMO_ID);
bytes.extend((memo.len() as u64).to_bytes());
bytes.extend(memo);
}
TransactionData::Blob(blobs) => {
bytes.push(Self::BLOB_ID);
#[allow(clippy::cast_possible_truncation)]
bytes.extend((blobs.len() as u8).to_bytes());
for blob in blobs {
bytes.extend(blob.to_var_bytes());
}
}
}
bytes
}
#[must_use]
pub fn option_to_var_bytes(data: Option<&TransactionData>) -> Vec<u8> {
let mut bytes = Vec::new();
if let Some(data) = data {
bytes.extend(data.to_var_bytes());
} else {
bytes.push(Self::NONE_ID);
}
bytes
}
pub fn from_slice(buf: &[u8]) -> Result<Option<Self>, BytesError> {
let mut buf = buf;
let data = match u8::from_reader(&mut buf)? {
Self::NONE_ID => None,
Self::CALL_ID => {
Some(TransactionData::Call(ContractCall::from_slice(buf)?))
}
Self::DEPLOY_ID => {
Some(TransactionData::Deploy(ContractDeploy::from_slice(buf)?))
}
Self::MEMO_ID => {
#[allow(clippy::cast_possible_truncation)]
let size = u64::from_reader(&mut buf)? as usize;
if buf.len() != size || size > MAX_MEMO_SIZE {
return Err(BytesError::InvalidData);
}
let memo = buf[..size].to_vec();
Some(TransactionData::Memo(memo))
}
Self::BLOB_ID => {
let blobs_len = u8::from_reader(&mut buf)?;
let mut blobs = Vec::with_capacity(blobs_len as usize);
for _ in 0..blobs_len {
let blob = BlobData::from_buf(&mut buf)?;
blobs.push(blob);
}
Some(TransactionData::Blob(blobs))
}
_ => {
return Err(BytesError::InvalidData);
}
};
Ok(data)
}
}
impl From<ContractCall> for TransactionData {
fn from(c: ContractCall) -> Self {
TransactionData::Call(c)
}
}
impl From<ContractDeploy> for TransactionData {
fn from(d: ContractDeploy) -> Self {
TransactionData::Deploy(d)
}
}
impl From<Vec<u8>> for TransactionData {
fn from(d: Vec<u8>) -> Self {
TransactionData::Memo(d)
}
}
impl From<String> for TransactionData {
fn from(d: String) -> Self {
TransactionData::Memo(d.as_bytes().to_vec())
}
}
impl From<Vec<BlobData>> for TransactionData {
fn from(blobs: Vec<BlobData>) -> Self {
TransactionData::Blob(blobs)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
pub struct ContractDeploy {
pub bytecode: ContractBytecode,
pub owner: Vec<u8>,
pub init_args: Option<Vec<u8>>,
pub nonce: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlobData {
#[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
pub hash: [u8; 32],
pub data: Option<BlobSidecar>,
}
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlobSidecar {
#[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
pub commitment: [u8; 48],
#[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
pub proof: [u8; 48],
#[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
pub data: BlobDataPart,
}
const BYTES_PER_BLOB: usize = 4096 * 32;
pub type BlobDataPart = [u8; BYTES_PER_BLOB];
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ContractCall {
pub contract: ContractId,
pub fn_name: String,
#[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))]
pub fn_args: Vec<u8>,
}
impl ContractDeploy {
#[must_use]
pub fn to_var_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(&self.bytecode.to_var_bytes());
bytes.extend((self.owner.len() as u64).to_bytes());
bytes.extend(&self.owner);
match &self.init_args {
Some(init_args) => {
bytes.push(1);
bytes.extend((init_args.len() as u64).to_bytes());
bytes.extend(init_args);
}
None => bytes.push(0),
}
bytes.extend(self.nonce.to_bytes());
bytes
}
pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
let mut buf = buf;
let bytecode = ContractBytecode::from_buf(&mut buf)?;
let owner = crate::read_vec(&mut buf)?;
let init_args = match u8::from_reader(&mut buf)? {
0 => None,
1 => Some(crate::read_vec(&mut buf)?),
_ => return Err(BytesError::InvalidData),
};
let nonce = u64::from_reader(&mut buf)?;
Ok(Self {
bytecode,
owner,
init_args,
nonce,
})
}
}
impl ContractCall {
pub fn new(
contract: impl Into<ContractId>,
fn_name: impl Into<String>,
) -> Self {
Self {
contract: contract.into(),
fn_name: fn_name.into(),
fn_args: vec![],
}
}
#[must_use]
pub fn with_raw_args(mut self, fn_args: Vec<u8>) -> Self {
self.fn_args = fn_args;
self
}
pub fn with_args<A>(self, fn_arg: &A) -> Result<Self, Error>
where
A: for<'b> Serialize<StandardBufSerializer<'b>>,
A::Archived: for<'b> CheckBytes<DefaultValidator<'b>>,
{
const SCRATCH_SPACE: usize = 1024;
const PAGE_SIZE: usize = 0x1000;
let mut sbuf = [0u8; SCRATCH_SPACE];
let scratch = BufferScratch::new(&mut sbuf);
let mut buffer = [0u8; PAGE_SIZE];
let ser = BufferSerializer::new(&mut buffer[..]);
let mut ser = CompositeSerializer::new(ser, scratch, Infallible);
ser.serialize_value(fn_arg)
.map_err(|e| Error::Rkyv(format!("{e:?}")))?;
let pos = ser.pos();
let fn_args = buffer[..pos].to_vec();
Ok(self.with_raw_args(fn_args))
}
#[must_use]
pub fn to_var_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(self.contract.as_bytes());
let fn_name_bytes = self.fn_name.as_bytes();
bytes.extend((fn_name_bytes.len() as u64).to_bytes());
bytes.extend(fn_name_bytes);
bytes.extend((self.fn_args.len() as u64).to_bytes());
bytes.extend(&self.fn_args);
bytes
}
pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
let mut buf = buf;
let contract = crate::read_arr::<32>(&mut buf)?;
let fn_name = crate::read_str(&mut buf)?;
let fn_args = crate::read_vec(&mut buf)?;
Ok(Self {
contract: contract.into(),
fn_name,
fn_args,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
pub struct ContractBytecode {
pub hash: [u8; 32],
pub bytes: Vec<u8>,
}
impl ContractBytecode {
#[must_use]
pub fn to_hash_input_bytes(&self) -> Vec<u8> {
self.hash.to_vec()
}
#[must_use]
pub fn to_var_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(self.hash);
bytes.extend((self.bytes.len() as u64).to_bytes());
bytes.extend(&self.bytes);
bytes
}
pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
let hash = crate::read_arr::<32>(buf)?;
let bytes = crate::read_vec(buf)?;
Ok(Self { hash, bytes })
}
}
impl BlobData {
pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
#[must_use]
pub fn to_hash_input_bytes(&self) -> Vec<u8> {
self.hash.to_vec()
}
#[must_use]
pub fn to_var_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(self.hash);
if let Some(data) = &self.data {
bytes.push(1u8);
bytes.extend(data.to_var_bytes());
} else {
bytes.push(0u8);
}
bytes
}
pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
let hash = crate::read_arr(buf)?;
let data = match u8::from_reader(buf)? {
0 => None,
1 => Some(BlobSidecar::from_buf(buf)?),
_ => return Err(BytesError::InvalidData),
};
Ok(Self { hash, data })
}
#[must_use]
pub fn take_sidecar(&mut self) -> Option<BlobSidecar> {
self.data.take()
}
#[must_use]
pub fn hash_from_commitment(commitment: &[u8]) -> [u8; 32] {
let digest = Sha256::digest(commitment);
let mut out = [0u8; 32];
out[0] = Self::VERSIONED_HASH_VERSION_KZG;
out[1..].copy_from_slice(&digest[1..]);
out
}
}
impl BlobSidecar {
#[must_use]
pub fn to_var_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(self.commitment);
bytes.extend(self.proof);
bytes.extend(self.data);
bytes
}
pub fn from_buf(buf: &mut &[u8]) -> Result<Self, BytesError> {
let commitment = crate::read_arr(buf)?;
let proof = crate::read_arr(buf)?;
let data = crate::read_arr(buf)?;
Ok(Self {
commitment,
proof,
data,
})
}
}