Skip to content

Commit

Permalink
feat: replaced ToNullifier with ToInputNoteCommitment (#732)
Browse files Browse the repository at this point in the history
  • Loading branch information
hackaugusto authored and bobbinth committed Jul 4, 2024
1 parent de1159f commit e315a93
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 99 deletions.
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.1 (2024-06-12)
* Replaced `cargo-make` with just `make` for running tasks (#696).
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

0 comments on commit e315a93

Please sign in to comment.