Skip to main content

miden_objects/decoded/transaction/
core.rs

1use miden_protobuf::unwrap_infallible;
2pub use proto::transaction::DecodedTransactionId as TransactionId;
3
4use crate::decoded::VerificationError;
5use crate::{BuildUnchecked, Verify, proto};
6
7#[cfg(test)]
8mod tests;
9
10impl Verify for TransactionId {
11    type Verified = miden_protocol::transaction::TransactionId;
12    type Error = core::convert::Infallible;
13    fn verify(self) -> Result<Self::Verified, Self::Error> {
14        Ok(Self::Verified::from_raw(self.id))
15    }
16}
17
18pub use proto::transaction::DecodedTransactionHeader as TransactionHeader;
19
20/// Builds a header using unchecked input-note commitments. Input/output note uniqueness,
21/// note-header invariants, and the transmitted transaction ID are still checked. The caller must
22/// establish each input's nullifier/header consistency and authentication, the original note order,
23/// and the account ID's relationship to the transaction data.
24impl BuildUnchecked for TransactionHeader {
25    type Output = miden_protocol::transaction::TransactionHeader;
26    type Error = VerificationError;
27    fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
28        let transmitted = unwrap_infallible(self.transaction_id.verify());
29        let input_notes = self
30            .input_notes
31            .into_iter()
32            .map(BuildUnchecked::build_unchecked)
33            .collect::<Result<_, _>>()?;
34        let input_notes = miden_protocol::transaction::InputNotes::new(input_notes)?;
35        let output_notes =
36            self.output_notes.into_iter().map(Verify::verify).collect::<Result<_, _>>()?;
37        let header = Self::Output::new(
38            self.account_id.verify()?,
39            self.initial_state_commitment,
40            self.final_state_commitment,
41            input_notes,
42            output_notes,
43        )?;
44        if header.id() != transmitted {
45            return Err(TransactionHeaderBuildError::IdMismatch {
46                transmitted,
47                recomputed: header.id(),
48            }
49            .into());
50        }
51        Ok(header)
52    }
53}
54
55#[derive(Debug, thiserror::Error)]
56pub enum TransactionHeaderBuildError {
57    #[error("transaction ID mismatch: transmitted {transmitted}, recomputed {recomputed}")]
58    IdMismatch {
59        transmitted: miden_protocol::transaction::TransactionId,
60        recomputed: miden_protocol::transaction::TransactionId,
61    },
62}
63
64pub use proto::transaction::DecodedTxAccountUpdate as TxAccountUpdate;
65
66impl Verify for TxAccountUpdate {
67    type Verified = miden_protocol::transaction::TxAccountUpdate;
68    type Error = VerificationError;
69    fn verify(self) -> Result<Self::Verified, Self::Error> {
70        Ok(Self::Verified::new(
71            self.account_id.verify()?,
72            self.initial_state_commitment,
73            self.final_state_commitment,
74            self.account_patch_commitment,
75            self.details.verify()?,
76        )?)
77    }
78}
79
80pub use proto::transaction::DecodedProvenTransaction as ProvenTransaction;
81
82/// Checks transaction construction invariants, but not its proof or input-note authentication.
83impl crate::BuildUnchecked for ProvenTransaction {
84    type Output = miden_protocol::transaction::ProvenTransaction;
85    type Error = VerificationError;
86    fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
87        let inputs = self
88            .input_notes
89            .into_iter()
90            .map(BuildUnchecked::build_unchecked)
91            .collect::<Result<alloc::vec::Vec<_>, _>>()?;
92        let outputs = self
93            .output_notes
94            .into_iter()
95            .map(Verify::verify)
96            .collect::<Result<alloc::vec::Vec<_>, _>>()?;
97        Ok(Self::Output::new(
98            self.account_update.verify()?,
99            inputs,
100            outputs,
101            unwrap_infallible(self.reference_block_num.verify()),
102            self.reference_block_commitment,
103            unwrap_infallible(self.expiration_block_num.verify()),
104            self.proof,
105        )?)
106    }
107}