Skip to content

Move note inputs number to the note data section#1327

Merged
Fumuran merged 7 commits intonextfrom
andrew-refactor-note-inputs
May 12, 2025
Merged

Move note inputs number to the note data section#1327
Fumuran merged 7 commits intonextfrom
andrew-refactor-note-inputs

Conversation

@Fumuran
Copy link
Copy Markdown
Contributor

@Fumuran Fumuran commented May 6, 2025

This PR changes the way we handle note inputs. The number of input values is removed from the input values vector provided to the advice inputs, it is stored in the memory (in the notes data section) instead.

Closes: #1267

@Fumuran Fumuran changed the title refactor: move note inputs number to the note data section Move note inputs number to the note data section May 6, 2025
@Fumuran Fumuran added the no docs label May 6, 2025
Comment on lines 747 to 757
export.note_get_inputs_commitment
exec.memory::get_input_note_num_inputs
# => [num_inputs, pad(16)]

exec.note::get_note_inputs_commitment
# => [NOTE_INPUTS_COMMITMENT, pad(16)]
# => [NOTE_INPUTS_COMMITMENT, num_inputs, pad(16)]

# truncate the stack
swapw dropw
# => [NOTE_INPUTS_COMMITMENT, pad(12)]
swapdw dropw dropw
# => [NOTE_INPUTS_COMMITMENT, num_inputs, pad(11)]
end
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although my implementation works, I'm not sure which approach would be the best to get the number of note inputs from memory.

The only way to do so from the miden lib is to call some kernel procedure. So we have two options, which are both not great IMO:

  • Create a new kernel procedure which will return the number of inputs (which is overkill).
  • Return the number of inputs alongside with some other data, i.e. change some existing kernel procedure to return it as well (which is not a good UX, it could be quite confusing).

For now I implemented the second approach.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the approach you've taken is fine. Returning it as part of the commitment is reasonable and, as you've mentioned, we don't have to add a new kernel procedure.

@Fumuran Fumuran marked this pull request as ready for review May 6, 2025 18:40
Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

mem_storew
end

#! Returns the inputs number of the note currently being processed.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#! Returns the inputs number of the note currently being processed.
#! Returns the number of inputs of the note currently being processed.

Nit: I find this slightly clearer.

#! - inputs_len, the note's input count.
#! - INPUTS, the data corresponding to the note's inputs.
#! - INPUTS_COMMITMENT is the sequential hash of the padded note's inputs.
#! - INPUTS is the the data corresponding to the note's inputs.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#! - INPUTS is the the data corresponding to the note's inputs.
#! - INPUTS is the data corresponding to the note's inputs.

Comment on lines +124 to +125
adv_push.1 dup.5
gte assert.err=ERR_NOTE_TOO_FEW_ELEMENTS_FOR_NOTE_INPUTS
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why gte rather than eq? In what scenarios would more elements be appropriate/valid?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resulting note inputs vector could contain padded values (ZEROs that we are pushing there before hashing), so the length value we are getting from the adv.push_mapvaln could be bigger than the actual number of inputs or they could be equal, if the real number of values divides by 8. But it never should be less — that would be an error.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we round up to the next multiple of 8 so we can use eq? If we can do this, then we don't have to think through whether using gte would be an attack vector somehow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, makes sense. I'll update the code to round up the real number of values.

Comment on lines +689 to +690
#[test]
fn test_public_key_as_note_input() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment what the purpose of the test is? Both for future reference and because this tests a very specific issue which is not obvious.

let recipient = NoteRecipient::new(
serial_num,
note_script,
NoteInputs::new(vec![ZERO, ONE, Felt::new(2), Felt::new(3)]).unwrap(),
Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point of the test is that this word is the same as mock_public_key, right? Could we make a single variable out of this (which has that value and is a Word) and then add a comment to explain that this is used both as pub key as well as note input? Just to make it easier to understand.

Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you! I left a few questions/comments inline.

Comment on lines 74 to 76
pub fn format_for_advice(&self) -> Vec<Felt> {
// NOTE: keep map in sync with the `note::get_inputs` API procedure
let mut padded = pad_inputs(&self.values);
padded.insert(0, self.num_values().into());
padded
pad_inputs(&self.values)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but currently the logic of how a given objects is injected into the advice input can be located in a number of places (e.g., here and also in miden-lib). And it is not easy to tell why certain logic "lives" together with the type (like here) or why it "lives" in the transaction advice input injector module of miden-lib. I wonder if there is a good way to come up with a clear methodology for this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I think for me, the clearest way would be if everything was contained within miden-lib as part of extend_advice_inputs.
The other approach is to move it to the individual types. That doesn't make much sense to me, because this format is specific to the transaction kernel / advice provider so it being "closer" to that, i.e. in miden-lib, seems best. Also, in the future, we might have multiple ways to add a given type to the advice provider when we introduce the batch or block kernels.

Comment on lines +264 to 269
// NOTE: keep in sync with the `prologue::process_note_inputs_length` kernel procedure
note_data.push(recipient.inputs().num_values().into());

// NOTE: keep in sync with the `prologue::process_note_assets` kernel procedure
note_data.push((assets.num_assets() as u32).into());
note_data.extend(assets.to_padded_assets());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the main reason why for assets, we push all assets into note_data (i.e., on line 269), but for note inputs we push just the number of inputs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's just a matter of decision: previously none of the note input values were stored in the memory, so for now I decided to store only their number to make less changes to the memory layout of the input notes. But if you think that it will be better to store the values themselves too — I can do so.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue: #1355

Comment on lines +297 to +298
// - NUM_INPUTS is encoded as [num_inputs, 0, 0, 0].
// - NUM_ASSETS is encoded as [num_assets, 0, 0, 0].
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for using two words here? Could this not be:

[num_inputs, num_assets, 0, 0]

But also, if having two words simplifies things, it's fine to keep.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, but decided that it could be confusing. But if we decide to move the note inputs from the advice provider to the memory, that would make more sense: each word would contain the number of corresponding values.

/// The docs.rs build pipeline has a read-only filesystem, so we have to avoid writing to `src`,
/// otherwise the docs will fail to build there. Note that writing to `OUT_DIR` is fine.
const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN_SRC").is_some();
const BUILD_GENERATED_FILES_IN_SRC: bool = true; // option_env!("BUILD_GENERATED_FILES_IN_SRC").is_some();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this change be reverted?

@Fumuran Fumuran merged commit 68b7cc7 into next May 12, 2025
16 checks passed
@Fumuran Fumuran deleted the andrew-refactor-note-inputs branch May 12, 2025 18:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Transaction failure when using account creator's pub_key as note input

3 participants