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.set_tx_args_inner(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.set_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    ///
119    /// Note: the advice stack from the provided advice inputs is discarded.
120    pub fn set_advice_inputs(&mut self, new_advice_inputs: AdviceInputs) {
121        let AdviceInputs { map, store, .. } = new_advice_inputs;
122        self.advice_inputs = AdviceInputs { stack: Default::default(), map, store };
123        self.tx_args.extend_advice_inputs(self.advice_inputs.clone());
124    }
125
126    /// Updates the transaction arguments of the inputs.
127    #[cfg(feature = "testing")]
128    pub fn set_tx_args(&mut self, tx_args: TransactionArgs) {
129        self.set_tx_args_inner(tx_args);
130    }
131
132    // PUBLIC ACCESSORS
133    // --------------------------------------------------------------------------------------------
134
135    /// Returns the account against which the transaction is executed.
136    pub fn account(&self) -> &PartialAccount {
137        &self.account
138    }
139
140    /// Returns block header for the block referenced by the transaction.
141    pub fn block_header(&self) -> &BlockHeader {
142        &self.block_header
143    }
144
145    /// Returns partial blockchain containing authentication paths for all notes consumed by the
146    /// transaction.
147    pub fn blockchain(&self) -> &PartialBlockchain {
148        &self.blockchain
149    }
150
151    /// Returns the notes to be consumed in the transaction.
152    pub fn input_notes(&self) -> &InputNotes<InputNote> {
153        &self.input_notes
154    }
155
156    /// Returns the block number referenced by the inputs.
157    pub fn ref_block(&self) -> BlockNumber {
158        self.block_header.block_num()
159    }
160
161    /// Returns the transaction script to be executed.
162    pub fn tx_script(&self) -> Option<&TransactionScript> {
163        self.tx_args.tx_script()
164    }
165
166    /// Returns the foreign account code to be executed.
167    pub fn foreign_account_code(&self) -> &[AccountCode] {
168        &self.foreign_account_code
169    }
170
171    /// Returns the advice inputs to be consumed in the transaction.
172    pub fn advice_inputs(&self) -> &AdviceInputs {
173        &self.advice_inputs
174    }
175
176    /// Returns the transaction arguments to be consumed in the transaction.
177    pub fn tx_args(&self) -> &TransactionArgs {
178        &self.tx_args
179    }
180
181    // CONVERSIONS
182    // --------------------------------------------------------------------------------------------
183
184    /// Consumes these transaction inputs and returns their underlying components.
185    pub fn into_parts(
186        self,
187    ) -> (
188        PartialAccount,
189        BlockHeader,
190        PartialBlockchain,
191        InputNotes<InputNote>,
192        TransactionArgs,
193    ) {
194        (self.account, self.block_header, self.blockchain, self.input_notes, self.tx_args)
195    }
196
197    // HELPER METHODS
198    // --------------------------------------------------------------------------------------------
199
200    /// Replaces the current tx_args with the provided value.
201    ///
202    /// This also appends advice inputs from these transaction inputs to the advice inputs of the
203    /// tx args.
204    fn set_tx_args_inner(&mut self, tx_args: TransactionArgs) {
205        self.tx_args = tx_args;
206        self.tx_args.extend_advice_inputs(self.advice_inputs.clone());
207    }
208}
209
210impl Serializable for TransactionInputs {
211    fn write_into<W: miden_core::utils::ByteWriter>(&self, target: &mut W) {
212        self.account.write_into(target);
213        self.block_header.write_into(target);
214        self.blockchain.write_into(target);
215        self.input_notes.write_into(target);
216        self.tx_args.write_into(target);
217        self.advice_inputs.write_into(target);
218        self.foreign_account_code.write_into(target);
219    }
220}
221
222impl Deserializable for TransactionInputs {
223    fn read_from<R: miden_core::utils::ByteReader>(
224        source: &mut R,
225    ) -> Result<Self, miden_core::utils::DeserializationError> {
226        let account = PartialAccount::read_from(source)?;
227        let block_header = BlockHeader::read_from(source)?;
228        let blockchain = PartialBlockchain::read_from(source)?;
229        let input_notes = InputNotes::read_from(source)?;
230        let tx_args = TransactionArgs::read_from(source)?;
231        let advice_inputs = AdviceInputs::read_from(source)?;
232        let foreign_account_code = Vec::<AccountCode>::read_from(source)?;
233
234        Ok(TransactionInputs {
235            account,
236            block_header,
237            blockchain,
238            input_notes,
239            tx_args,
240            advice_inputs,
241            foreign_account_code,
242        })
243    }
244}
245
246// HELPER FUNCTIONS
247// ================================================================================================
248
249/// Validates whether the provided note belongs to the note tree of the specified block.
250fn validate_is_in_block(
251    note: &Note,
252    proof: &NoteInclusionProof,
253    block_header: &BlockHeader,
254) -> Result<(), TransactionInputError> {
255    let note_index = proof.location().node_index_in_block().into();
256    let note_commitment = note.commitment();
257    proof
258        .note_path()
259        .verify(note_index, note_commitment, &block_header.note_root())
260        .map_err(|_| {
261            TransactionInputError::InputNoteNotInBlock(note.id(), proof.location().block_num())
262        })
263}