mod stake;
pub use stake::*;
mod transfer;
pub use transfer::*;
use alloc::string::String;
use alloc::vec::Vec;
#[cfg(feature = "rkyv-impl")]
use rkyv::{Archive, Deserialize, Serialize};
use dusk_bls12_381::BlsScalar;
use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
use crate::{Crossover, Fee, Note};
pub type ModuleId = [u8; 32];
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "rkyv-impl",
derive(Archive, Serialize, Deserialize),
archive_attr(derive(bytecheck::CheckBytes))
)]
pub struct Transaction {
pub anchor: BlsScalar,
pub nullifiers: Vec<BlsScalar>,
pub outputs: Vec<Note>,
pub fee: Fee,
pub crossover: Option<Crossover>,
pub proof: Vec<u8>,
pub call: Option<(ModuleId, String, Vec<u8>)>,
}
impl Transaction {
#[must_use]
pub fn hash_input_bytes_from_components(
nullifiers: &[BlsScalar],
outputs: &[Note],
anchor: &BlsScalar,
fee: &Fee,
crossover: &Option<Crossover>,
call: &Option<(ModuleId, String, Vec<u8>)>,
) -> Vec<u8> {
let mut bytes = Vec::new();
for nullifier in nullifiers {
bytes.extend(nullifier.to_bytes());
}
for note in outputs {
bytes.extend(note.to_bytes());
}
bytes.extend(anchor.to_bytes());
bytes.extend(fee.to_bytes());
if let Some(crossover) = crossover {
bytes.extend(crossover.to_bytes());
}
if let Some((module, fn_name, call_data)) = call {
bytes.extend(module);
bytes.extend(fn_name.as_bytes());
bytes.extend(call_data);
}
bytes
}
#[must_use]
pub fn to_hash_input_bytes(&self) -> Vec<u8> {
Self::hash_input_bytes_from_components(
&self.nullifiers,
&self.outputs,
&self.anchor,
&self.fee,
&self.crossover,
&self.call,
)
}
#[allow(unused_must_use)]
pub fn to_var_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(self.anchor.to_bytes());
let size = self.nullifiers.len() as u64;
bytes.extend(size.to_bytes());
self.nullifiers.iter().for_each(|val| {
bytes.extend(val.to_bytes());
});
let size = self.outputs.len() as u64;
bytes.extend(size.to_bytes());
self.outputs.iter().for_each(|val| {
bytes.extend(val.to_bytes());
});
bytes.extend(self.fee.to_bytes());
if let Some(co) = self.crossover {
bytes.push(1);
bytes.extend(co.to_bytes());
} else {
bytes.push(0);
}
let proof_len = self.proof.len() as u64;
bytes.extend(proof_len.to_bytes());
bytes.extend(&self.proof);
if let Some((module, fn_name, call_data)) = &self.call {
bytes.push(1);
bytes.extend(module);
let size = fn_name.len() as u64;
bytes.extend(size.to_bytes());
bytes.extend(fn_name.as_bytes());
bytes.extend(call_data);
} else {
bytes.push(0);
}
bytes
}
pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
let mut buffer = buf;
let anchor = BlsScalar::from_reader(&mut buffer)?;
let num_nullifiers = u64::from_reader(&mut buffer)?;
let mut nullifiers = Vec::with_capacity(num_nullifiers as usize);
for _ in 0..num_nullifiers {
nullifiers.push(BlsScalar::from_reader(&mut buffer)?);
}
let num_outputs = u64::from_reader(&mut buffer)?;
let mut outputs = Vec::with_capacity(num_outputs as usize);
for _ in 0..num_outputs {
outputs.push(Note::from_reader(&mut buffer)?);
}
let fee = Fee::from_reader(&mut buffer)?;
let has_crossover = buffer[0] != 0;
let mut buffer = &buffer[1..];
let crossover = if has_crossover {
Some(Crossover::from_reader(&mut buffer)?)
} else {
None
};
let proof_size = u64::from_reader(&mut buffer)? as usize;
let proof = buffer[..proof_size].to_vec();
let buffer = &buffer[proof_size..];
let has_call = buffer[0] != 0;
let mut buffer = &buffer[1..];
let call = if has_call {
let buffer_len = buffer.len();
if buffer.len() < 32 {
return Err(BytesError::BadLength {
found: buffer_len,
expected: 32,
});
}
let (module_buf, buf) = buffer.split_at(32);
buffer = buf;
let mut module = [0u8; 32];
module.copy_from_slice(module_buf);
let fn_name_size = u64::from_reader(&mut buffer)? as usize;
let fn_name = String::from_utf8(buffer[..fn_name_size].to_vec())
.map_err(|_err| BytesError::InvalidData)?;
let call_data = buffer[fn_name_size..].to_vec();
Some((module, fn_name, call_data))
} else {
None
};
Ok(Self {
anchor,
nullifiers,
outputs,
fee,
crossover,
proof,
call,
})
}
pub fn nullifiers(&self) -> &[BlsScalar] {
&self.nullifiers
}
pub fn outputs(&self) -> &[Note] {
&self.outputs
}
pub fn fee(&self) -> &Fee {
&self.fee
}
pub fn crossover(&self) -> Option<&Crossover> {
self.crossover.as_ref()
}
pub fn call(&self) -> Option<&(ModuleId, String, Vec<u8>)> {
self.call.as_ref()
}
}