Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid double computation of note commitment #1718

Closed
suyash67 opened this issue Aug 22, 2023 · 6 comments · Fixed by #7103
Closed

Avoid double computation of note commitment #1718

suyash67 opened this issue Aug 22, 2023 · 6 comments · Fixed by #7103
Assignees
Labels
A-security Area: Relates to security. Something is insecure. T-optimisation Type: Optimisation. Making something faster / cheaper / smaller

Comments

@suyash67
Copy link
Contributor

When destroying a note, along with the nullifier of that note, we also output the note commitment. For this, we compute the note commitment from the note itself. Ideally, we must reuse the note commitment (that must already be computed before nullifying a note).

nullified_commitment = compute_inner_note_hash(note_interface, note);

@github-project-automation github-project-automation bot moved this to Todo in A3 Aug 22, 2023
@suyash67 suyash67 added the T-optimisation Type: Optimisation. Making something faster / cheaper / smaller label Aug 22, 2023
@iAmMichaelConnor
Copy link
Contributor

Perhaps the note could contain a cached _inner_note_hash data member, which gets set whenever compute_inner_note_hash is first called?
This approach has the downside that it would worsen the learning curve for developers who which to write their own notes (because they would then have to understand why they need to include this field, and how to set this field). Maybe it's a bad suggestion, because it kind-of breaks the interfaces that we have? If Noir had 'inheritance', then users could inherit a base note class which could implement these things. I'm not sure what the 'Rust' equivalent of a inheritance is.

@iAmMichaelConnor
Copy link
Contributor

iAmMichaelConnor commented Aug 23, 2023

Ideally, we must reuse the note commitment (that must already be computed before nullifying a note)

I think it might actually be essential, from a security perspective, because we can't trust the compute_inner_note_hash function to always return the same note value (because its implementation is user-defined)

@iAmMichaelConnor iAmMichaelConnor added T-bug Type: Bug. Something is broken. A-security Area: Relates to security. Something is insecure. and removed T-bug Type: Bug. Something is broken. labels Aug 23, 2023
@iAmMichaelConnor
Copy link
Contributor

Tagging @LeilaWang for thoughts too

@iAmMichaelConnor iAmMichaelConnor added this to the 📢 Initial Public Sandbox Release milestone Aug 25, 2023
@suyash67
Copy link
Contributor Author

suyash67 commented Sep 6, 2023

Discussed this issue with @iAmMichaelConnor @dbanks12 and @jeanmon. This does not look like a security issue with the protocol. The function compute_inner_note_hash in the note interface is user-defined and a malicious developer can make this function return two different note hashes for a same note while:

  1. Creating the note hash when creating the note
  2. Computing the note hash of a note while destroying a note
    This is dependent only on the implementation of compute_inner_note_hash method and it is the developers responsibility to make sure it is a deterministic function.

We still might want to cache the inner_note_hash as a part of the note interface but that would require changing the current note interface (at the cost of confusing developers).

@suyash67
Copy link
Contributor Author

We can extend this optimisation to other parts of the code as well. For example, even when siloing a note, we re-compute the inner note hash.

fn compute_siloed_note_hash<Note, N>(
note_interface: NoteInterface<Note, N>,
note_with_header: Note,
) -> Field {
let get_header = note_interface.get_header;
let header = get_header(note_with_header);
let inner_note_hash = compute_inner_note_hash(note_interface, note_with_header);
compute_siloed_hash(header.contract_address, inner_note_hash)
}

fn compute_unique_siloed_note_hash<Note, N>(
note_interface: NoteInterface<Note, N>,
note_with_header: Note,
) -> Field {
let get_header = note_interface.get_header;
let header = get_header(note_with_header);
let siloed_note_hash = compute_siloed_note_hash(note_interface, note_with_header);
compute_unique_hash(header.nonce, siloed_note_hash)
}

This can only be optimised if we introduce a new member inner_hash (as Mike suggested) in the Note struct. From my understanding, this will be tricky since it'd touch a lot of aztec-noir code and must be done only after initial public release.

@iAmMichaelConnor
Copy link
Contributor

That's a nice idea, @suyash67 .
Returning to this issue after some time, perhaps the note's header should contain the "inner hash"/"siloed hash"/"unique hash" data members, so that the user-defined note isn't cluttered with low-level concepts

@suyash67 suyash67 removed this from the 📢 Initial Public Sandbox Release milestone Sep 11, 2023
@benesjan benesjan assigned benesjan and unassigned suyash67 Jun 19, 2024
@github-project-automation github-project-automation bot moved this from Todo to Done in A3 Jun 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-security Area: Relates to security. Something is insecure. T-optimisation Type: Optimisation. Making something faster / cheaper / smaller
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

3 participants