use alloc::collections::BTreeMap;
use alloc::sync::Arc;
use alloc::vec::Vec;
use miden_crypto::merkle::InnerNodeInfo;
use miden_processor::MastNodeExt;
use super::{Felt, Hasher, Word};
use crate::account::auth::{PublicKeyCommitment, Signature};
use crate::note::{NoteId, NoteRecipient};
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::vm::{AdviceInputs, AdviceMap, Program};
use crate::{EMPTY_WORD, MastForest, MastNodeId};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransactionArgs {
tx_script: Option<TransactionScript>,
tx_script_args: Word,
note_args: BTreeMap<NoteId, Word>,
advice_inputs: AdviceInputs,
auth_args: Word,
}
impl TransactionArgs {
pub fn new(advice_map: AdviceMap) -> Self {
let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() };
Self {
tx_script: None,
tx_script_args: EMPTY_WORD,
note_args: Default::default(),
advice_inputs,
auth_args: EMPTY_WORD,
}
}
#[must_use]
pub fn with_tx_script(mut self, tx_script: TransactionScript) -> Self {
self.tx_script = Some(tx_script);
self
}
#[must_use]
pub fn with_tx_script_and_args(
mut self,
tx_script: TransactionScript,
tx_script_args: Word,
) -> Self {
self.tx_script = Some(tx_script);
self.tx_script_args = tx_script_args;
self
}
#[must_use]
pub fn with_note_args(mut self, note_args: BTreeMap<NoteId, Word>) -> Self {
self.note_args = note_args;
self
}
#[must_use]
pub fn with_auth_args(mut self, auth_args: Word) -> Self {
self.auth_args = auth_args;
self
}
pub fn tx_script(&self) -> Option<&TransactionScript> {
self.tx_script.as_ref()
}
pub fn tx_script_args(&self) -> Word {
self.tx_script_args
}
pub fn get_note_args(&self, note_id: NoteId) -> Option<&Word> {
self.note_args.get(¬e_id)
}
pub fn advice_inputs(&self) -> &AdviceInputs {
&self.advice_inputs
}
pub fn auth_args(&self) -> Word {
self.auth_args
}
pub fn add_output_note_recipient<T: AsRef<NoteRecipient>>(&mut self, note_recipient: T) {
let note_recipient = note_recipient.as_ref();
let inputs = note_recipient.inputs();
let script = note_recipient.script();
let script_encoded: Vec<Felt> = script.into();
let sn_hash = Hasher::merge(&[note_recipient.serial_num(), Word::empty()]);
let sn_script_hash = Hasher::merge(&[sn_hash, script.root()]);
let new_elements = vec![
(sn_hash, concat_words(note_recipient.serial_num(), Word::empty())),
(sn_script_hash, concat_words(sn_hash, script.root())),
(note_recipient.digest(), concat_words(sn_script_hash, inputs.commitment())),
(inputs.commitment(), inputs.to_elements()),
(
Hasher::hash_elements(inputs.commitment().as_elements()),
vec![Felt::from(inputs.num_values())],
),
(script.root(), script_encoded),
];
self.advice_inputs.extend(AdviceInputs::default().with_map(new_elements));
}
pub fn add_signature(
&mut self,
pub_key: PublicKeyCommitment,
message: Word,
signature: Signature,
) {
let pk_word: Word = pub_key.into();
self.advice_inputs
.map
.insert(Hasher::merge(&[pk_word, message]), signature.to_prepared_signature(message));
}
pub fn extend_output_note_recipients<T, L>(&mut self, notes: L)
where
L: IntoIterator<Item = T>,
T: AsRef<NoteRecipient>,
{
for note in notes {
self.add_output_note_recipient(note);
}
}
pub fn extend_advice_map<T: IntoIterator<Item = (Word, Vec<Felt>)>>(&mut self, iter: T) {
self.advice_inputs.map.extend(iter);
}
pub fn extend_merkle_store<I: Iterator<Item = InnerNodeInfo>>(&mut self, iter: I) {
self.advice_inputs.store.extend(iter);
}
pub fn extend_advice_inputs(&mut self, advice_inputs: AdviceInputs) {
self.advice_inputs.extend(advice_inputs);
}
}
fn concat_words(first: Word, second: Word) -> Vec<Felt> {
let mut result = Vec::with_capacity(8);
result.extend(first);
result.extend(second);
result
}
impl Default for TransactionArgs {
fn default() -> Self {
Self::new(AdviceMap::default())
}
}
impl Serializable for TransactionArgs {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.tx_script.write_into(target);
self.tx_script_args.write_into(target);
self.note_args.write_into(target);
self.advice_inputs.write_into(target);
self.auth_args.write_into(target);
}
}
impl Deserializable for TransactionArgs {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let tx_script = Option::<TransactionScript>::read_from(source)?;
let tx_script_args = Word::read_from(source)?;
let note_args = BTreeMap::<NoteId, Word>::read_from(source)?;
let advice_inputs = AdviceInputs::read_from(source)?;
let auth_args = Word::read_from(source)?;
Ok(Self {
tx_script,
tx_script_args,
note_args,
advice_inputs,
auth_args,
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransactionScript {
mast: Arc<MastForest>,
entrypoint: MastNodeId,
}
impl TransactionScript {
pub fn new(code: Program) -> Self {
Self::from_parts(code.mast_forest().clone(), code.entrypoint())
}
pub fn from_parts(mast: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
assert!(mast.get_node_by_id(entrypoint).is_some());
Self { mast, entrypoint }
}
pub fn mast(&self) -> Arc<MastForest> {
self.mast.clone()
}
pub fn root(&self) -> Word {
self.mast[self.entrypoint].digest()
}
}
impl Serializable for TransactionScript {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.mast.write_into(target);
target.write_u32(u32::from(self.entrypoint));
}
}
impl Deserializable for TransactionScript {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let mast = MastForest::read_from(source)?;
let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast)?;
Ok(Self::from_parts(Arc::new(mast), entrypoint))
}
}
#[cfg(test)]
mod tests {
use miden_core::AdviceMap;
use miden_core::utils::{Deserializable, Serializable};
use crate::transaction::TransactionArgs;
#[test]
fn test_tx_args_serialization() {
let tx_args = TransactionArgs::new(AdviceMap::default());
let bytes: std::vec::Vec<u8> = tx_args.to_bytes();
let decoded = TransactionArgs::read_from_bytes(&bytes).unwrap();
assert_eq!(tx_args, decoded);
}
}