From fdfc8bc7bcbe498e19c5ea68ee1a327cb8079861 Mon Sep 17 00:00:00 2001 From: Marti Date: Tue, 6 Jan 2026 11:29:43 +0000 Subject: [PATCH 01/16] chore: test case n=8 instead of n=13 --- .../src/kernel_tests/tx/test_note.rs | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 8bee856271..418e8ccc53 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -198,43 +198,38 @@ async fn test_build_recipient() -> anyhow::Result<()> { // Define test values as Words let word_1 = Word::from([1, 2, 3, 4u32]); let word_2 = Word::from([5, 6, 7, 8u32]); - let word_3 = Word::from([9, 10, 11, 12u32]); - let word_4 = Word::from([13, 14, 15, 16u32]); const BASE_ADDR: u32 = 4000; let code = format!( " use miden::core::sys - use miden::protocol::note begin # put the values that will be hashed into the memory push.{word_1} push.{base_addr} mem_storew_be dropw push.{word_2} push.{addr_1} mem_storew_be dropw - push.{word_3} push.{addr_2} mem_storew_be dropw - push.{word_4} push.{addr_3} mem_storew_be dropw - # Test with 4 values + # Test with 4 values (needs padding to 8) push.{script_root} # SCRIPT_ROOT push.{serial_num} # SERIAL_NUM push.4.4000 # num_inputs, inputs_ptr exec.note::build_recipient # => [RECIPIENT_4] - # Test with 5 values + # Test with 5 values (needs padding to 8) push.{script_root} # SCRIPT_ROOT push.{serial_num} # SERIAL_NUM push.5.4000 # num_inputs, inputs_ptr exec.note::build_recipient # => [RECIPIENT_5, RECIPIENT_4] - # Test with 13 values + # Test with 8 values (no padding needed - exactly one rate block) push.{script_root} # SCRIPT_ROOT push.{serial_num} # SERIAL_NUM - push.13.4000 # num_inputs, inputs_ptr + push.8.4000 # num_inputs, inputs_ptr exec.note::build_recipient - # => [RECIPIENT_13, RECIPIENT_5, RECIPIENT_4] + # => [RECIPIENT_8, RECIPIENT_5, RECIPIENT_4] # truncate the stack exec.sys::truncate_stack @@ -242,38 +237,35 @@ async fn test_build_recipient() -> anyhow::Result<()> { ", word_1 = word_1, word_2 = word_2, - word_3 = word_3, - word_4 = word_4, base_addr = BASE_ADDR, addr_1 = BASE_ADDR + 4, - addr_2 = BASE_ADDR + 8, - addr_3 = BASE_ADDR + 12, script_root = note_script.root(), serial_num = serial_num, ); let exec_output = &tx_context.execute_code(&code).await?; - // Create expected recipients and get their digests + // Create expected NoteInputs for each test case let note_inputs_4 = NoteInputs::new(word_1.to_vec())?; - let recipient_4 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_4); let mut inputs_5 = word_1.to_vec(); inputs_5.push(word_2[0]); let note_inputs_5 = NoteInputs::new(inputs_5)?; - let recipient_5 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_5); - let mut inputs_13 = word_1.to_vec(); - inputs_13.extend_from_slice(&word_2.to_vec()); - inputs_13.extend_from_slice(&word_3.to_vec()); - inputs_13.push(word_4[0]); - let note_inputs_13 = NoteInputs::new(inputs_13)?; - let recipient_13 = NoteRecipient::new(serial_num, note_script, note_inputs_13); + let mut inputs_8 = word_1.to_vec(); + inputs_8.extend_from_slice(&word_2.to_vec()); + let note_inputs_8 = NoteInputs::new(inputs_8)?; + + // Create expected recipients and get their digests + let recipient_4 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_4.clone()); + let recipient_5 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_5.clone()); + let recipient_8 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_8.clone()); + let mut expected_stack = alloc::vec::Vec::new(); expected_stack.extend_from_slice(recipient_4.digest().as_elements()); expected_stack.extend_from_slice(recipient_5.digest().as_elements()); - expected_stack.extend_from_slice(recipient_13.digest().as_elements()); + expected_stack.extend_from_slice(recipient_8.digest().as_elements()); expected_stack.reverse(); assert_eq!(exec_output.stack[0..12], expected_stack); From 532fb2e07a67c09c295ef9e174c8e24dae93527e Mon Sep 17 00:00:00 2001 From: Marti Date: Tue, 6 Jan 2026 12:36:17 +0000 Subject: [PATCH 02/16] feat: dont pad note inputs in advice map --- crates/miden-protocol/asm/protocol/active_note.masm | 8 +------- crates/miden-protocol/src/note/inputs.rs | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index 3a32919912..a0aa405a3c 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -274,7 +274,7 @@ end #! Operand stack: [num_inputs, dest_ptr] proc write_inputs_to_memory # load the inputs from the advice map to the advice stack - adv.push_mapvaln + adv.push_mapvaln.8 # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [advice_num_inputs, [INPUT_VALUES]] @@ -283,12 +283,6 @@ proc write_inputs_to_memory # OS => [num_inputs, advice_num_inputs, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] - # Validate the note inputs length. Round up the number of inputs to the next multiple of 8: that - # value should be equal to the length obtained from the `adv.push_mapvaln` procedure. - u32divmod.8 neq.0 add mul.8 - # OS => [rounded_up_num_inputs, advice_num_inputs, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] - # AS => [[INPUT_VALUES]] - assert_eq.err=ERR_NOTE_INVALID_NUMBER_OF_INPUTS # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] diff --git a/crates/miden-protocol/src/note/inputs.rs b/crates/miden-protocol/src/note/inputs.rs index 57b3b9365d..8c6ae55318 100644 --- a/crates/miden-protocol/src/note/inputs.rs +++ b/crates/miden-protocol/src/note/inputs.rs @@ -75,7 +75,7 @@ impl NoteInputs { /// - INPUTS is the variable inputs for the note /// - PADDING is the optional padding to align the data with a 2WORD boundary pub fn to_elements(&self) -> Vec { - pad_inputs(&self.values) + self.values.to_vec() } } From ce6e8099b66603040573d648d51edf239199ec16 Mon Sep 17 00:00:00 2001 From: Marti Date: Tue, 6 Jan 2026 12:36:56 +0000 Subject: [PATCH 03/16] chore: remove outdated comment --- crates/miden-protocol/src/note/inputs.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/miden-protocol/src/note/inputs.rs b/crates/miden-protocol/src/note/inputs.rs index 8c6ae55318..7453722157 100644 --- a/crates/miden-protocol/src/note/inputs.rs +++ b/crates/miden-protocol/src/note/inputs.rs @@ -69,11 +69,6 @@ impl NoteInputs { } /// Returns the note's input as a vector of field elements. - /// - /// The format is `INPUTS || PADDING`, where: - /// - /// - INPUTS is the variable inputs for the note - /// - PADDING is the optional padding to align the data with a 2WORD boundary pub fn to_elements(&self) -> Vec { self.values.to_vec() } From 95ee9eaa83cf7d0ab10804129dd2cf7e887d0460 Mon Sep 17 00:00:00 2001 From: Marti Date: Tue, 6 Jan 2026 13:03:07 +0000 Subject: [PATCH 04/16] chore: test that advice map is correct --- .../src/kernel_tests/tx/test_note.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 418e8ccc53..1aff54f7fb 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -29,7 +29,7 @@ use miden_protocol::testing::account_id::{ }; use miden_protocol::transaction::memory::ACTIVE_INPUT_NOTE_PTR; use miden_protocol::transaction::{OutputNote, TransactionArgs}; -use miden_protocol::{Felt, Word, ZERO}; +use miden_protocol::{Felt, Hasher, Word, ZERO}; use miden_standards::account::wallets::BasicWallet; use miden_standards::code_builder::CodeBuilder; use miden_standards::testing::note::NoteBuilder; @@ -261,6 +261,22 @@ async fn test_build_recipient() -> anyhow::Result<()> { let recipient_5 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_5.clone()); let recipient_8 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_8.clone()); + for note_inputs in [note_inputs_4, note_inputs_5, note_inputs_8] { + let inputs_advice_map_key = note_inputs.commitment(); + assert_eq!( + exec_output.advice.get_mapped_values(&inputs_advice_map_key).unwrap(), + note_inputs.to_elements(), + "advice entry with note inputs should contain the padded values" + ); + + let num_inputs_advice_map_key = + Hasher::hash_elements(note_inputs.commitment().as_elements()); + assert_eq!( + exec_output.advice.get_mapped_values(&num_inputs_advice_map_key).unwrap(), + &[Felt::from(note_inputs.num_values())], + "advice entry with num note inputs should contain the original number of values" + ); + } let mut expected_stack = alloc::vec::Vec::new(); expected_stack.extend_from_slice(recipient_4.digest().as_elements()); From a5bfeff100d45252965b3b58b83466785d3ba07e Mon Sep 17 00:00:00 2001 From: Marti Date: Tue, 6 Jan 2026 13:11:37 +0000 Subject: [PATCH 05/16] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f44c133564..5bedfdc2f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Changes +- No longer pad the note inputs on insertion into advice map ([#2232](https://github.com/0xMiden/miden-base/pull/2232)). - Added proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)). - [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)). - [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)). From 51922fe138f0ebe2a26efbe3c34bac1e649b4338 Mon Sep 17 00:00:00 2001 From: Marti Date: Tue, 6 Jan 2026 13:25:47 +0000 Subject: [PATCH 06/16] fix: compare the advice entry against original --- .../src/kernel_tests/tx/test_note.rs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_note.rs index 1aff54f7fb..0c4543c4f1 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_note.rs @@ -246,34 +246,39 @@ async fn test_build_recipient() -> anyhow::Result<()> { let exec_output = &tx_context.execute_code(&code).await?; // Create expected NoteInputs for each test case - let note_inputs_4 = NoteInputs::new(word_1.to_vec())?; + let inputs_4 = word_1.to_vec(); + let note_inputs_4 = NoteInputs::new(inputs_4.clone())?; let mut inputs_5 = word_1.to_vec(); inputs_5.push(word_2[0]); - let note_inputs_5 = NoteInputs::new(inputs_5)?; + let note_inputs_5 = NoteInputs::new(inputs_5.clone())?; let mut inputs_8 = word_1.to_vec(); inputs_8.extend_from_slice(&word_2.to_vec()); - let note_inputs_8 = NoteInputs::new(inputs_8)?; + let note_inputs_8 = NoteInputs::new(inputs_8.clone())?; // Create expected recipients and get their digests let recipient_4 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_4.clone()); let recipient_5 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_5.clone()); let recipient_8 = NoteRecipient::new(serial_num, note_script.clone(), note_inputs_8.clone()); - for note_inputs in [note_inputs_4, note_inputs_5, note_inputs_8] { - let inputs_advice_map_key = note_inputs.commitment(); + for note_inputs in [ + (note_inputs_4, inputs_4.clone()), + (note_inputs_5, inputs_5.clone()), + (note_inputs_8, inputs_8.clone()), + ] { + let inputs_advice_map_key = note_inputs.0.commitment(); assert_eq!( exec_output.advice.get_mapped_values(&inputs_advice_map_key).unwrap(), - note_inputs.to_elements(), - "advice entry with note inputs should contain the padded values" + note_inputs.1, + "advice entry with note inputs should contain the unpadded values" ); let num_inputs_advice_map_key = - Hasher::hash_elements(note_inputs.commitment().as_elements()); + Hasher::hash_elements(note_inputs.0.commitment().as_elements()); assert_eq!( exec_output.advice.get_mapped_values(&num_inputs_advice_map_key).unwrap(), - &[Felt::from(note_inputs.num_values())], + &[Felt::from(note_inputs.0.num_values())], "advice entry with num note inputs should contain the original number of values" ); } From b8b04ff265601808ae4499cb25a26bcac265750b Mon Sep 17 00:00:00 2001 From: Marti Date: Thu, 8 Jan 2026 15:18:21 +0000 Subject: [PATCH 07/16] feat: Refactor note inputs commitment calculation --- .../asm/protocol/active_note.masm | 27 +++++++++------ crates/miden-protocol/asm/protocol/note.masm | 5 +-- crates/miden-protocol/src/note/inputs.rs | 34 +++++-------------- .../src/transaction/kernel/memory.rs | 2 +- 4 files changed, 29 insertions(+), 39 deletions(-) diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index a0aa405a3c..ccc68e8985 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -95,7 +95,7 @@ end #! #! Where: #! - dest_ptr is the memory address to write the note inputs. -#! - NOTE_INPUTS_COMMITMENT is the sequential hash of the padded note's inputs. +#! - NOTE_INPUTS_COMMITMENT is the commitment to the note's (unpadded) inputs. #! - INPUTS is the data corresponding to the note's inputs. #! #! Panics if: @@ -274,7 +274,7 @@ end #! Operand stack: [num_inputs, dest_ptr] proc write_inputs_to_memory # load the inputs from the advice map to the advice stack - adv.push_mapvaln.8 + adv.push_mapvaln.4 # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [advice_num_inputs, [INPUT_VALUES]] @@ -292,18 +292,23 @@ proc write_inputs_to_memory # OS => [num_words, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] - # round up the number of words to the next multiple of 2 - dup is_odd add - # OS => [even_num_words, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] - # AS => [[INPUT_VALUES]] - - # prepare the stack for the `pipe_preimage_to_memory` procedure + # prepare the stack for the `pipe_words_to_memory` procedure dup.6 swap - # OS => [even_num_words, dest_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # OS => [num_words, dest_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] # write the inputs from the advice stack into memory - exec.mem::pipe_preimage_to_memory drop - # OS => [num_inputs, dest_ptr] + exec.mem::pipe_words_to_memory + # OS => [C, B, A, dest_ptr', NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [] + + # drop the hasher state and dest_ptr' + dropw dropw dropw drop + # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + + # compute and validate the inputs commitment (over the unpadded values) + dup.5 dup.5 swap + exec.note::compute_inputs_commitment + assert_eqw.err=ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT + # => [num_inputs, dest_ptr] end diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index 284c68d4ea..f914711627 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -16,7 +16,8 @@ const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceede #! This procedure checks that the provided number of note inputs is within limits and then computes #! the commitment. #! -#! Notice that the note inputs are padded with zeros in case their number is not a multiple of 8. +#! Notice that the commitment is computed over the unpadded input values (i.e. without extending +#! them with zeros to the next multiple of 8). #! #! If the number of note inputs is 0, procedure returns the empty word: [0, 0, 0, 0]. #! @@ -43,7 +44,7 @@ pub proc compute_inputs_commitment # TODO: refactor the hash computation process as part of the # https://github.com/0xMiden/miden-base/issues/2036 and # https://github.com/0xMiden/miden-base/issues/2129 - exec.rpo256::pad_and_hash_elements + exec.rpo256::hash_elements # => [INPUTS_COMMITMENT] end diff --git a/crates/miden-protocol/src/note/inputs.rs b/crates/miden-protocol/src/note/inputs.rs index 7453722157..e839d76246 100644 --- a/crates/miden-protocol/src/note/inputs.rs +++ b/crates/miden-protocol/src/note/inputs.rs @@ -8,7 +8,7 @@ use crate::utils::serde::{ DeserializationError, Serializable, }; -use crate::{Felt, Hasher, MAX_INPUTS_PER_NOTE, WORD_SIZE, Word, ZERO}; +use crate::{Felt, Hasher, MAX_INPUTS_PER_NOTE, Word}; // NOTE INPUTS // ================================================================================================ @@ -18,9 +18,8 @@ use crate::{Felt, Hasher, MAX_INPUTS_PER_NOTE, WORD_SIZE, Word, ZERO}; /// A note can be associated with up to 1024 input values. Each value is represented by a single /// field element. Thus, note input values can contain up to ~8 KB of data. /// -/// All inputs associated with a note can be reduced to a single commitment which is computed by -/// first padding the inputs with ZEROs to the next multiple of 8, and then by computing a -/// sequential hash of the resulting elements. +/// All inputs associated with a note can be reduced to a single commitment which is computed as an +/// RPO256 hash over the input elements. #[derive(Clone, Debug)] pub struct NoteInputs { values: Vec, @@ -40,7 +39,7 @@ impl NoteInputs { return Err(NoteError::TooManyInputs(values.len())); } - Ok(pad_and_build(values)) + Ok(build(values)) } // PUBLIC ACCESSORS @@ -76,7 +75,7 @@ impl NoteInputs { impl Default for NoteInputs { fn default() -> Self { - pad_and_build(vec![]) + build(vec![]) } } @@ -109,24 +108,9 @@ impl TryFrom> for NoteInputs { // HELPER FUNCTIONS // ================================================================================================ -/// Returns a vector with built from the provided inputs and padded to the next multiple of 8. -fn pad_inputs(inputs: &[Felt]) -> Vec { - const BLOCK_SIZE: usize = WORD_SIZE * 2; - - let padded_len = inputs.len().next_multiple_of(BLOCK_SIZE); - let mut padded_inputs = Vec::with_capacity(padded_len); - padded_inputs.extend(inputs.iter()); - padded_inputs.resize(padded_len, ZERO); - - padded_inputs -} - -/// Pad `values` and returns a new `NoteInputs`. -fn pad_and_build(values: Vec) -> NoteInputs { - let commitment = { - let padded_values = pad_inputs(&values); - Hasher::hash_elements(&padded_values) - }; +/// Builds a new [`NoteInputs`] and computes its commitment. +fn build(values: Vec) -> NoteInputs { + let commitment = Hasher::hash_elements(&values); NoteInputs { values, commitment } } @@ -163,7 +147,7 @@ mod tests { fn test_input_ordering() { // inputs are provided in reverse stack order let inputs = vec![Felt::new(1), Felt::new(2), Felt::new(3)]; - // we expect the inputs to be padded to length 16 and to remain in reverse stack order. + // we expect the inputs to remain in reverse stack order. let expected_ordering = vec![Felt::new(1), Felt::new(2), Felt::new(3)]; let note_inputs = NoteInputs::new(inputs).expect("note created should succeed"); diff --git a/crates/miden-protocol/src/transaction/kernel/memory.rs b/crates/miden-protocol/src/transaction/kernel/memory.rs index d143a96bc5..d6f84d716e 100644 --- a/crates/miden-protocol/src/transaction/kernel/memory.rs +++ b/crates/miden-protocol/src/transaction/kernel/memory.rs @@ -363,7 +363,7 @@ pub const NOTE_MEM_SIZE: MemoryAddress = 2048; // // Notice that note input values are not loaded to the memory, only their length. In order to obtain // the input values the advice map should be used: they are stored there as -// `INPUTS_COMMITMENT -> INPUTS || PADDING`. +// `INPUTS_COMMITMENT -> INPUTS`. // // As opposed to the asset values, input values are never used in kernel memory, so their presence // there is unnecessary. From 07d58764ba650277e5790553a2bd8c8807285cd9 Mon Sep 17 00:00:00 2001 From: Marti Date: Thu, 8 Jan 2026 15:07:06 +0000 Subject: [PATCH 08/16] feat: pipe even num of words --- crates/miden-protocol/asm/protocol/active_note.masm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index ccc68e8985..f31a23d75c 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -274,7 +274,10 @@ end #! Operand stack: [num_inputs, dest_ptr] proc write_inputs_to_memory # load the inputs from the advice map to the advice stack - adv.push_mapvaln.4 + # we pad the number of inputs to the next multiple of 8 so that we can use the + # `pipe_words_to_memory` instruction with an even number of words, even though the padded zeros + # are ignored during the commitment computation + adv.push_mapvaln.8 # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [advice_num_inputs, [INPUT_VALUES]] @@ -292,9 +295,14 @@ proc write_inputs_to_memory # OS => [num_words, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] + # round up the number of words to the next multiple of 2 + dup is_odd add + # OS => [even_num_words, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # AS => [[INPUT_VALUES]] + # prepare the stack for the `pipe_words_to_memory` procedure dup.6 swap - # OS => [num_words, dest_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # OS => [even_num_words, dest_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] # write the inputs from the advice stack into memory From 42ffa50be69f976c6a0f279565aa4c410a1afff5 Mon Sep 17 00:00:00 2001 From: Marti Date: Thu, 8 Jan 2026 15:25:08 +0000 Subject: [PATCH 09/16] chore: move reexport to top --- crates/miden-protocol/asm/protocol/note.masm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index f914711627..fa248caa67 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -3,6 +3,9 @@ use miden::core::crypto::hashes::rpo256 use miden::core::math::u64 use miden::core::mem +# Re-export the max inputs per note constant. +pub use ::miden::protocol::util::note::MAX_INPUTS_PER_NOTE + # ERRORS # ================================================================================================= @@ -48,9 +51,6 @@ pub proc compute_inputs_commitment # => [INPUTS_COMMITMENT] end -# Re-export the max inputs per note constant. -pub use ::miden::protocol::util::note::MAX_INPUTS_PER_NOTE - #! Writes the assets data stored in the advice map to the memory specified by the provided #! destination pointer. #! From ef80b9566cb9cce6eb4cc821bb9b7a04efffd32d Mon Sep 17 00:00:00 2001 From: Marti Date: Thu, 8 Jan 2026 15:25:30 +0000 Subject: [PATCH 10/16] chore: remove todos --- crates/miden-protocol/asm/protocol/note.masm | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index fa248caa67..85730c9528 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -19,9 +19,6 @@ const ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT="number of note inputs exceede #! This procedure checks that the provided number of note inputs is within limits and then computes #! the commitment. #! -#! Notice that the commitment is computed over the unpadded input values (i.e. without extending -#! them with zeros to the next multiple of 8). -#! #! If the number of note inputs is 0, procedure returns the empty word: [0, 0, 0, 0]. #! #! Inputs: [inputs_ptr, num_inputs] @@ -42,11 +39,7 @@ pub proc compute_inputs_commitment u32lte assert.err=ERR_PROLOGUE_NOTE_INPUTS_LEN_EXCEEDED_LIMIT # => [inputs_ptr, num_inputs] - # compute the inputs commitment - # - # TODO: refactor the hash computation process as part of the - # https://github.com/0xMiden/miden-base/issues/2036 and - # https://github.com/0xMiden/miden-base/issues/2129 + # compute the inputs commitment (over the unpadded values) exec.rpo256::hash_elements # => [INPUTS_COMMITMENT] end From 86465bd92d7527d8b2db1f01ac726d0998cc567c Mon Sep 17 00:00:00 2001 From: Marti Date: Thu, 8 Jan 2026 15:55:12 +0000 Subject: [PATCH 11/16] chore: remove the getter proc from docs --- docs/src/protocol_library.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/src/protocol_library.md b/docs/src/protocol_library.md index b67662d744..01bc92dee6 100644 --- a/docs/src/protocol_library.md +++ b/docs/src/protocol_library.md @@ -117,7 +117,6 @@ Note utility procedures can be used to compute the required utility data or writ | Procedure | Description | Context | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `compute_inputs_commitment` | Computes the commitment to the output note inputs starting at the specified memory address.

**Inputs:** `[inputs_ptr, num_inputs]`
**Outputs:** `[INPUTS_COMMITMENT]` | Any | -| `get_max_inputs_per_note` | Returns the max allowed number of input values per note.

**Inputs:** `[]`
**Outputs:** `[max_inputs_per_note]` | Any | | `write_assets_to_memory` | Writes the assets data stored in the advice map to the memory specified by the provided destination pointer.

**Inputs:** `[ASSETS_COMMITMENT, num_assets, dest_ptr]`
**Outputs:** `[num_assets, dest_ptr]` | Any | | `build_recipient_hash` | Returns the `RECIPIENT` for a specified `SERIAL_NUM`, `SCRIPT_ROOT`, and inputs commitment.

**Inputs:** `[SERIAL_NUM, SCRIPT_ROOT, INPUT_COMMITMENT]`
**Outputs:** `[RECIPIENT]` | Any | | `build_recipient` | Builds the recipient hash from note inputs, script root, and serial number.

**Inputs:** `[inputs_ptr, num_inputs, SERIAL_NUM, SCRIPT_ROOT]`
**Outputs:** `[RECIPIENT]` | Any | From c538e70f2efb0f448409fc5b3ff9591bbddc478a Mon Sep 17 00:00:00 2001 From: Marti Date: Fri, 9 Jan 2026 09:08:56 +0000 Subject: [PATCH 12/16] chore: inline `build` --- crates/miden-protocol/src/note/inputs.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/crates/miden-protocol/src/note/inputs.rs b/crates/miden-protocol/src/note/inputs.rs index e839d76246..7d7b04a0c6 100644 --- a/crates/miden-protocol/src/note/inputs.rs +++ b/crates/miden-protocol/src/note/inputs.rs @@ -39,7 +39,9 @@ impl NoteInputs { return Err(NoteError::TooManyInputs(values.len())); } - Ok(build(values)) + let commitment = Hasher::hash_elements(&values); + + Ok(Self { values, commitment }) } // PUBLIC ACCESSORS @@ -75,7 +77,7 @@ impl NoteInputs { impl Default for NoteInputs { fn default() -> Self { - build(vec![]) + Self::new(vec![]).expect("empty values should be valid") } } @@ -105,16 +107,6 @@ impl TryFrom> for NoteInputs { } } -// HELPER FUNCTIONS -// ================================================================================================ - -/// Builds a new [`NoteInputs`] and computes its commitment. -fn build(values: Vec) -> NoteInputs { - let commitment = Hasher::hash_elements(&values); - - NoteInputs { values, commitment } -} - // SERIALIZATION // ================================================================================================ From 8aefd5bdae4e75a9e2ec4f00a5410bf86eff30f8 Mon Sep 17 00:00:00 2001 From: Marti Date: Fri, 9 Jan 2026 09:10:44 +0000 Subject: [PATCH 13/16] chore: shorten doc comment --- crates/miden-protocol/asm/protocol/active_note.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index f31a23d75c..0396d3f032 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -95,7 +95,7 @@ end #! #! Where: #! - dest_ptr is the memory address to write the note inputs. -#! - NOTE_INPUTS_COMMITMENT is the commitment to the note's (unpadded) inputs. +#! - NOTE_INPUTS_COMMITMENT is the commitment to the note's inputs. #! - INPUTS is the data corresponding to the note's inputs. #! #! Panics if: From d8c3d2273f8327a6f371574ef2b73aa5cf104994 Mon Sep 17 00:00:00 2001 From: Marti Date: Fri, 9 Jan 2026 09:45:40 +0000 Subject: [PATCH 14/16] chore: move to `pipe_double_words_to_memory` --- .../asm/protocol/active_note.masm | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index 0396d3f032..3ccc091ad5 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -275,8 +275,8 @@ end proc write_inputs_to_memory # load the inputs from the advice map to the advice stack # we pad the number of inputs to the next multiple of 8 so that we can use the - # `pipe_words_to_memory` instruction with an even number of words, even though the padded zeros - # are ignored during the commitment computation + # `pipe_double_words_to_memory` instruction, even though the padded zeros are ignored during the + # commitment computation adv.push_mapvaln.8 # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [advice_num_inputs, [INPUT_VALUES]] @@ -300,17 +300,22 @@ proc write_inputs_to_memory # OS => [even_num_words, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] - # prepare the stack for the `pipe_words_to_memory` procedure - dup.6 swap - # OS => [even_num_words, dest_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # compute the end pointer for writing the padded inputs (even_num_words * 4 elements) + dup.6 swap mul.4 add + # OS => [end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # AS => [[INPUT_VALUES]] + + # prepare the stack for the `pipe_double_words_to_memory` procedure + dup.6 padw padw padw + # OS => [PAD, PAD, PAD, dest_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] # write the inputs from the advice stack into memory - exec.mem::pipe_words_to_memory - # OS => [C, B, A, dest_ptr', NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + exec.mem::pipe_double_words_to_memory + # OS => [PERM, PERM, PERM, end_ptr', NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [] - # drop the hasher state and dest_ptr' + # drop the hasher state and end_ptr' dropw dropw dropw drop # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] From 24aa334d1759155a59aae0868490ec6ed291a0d2 Mon Sep 17 00:00:00 2001 From: Marti Date: Tue, 13 Jan 2026 09:39:51 +0000 Subject: [PATCH 15/16] chore: reword comment about padded zeros --- crates/miden-protocol/asm/protocol/active_note.masm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index 3ccc091ad5..2d30d86233 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -275,8 +275,9 @@ end proc write_inputs_to_memory # load the inputs from the advice map to the advice stack # we pad the number of inputs to the next multiple of 8 so that we can use the - # `pipe_double_words_to_memory` instruction, even though the padded zeros are ignored during the - # commitment computation + # `pipe_double_words_to_memory` instruction. The padded zeros don't affect the commitment + # computation. + adv.push_mapvaln.8 # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [advice_num_inputs, [INPUT_VALUES]] From 58b12b61edfc233ec658d55d6f81edd1b6460652 Mon Sep 17 00:00:00 2001 From: Marti Date: Tue, 13 Jan 2026 09:40:24 +0000 Subject: [PATCH 16/16] chore: squeeze rpo digest instead of rehashing --- .../asm/protocol/active_note.masm | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/crates/miden-protocol/asm/protocol/active_note.masm b/crates/miden-protocol/asm/protocol/active_note.masm index 2d30d86233..9cda06a621 100644 --- a/crates/miden-protocol/asm/protocol/active_note.masm +++ b/crates/miden-protocol/asm/protocol/active_note.masm @@ -1,3 +1,4 @@ +use miden::core::crypto::hashes::rpo256 use miden::core::mem use miden::protocol::kernel_proc_offsets @@ -306,9 +307,24 @@ proc write_inputs_to_memory # OS => [end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] - # prepare the stack for the `pipe_double_words_to_memory` procedure - dup.6 padw padw padw - # OS => [PAD, PAD, PAD, dest_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # prepare the stack for the `pipe_double_words_to_memory` procedure. + # + # To match `rpo256::hash_elements` (used for NOTE_INPUTS_COMMITMENT), we set the first capacity + # element to `num_inputs % 8`. + dup.6 dup.6 + # OS => [num_inputs, write_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # AS => [[INPUT_VALUES]] + + u32divmod.8 swap drop + # OS => [num_inputs_mod_8, write_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # AS => [[INPUT_VALUES]] + + push.0.0.0 + # OS => [A, write_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr], where A = [0, 0, 0, num_inputs_mod_8] + # AS => [[INPUT_VALUES]] + + padw padw + # OS => [PAD, PAD, A, write_ptr, end_ptr, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [[INPUT_VALUES]] # write the inputs from the advice stack into memory @@ -316,13 +332,15 @@ proc write_inputs_to_memory # OS => [PERM, PERM, PERM, end_ptr', NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] # AS => [] - # drop the hasher state and end_ptr' - dropw dropw dropw drop - # OS => [NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + # extract the computed commitment from the hasher state + exec.rpo256::squeeze_digest + # OS => [COMPUTED_COMMITMENT, end_ptr', NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] + + # drop end_ptr' + movup.4 drop + # OS => [COMPUTED_COMMITMENT, NOTE_INPUTS_COMMITMENT, num_inputs, dest_ptr] - # compute and validate the inputs commitment (over the unpadded values) - dup.5 dup.5 swap - exec.note::compute_inputs_commitment + # validate that the inputs written to memory match the inputs commitment assert_eqw.err=ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT # => [num_inputs, dest_ptr] end