miden_objects/transaction/inputs/
mod.rs

1use alloc::vec::Vec;
2use core::fmt::Debug;
3
4use miden_core::utils::{Deserializable, Serializable};
5
6use super::PartialBlockchain;
7use crate::TransactionInputError;
8use crate::account::{AccountCode, PartialAccount};
9use crate::block::{BlockHeader, BlockNumber};
10use crate::note::{Note, NoteInclusionProof};
11use crate::transaction::{TransactionArgs, TransactionScript};
12
13mod account;
14pub use account::AccountInputs;
15
16mod notes;
17use miden_processor::AdviceInputs;
18pub use notes::{InputNote, InputNotes, ToInputNoteCommitments};
19
20// TRANSACTION INPUTS
21// ================================================================================================
22
23/// Contains the data required to execute a transaction.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct TransactionInputs {
26    account: PartialAccount,
27    block_header: BlockHeader,
28    blockchain: PartialBlockchain,
29    input_notes: InputNotes<InputNote>,
30    tx_args: TransactionArgs,
31    advice_inputs: AdviceInputs,
32    foreign_account_code: Vec<AccountCode>,
33}
34
35impl TransactionInputs {
36    // CONSTRUCTOR
37    // --------------------------------------------------------------------------------------------
38
39    /// Returns new [`TransactionInputs`] instantiated with the specified parameters.
40    ///
41    /// # Errors
42    ///
43    /// Returns an error if:
44    /// - The partial blockchain does not track the block headers required to prove inclusion of any
45    ///   authenticated input note.
46    pub fn new(
47        account: PartialAccount,
48        block_header: BlockHeader,
49        blockchain: PartialBlockchain,
50        input_notes: InputNotes<InputNote>,
51    ) -> Result<Self, TransactionInputError> {
52        // Check that the partial blockchain and block header are consistent.
53        if blockchain.chain_length() != block_header.block_num() {
54            return Err(TransactionInputError::InconsistentChainLength {
55                expected: block_header.block_num(),
56                actual: blockchain.chain_length(),
57            });
58        }
59        if blockchain.peaks().hash_peaks() != block_header.chain_commitment() {
60            return Err(TransactionInputError::InconsistentChainCommitment {
61                expected: block_header.chain_commitment(),
62                actual: blockchain.peaks().hash_peaks(),
63            });
64        }
65        // Validate the authentication paths of the input notes.
66        for note in input_notes.iter() {
67            if let InputNote::Authenticated { note, proof } = note {
68                let note_block_num = proof.location().block_num();
69                let block_header = if note_block_num == block_header.block_num() {
70                    &block_header
71                } else {
72                    blockchain.get_block(note_block_num).ok_or(
73                        TransactionInputError::InputNoteBlockNotInPartialBlockchain(note.id()),
74                    )?
75                };
76                validate_is_in_block(note, proof, block_header)?;
77            }
78        }
79
80        Ok(Self {
81            account,
82            block_header,
83            blockchain,
84            input_notes,
85            tx_args: TransactionArgs::default(),
86            advice_inputs: AdviceInputs::default(),
87            foreign_account_code: Vec::new(),
88        })
89    }
90
91    /// Replaces the transaction inputs and assigns the given foreign account code.
92    pub fn with_foreign_account_code(mut self, foreign_account_code: Vec<AccountCode>) -> Self {
93        self.foreign_account_code = foreign_account_code;
94        self
95    }
96
97    /// Replaces the transaction inputs and assigns the given transaction arguments.
98    pub fn with_tx_args(mut self, tx_args: TransactionArgs) -> Self {
99        self.tx_args = tx_args;
100        self
101    }
102
103    /// Replaces the transaction inputs and assigns the given advice inputs.
104    pub fn with_advice_inputs(mut self, advice_inputs: AdviceInputs) -> Self {
105        self.advice_inputs = advice_inputs;
106        self
107    }
108
109    // MUTATORS
110    // --------------------------------------------------------------------------------------------
111
112    /// Replaces the input notes for the transaction.
113    pub fn set_input_notes(&mut self, new_notes: Vec<Note>) {
114        self.input_notes = new_notes.into();
115    }
116
117    /// Replaces the advice inputs for the transaction.
118    pub fn set_advice_inputs(&mut self, new_advice_inputs: AdviceInputs) {
119        self.advice_inputs = new_advice_inputs;
120    }
121
122    /// Updates the transaction arguments of the inputs.
123    #[cfg(feature = "testing")]
124    pub fn set_tx_args(&mut self, tx_args: TransactionArgs) {
125        self.tx_args = tx_args;
126    }
127
128    // PUBLIC ACCESSORS
129    // --------------------------------------------------------------------------------------------
130
131    /// Returns the account against which the transaction is executed.
132    pub fn account(&self) -> &PartialAccount {
133        &self.account
134    }
135
136    /// Returns block header for the block referenced by the transaction.
137    pub fn block_header(&self) -> &BlockHeader {
138        &self.block_header
139    }
140
141    /// Returns partial blockchain containing authentication paths for all notes consumed by the
142    /// transaction.
143    pub fn blockchain(&self) -> &PartialBlockchain {
144        &self.blockchain
145    }
146
147    /// Returns the notes to be consumed in the transaction.
148    pub fn input_notes(&self) -> &InputNotes<InputNote> {
149        &self.input_notes
150    }
151
152    /// Returns the block number referenced by the inputs.
153    pub fn ref_block(&self) -> BlockNumber {
154        self.block_header.block_num()
155    }
156
157    /// Returns the transaction script to be executed.
158    pub fn tx_script(&self) -> Option<&TransactionScript> {
159        self.tx_args.tx_script()
160    }
161
162    /// Returns the foreign account code to be executed.
163    pub fn foreign_account_code(&self) -> &[AccountCode] {
164        &self.foreign_account_code
165    }
166
167    /// Returns the advice inputs to be consumed in the transaction.
168    pub fn advice_inputs(&self) -> &AdviceInputs {
169        &self.advice_inputs
170    }
171
172    /// Returns the transaction arguments to be consumed in the transaction.
173    pub fn tx_args(&self) -> &TransactionArgs {
174        &self.tx_args
175    }
176
177    // CONVERSIONS
178    // --------------------------------------------------------------------------------------------
179
180    /// Consumes these transaction inputs and returns their underlying components.
181    pub fn into_parts(
182        self,
183    ) -> (
184        PartialAccount,
185        BlockHeader,
186        PartialBlockchain,
187        InputNotes<InputNote>,
188        TransactionArgs,
189    ) {
190        (self.account, self.block_header, self.blockchain, self.input_notes, self.tx_args)
191    }
192}
193
194impl Serializable for TransactionInputs {
195    fn write_into<W: miden_core::utils::ByteWriter>(&self, target: &mut W) {
196        self.account.write_into(target);
197        self.block_header.write_into(target);
198        self.blockchain.write_into(target);
199        self.input_notes.write_into(target);
200        self.tx_args.write_into(target);
201        self.advice_inputs.write_into(target);
202        self.foreign_account_code.write_into(target);
203    }
204}
205
206impl Deserializable for TransactionInputs {
207    fn read_from<R: miden_core::utils::ByteReader>(
208        source: &mut R,
209    ) -> Result<Self, miden_core::utils::DeserializationError> {
210        let account = PartialAccount::read_from(source)?;
211        let block_header = BlockHeader::read_from(source)?;
212        let blockchain = PartialBlockchain::read_from(source)?;
213        let input_notes = InputNotes::read_from(source)?;
214        let tx_args = TransactionArgs::read_from(source)?;
215        let advice_inputs = AdviceInputs::read_from(source)?;
216        let foreign_account_code = Vec::<AccountCode>::read_from(source)?;
217
218        Ok(TransactionInputs {
219            account,
220            block_header,
221            blockchain,
222            input_notes,
223            tx_args,
224            advice_inputs,
225            foreign_account_code,
226        })
227    }
228}
229
230// HELPER FUNCTIONS
231// ================================================================================================
232
233/// Validates whether the provided note belongs to the note tree of the specified block.
234fn validate_is_in_block(
235    note: &Note,
236    proof: &NoteInclusionProof,
237    block_header: &BlockHeader,
238) -> Result<(), TransactionInputError> {
239    let note_index = proof.location().node_index_in_block().into();
240    let note_commitment = note.commitment();
241    proof
242        .note_path()
243        .verify(note_index, note_commitment, &block_header.note_root())
244        .map_err(|_| {
245            TransactionInputError::InputNoteNotInBlock(note.id(), proof.location().block_num())
246        })
247}