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

ToNullifier to ToInputNoteCommitment #732

Merged
merged 1 commit into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* [BREAKING] Added support for delegated authenticated notes (#724).
* [BREAKING] Changed rng to mutable reference in note creation functions (#733).
* Added transaction IDs to the `Block` struct (#734).
* [BREAKING] Replaced `ToNullifier` trait with `ToInputNoteCommitments`, which includes the `note_id` for delayed note authentication (#732).

## 0.3.0 (2024-05-14)

Expand Down
10 changes: 5 additions & 5 deletions miden-lib/src/transaction/inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl ToTransactionKernelInputs for PreparedTransaction {
let stack_inputs = TransactionKernel::build_input_stack(
account.id(),
account.init_hash(),
self.input_notes().nullifier_commitment(),
self.input_notes().commitment(),
self.block_header().hash(),
);

Expand All @@ -44,7 +44,7 @@ impl ToTransactionKernelInputs for ExecutedTransaction {
let stack_inputs = TransactionKernel::build_input_stack(
account.id(),
account.init_hash(),
self.input_notes().nullifier_commitment(),
self.input_notes().commitment(),
self.block_header().hash(),
);

Expand All @@ -62,7 +62,7 @@ impl ToTransactionKernelInputs for TransactionWitness {
let stack_inputs = TransactionKernel::build_input_stack(
account.id(),
account.init_hash(),
self.input_notes().nullifier_commitment(),
self.input_notes().commitment(),
self.block_header().hash(),
);

Expand Down Expand Up @@ -271,7 +271,7 @@ fn add_account_to_advice_inputs(
/// - And all notes details together under the nullifier commitment.
///
fn add_input_notes_to_advice_inputs(
notes: &InputNotes,
notes: &InputNotes<InputNote>,
tx_args: &TransactionArgs,
inputs: &mut AdviceInputs,
) {
Expand Down Expand Up @@ -337,5 +337,5 @@ fn add_input_notes_to_advice_inputs(
}

// NOTE: keep map in sync with the `process_input_notes_data` kernel procedure
inputs.extend_map([(notes.nullifier_commitment(), note_data)]);
inputs.extend_map([(notes.commitment(), note_data)]);
}
6 changes: 3 additions & 3 deletions miden-tx/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use alloc::{collections::BTreeMap, vec::Vec};

use miden_objects::{
assembly::{Assembler, AssemblyContext, ModuleAst, ProgramAst},
transaction::{InputNotes, TransactionScript},
transaction::{InputNote, InputNotes, TransactionScript},
Felt, NoteError, TransactionScriptError, Word,
};

Expand Down Expand Up @@ -150,7 +150,7 @@ impl TransactionCompiler {
pub fn compile_transaction(
&self,
account_id: AccountId,
notes: &InputNotes,
notes: &InputNotes<InputNote>,
tx_script: Option<&ProgramAst>,
) -> Result<Program, TransactionCompilerError> {
// Fetch the account interface from the `account_procedures` map. Return an error if the
Expand Down Expand Up @@ -219,7 +219,7 @@ impl TransactionCompiler {
fn compile_notes(
&self,
target_account_interface: &[Digest],
notes: &InputNotes,
notes: &InputNotes<InputNote>,
assembly_context: &mut AssemblyContext,
) -> Result<Vec<CodeBlock>, TransactionCompilerError> {
let mut note_programs = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion miden-tx/src/kernel_tests/test_prologue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ fn global_input_memory_assertions(process: &Process<MockHost>, inputs: &Prepared

assert_eq!(
read_root_mem_value(process, INPUT_NOTES_COMMITMENT_PTR),
inputs.input_notes().nullifier_commitment().as_elements(),
inputs.input_notes().commitment().as_elements(),
"The nullifier commitment should be stored at the INPUT_NOTES_COMMITMENT_PTR"
);

Expand Down
7 changes: 2 additions & 5 deletions miden-tx/src/prover/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use alloc::vec::Vec;
use miden_lib::transaction::{ToTransactionKernelInputs, TransactionKernel};
use miden_objects::{
accounts::delta::AccountUpdateDetails,
notes::Nullifier,
transaction::{
InputNotes, OutputNote, ProvenTransaction, ProvenTransactionBuilder, TransactionWitness,
},
transaction::{OutputNote, ProvenTransaction, ProvenTransactionBuilder, TransactionWitness},
};
use miden_prover::prove;
pub use miden_prover::ProvingOptions;
Expand Down Expand Up @@ -45,7 +42,7 @@ impl TransactionProver {
) -> Result<ProvenTransaction, TransactionProverError> {
let tx_witness: TransactionWitness = transaction.into();

let input_notes: InputNotes<Nullifier> = tx_witness.tx_inputs().input_notes().into();
let input_notes = tx_witness.tx_inputs().input_notes();
let account_id = tx_witness.account().id();
let block_hash = tx_witness.block_header().hash();

Expand Down
2 changes: 1 addition & 1 deletion miden-tx/src/verifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl TransactionVerifier {
let stack_inputs = TransactionKernel::build_input_stack(
transaction.account_id(),
transaction.account_update().init_state_hash(),
transaction.input_notes().nullifier_commitment(),
transaction.input_notes().commitment(),
transaction.block_ref(),
);
let stack_outputs = TransactionKernel::build_output_stack(
Expand Down
8 changes: 4 additions & 4 deletions objects/src/transaction/executed_tx.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use core::cell::OnceCell;

use super::{
Account, AccountDelta, AccountId, AccountStub, AdviceInputs, BlockHeader, InputNotes,
OutputNotes, Program, TransactionArgs, TransactionId, TransactionInputs, TransactionOutputs,
TransactionWitness,
Account, AccountDelta, AccountId, AccountStub, AdviceInputs, BlockHeader, InputNote,
InputNotes, OutputNotes, Program, TransactionArgs, TransactionId, TransactionInputs,
TransactionOutputs, TransactionWitness,
};

// EXECUTED TRANSACTION
Expand Down Expand Up @@ -89,7 +89,7 @@ impl ExecutedTransaction {
}

/// Returns the notes consumed in this transaction.
pub fn input_notes(&self) -> &InputNotes {
pub fn input_notes(&self) -> &InputNotes<InputNote> {
self.tx_inputs.input_notes()
}

Expand Down
130 changes: 70 additions & 60 deletions objects/src/transaction/inputs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloc::{collections::BTreeSet, string::ToString, vec::Vec};
use alloc::{collections::BTreeSet, vec::Vec};
use core::fmt::Debug;

use super::{BlockHeader, ChainMmr, Digest, Felt, Hasher, Word};
Expand All @@ -19,7 +19,7 @@ pub struct TransactionInputs {
account_seed: Option<Word>,
block_header: BlockHeader,
block_chain: ChainMmr,
input_notes: InputNotes,
input_notes: InputNotes<InputNote>,
}

impl TransactionInputs {
Expand All @@ -36,7 +36,7 @@ impl TransactionInputs {
account_seed: Option<Word>,
block_header: BlockHeader,
block_chain: ChainMmr,
input_notes: InputNotes,
input_notes: InputNotes<InputNote>,
) -> Result<Self, TransactionInputError> {
// validate the seed
validate_account_seed(&account, account_seed)?;
Expand Down Expand Up @@ -113,15 +113,17 @@ impl TransactionInputs {
}

/// Returns the notes to be consumed in the transaction.
pub fn input_notes(&self) -> &InputNotes {
pub fn input_notes(&self) -> &InputNotes<InputNote> {
&self.input_notes
}

// CONVERSIONS
// --------------------------------------------------------------------------------------------

/// Consumes these transaction inputs and returns their underlying components.
pub fn into_parts(self) -> (Account, Option<Word>, BlockHeader, ChainMmr, InputNotes) {
pub fn into_parts(
self,
) -> (Account, Option<Word>, BlockHeader, ChainMmr, InputNotes<InputNote>) {
(
self.account,
self.account_seed,
Expand All @@ -132,60 +134,35 @@ impl TransactionInputs {
}
}

// TO NULLIFIER TRAIT
// TO INPUT NOTE COMMITMENT
// ================================================================================================

/// Defines how a note object can be reduced to a nullifier.
/// Specifies the data used by the transaction kernel to commit to a note.
///
/// This trait is implemented on both [InputNote] and [Nullifier] so that we can treat them
/// generically as [InputNotes].
pub trait ToNullifier:
Debug + Clone + PartialEq + Eq + Serializable + Deserializable + Sized
{
/// The commitment is composed of:
///
/// - nullifier, which prevents double spend and provides unlinkability.
/// - an optional note_id, which allows for delayed note authentication.
pub trait ToInputNoteCommitments {
fn nullifier(&self) -> Nullifier;
}

impl ToNullifier for InputNote {
fn nullifier(&self) -> Nullifier {
self.note().nullifier()
}
}

impl ToNullifier for Nullifier {
fn nullifier(&self) -> Nullifier {
*self
}
}

impl From<InputNotes> for InputNotes<Nullifier> {
fn from(value: InputNotes) -> Self {
Self {
notes: value.notes.iter().map(|note| note.nullifier()).collect(),
nullifier_commitment: build_nullifier_commitment(&value.notes),
}
}
}

impl From<&InputNotes> for InputNotes<Nullifier> {
fn from(value: &InputNotes) -> Self {
Self {
notes: value.notes.iter().map(|note| note.nullifier()).collect(),
nullifier_commitment: build_nullifier_commitment(&value.notes),
}
}
fn note_id(&self) -> Option<NoteId>;
}

// INPUT NOTES
// ================================================================================================

/// Input notes for a transaction, empty if the transaction does not consume notes.
///
/// This structure is generic over `T`, so it can be used to create the input notes for transaction
/// execution, which require the note's details to run the transaction kernel, and the input notes
/// for proof verification, which require only the commitment data.
#[derive(Debug, Clone)]
pub struct InputNotes<T: ToNullifier = InputNote> {
pub struct InputNotes<T> {
notes: Vec<T>,
nullifier_commitment: Digest,
commitment: Digest,
}

impl<T: ToNullifier> InputNotes<T> {
impl<T: ToInputNoteCommitments> InputNotes<T> {
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
/// Returns new [InputNotes] instantiated from the provided vector of notes.
Expand All @@ -209,9 +186,9 @@ impl<T: ToNullifier> InputNotes<T> {
}
}

let nullifier_commitment = build_nullifier_commitment(&notes);
let commitment = build_input_note_commitment(&notes);

Ok(Self { notes, nullifier_commitment })
Ok(Self { notes, commitment })
}

// PUBLIC ACCESSORS
Expand All @@ -221,11 +198,11 @@ impl<T: ToNullifier> InputNotes<T> {
///
/// For non empty lists the commitment is defined as:
///
/// > hash(nullifier_0 || ZERO || nullifier_1 || ZERO || .. || nullifier_n || ZERO)
/// > hash(nullifier_0 || noteid0_or_zero || nullifier_1 || noteid1_or_zero || .. || nullifier_n || noteidn_or_zero)
///
/// Otherwise defined as ZERO for empty lists.
pub fn nullifier_commitment(&self) -> Digest {
self.nullifier_commitment
pub fn commitment(&self) -> Digest {
self.commitment
}

/// Returns total number of input notes.
Expand Down Expand Up @@ -260,7 +237,7 @@ impl<T: ToNullifier> InputNotes<T> {
}
}

impl<T: ToNullifier> IntoIterator for InputNotes<T> {
impl<T> IntoIterator for InputNotes<T> {
type Item = T;
type IntoIter = alloc::vec::IntoIter<Self::Item>;

Expand All @@ -269,27 +246,36 @@ impl<T: ToNullifier> IntoIterator for InputNotes<T> {
}
}

impl<T: ToNullifier> PartialEq for InputNotes<T> {
impl<'a, T> IntoIterator for &'a InputNotes<T> {
type Item = &'a T;
type IntoIter = alloc::slice::Iter<'a, T>;

fn into_iter(self) -> alloc::slice::Iter<'a, T> {
self.notes.iter()
}
}

impl<T: PartialEq> PartialEq for InputNotes<T> {
fn eq(&self, other: &Self) -> bool {
self.notes == other.notes
}
}

impl<T: ToNullifier> Eq for InputNotes<T> {}
impl<T: Eq> Eq for InputNotes<T> {}

impl<T: ToNullifier> Default for InputNotes<T> {
impl<T: ToInputNoteCommitments> Default for InputNotes<T> {
fn default() -> Self {
Self {
notes: Vec::new(),
nullifier_commitment: build_nullifier_commitment::<T>(&[]),
commitment: build_input_note_commitment::<T>(&[]),
}
}
}

// SERIALIZATION
// ------------------------------------------------------------------------------------------------

impl<T: ToNullifier> Serializable for InputNotes<T> {
impl<T: Serializable> Serializable for InputNotes<T> {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
// assert is OK here because we enforce max number of notes in the constructor
assert!(self.notes.len() <= u16::MAX.into());
Expand All @@ -298,18 +284,18 @@ impl<T: ToNullifier> Serializable for InputNotes<T> {
}
}

impl<T: ToNullifier> Deserializable for InputNotes<T> {
impl<T: Deserializable + ToInputNoteCommitments> Deserializable for InputNotes<T> {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let num_notes = source.read_u16()?;
let notes = source.read_many::<T>(num_notes.into())?;
Self::new(notes).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
Self::new(notes).map_err(|err| DeserializationError::InvalidValue(format!("{}", err)))
}
}

// HELPER FUNCTIONS
// ------------------------------------------------------------------------------------------------

fn build_nullifier_commitment<T: ToNullifier>(notes: &[T]) -> Digest {
fn build_input_note_commitment<T: ToInputNoteCommitments>(notes: &[T]) -> Digest {
// Note: This implementation must be kept in sync with the kernel's `process_input_notes_data`
if notes.is_empty() {
return Digest::default();
Expand All @@ -318,7 +304,8 @@ fn build_nullifier_commitment<T: ToNullifier>(notes: &[T]) -> Digest {
let mut elements: Vec<Felt> = Vec::with_capacity(notes.len() * 2);
for note in notes {
elements.extend_from_slice(note.nullifier().as_elements());
elements.extend_from_slice(&Word::default());
elements
.extend_from_slice(&note.note_id().map_or(Word::default(), |note_id| note_id.into()));
}
Hasher::hash_elements(&elements)
}
Expand Down Expand Up @@ -392,6 +379,29 @@ fn is_in_block(note: &Note, proof: &NoteInclusionProof, block_header: &BlockHead
proof.note_path().verify(note_index, note_hash, &block_header.note_root())
}

impl ToInputNoteCommitments for InputNote {
fn nullifier(&self) -> Nullifier {
self.note().nullifier()
}

fn note_id(&self) -> Option<NoteId> {
match self {
InputNote::Authenticated { .. } => None,
InputNote::Unauthenticated { note } => Some(note.id()),
}
}
}

impl ToInputNoteCommitments for &InputNote {
fn nullifier(&self) -> Nullifier {
(*self).nullifier()
}

fn note_id(&self) -> Option<NoteId> {
(*self).note_id()
}
}

// SERIALIZATION
// ------------------------------------------------------------------------------------------------

Expand Down
Loading
Loading