Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan committed Oct 11, 2024
1 parent 01fb665 commit dfc07cf
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 17 deletions.
29 changes: 15 additions & 14 deletions noir-projects/noir-contracts/contracts/nft_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,17 @@ contract NFT {
let nft = NFT::at(context.this_address());

// We prepare the transfer.
let hiding_point_slot = nft.prepare_transfer_to_private(from, to).call(&mut context);
let hiding_point_slot = nft.prepare_transfer_to_private(to).call(&mut context);

// At last we finalize the transfer.
nft._finalize_transfer_to_private_trusted(from, token_id, hiding_point_slot).enqueue(&mut context);
// At last we finalize the transfer. Usafe of the `unsafe` method here is safe because we set the `from`
// function argument to a message sender, guaranteeing that he can transfer only his own NFTs.
nft._finalize_transfer_to_private_unsafe(from, token_id, hiding_point_slot).enqueue(&mut context);
}

/// Prepares a transfer from public balance of `from` to a private balance of `to`. The transfer then needs to be
/// Prepares a transfer to a private balance of `to`. The transfer then needs to be
/// finalized by calling `finalize_transfer_to_private`. Returns a hiding point slot.
#[private]
fn prepare_transfer_to_private(from: AztecAddress, to: AztecAddress) -> Field {
fn prepare_transfer_to_private(to: AztecAddress) -> Field {
let to_keys = get_public_keys(to);
let to_npk_m_hash = to_keys.npk_m.hash();
let to_note_slot = storage.private_nfts.at(to).storage_slot;
Expand All @@ -168,25 +169,25 @@ contract NFT {
// We encrypt and emit the partial note log
encrypt_and_emit_partial_log(&mut context, note_setup_payload.log_plaintext, to_keys, to);

NFT::at(context.this_address())._store_point_in_transient_storage(note_setup_payload.hiding_point).enqueue(&mut context);

// Using the x-coordinate as a hiding point slot is safe because we have a guarantee that the public functions
// of the transaction are executed right after the private ones and for this reason the protocol guarantees
// that nobody can front-run us in consuming the hiding point. This guarantee would break
// if `finalize_transfer_to_private` was not called in the same transaction. This however is not the flow we
// are currently concerned with. To support the multi-transaction flow we could hash the x-coordinate with
// `from` and then repeat the hashing in `finalize_transfer_to_private`.
// are currently concerned with. To support the multi-transaction flow we could introduce a `from` function
// argument, hash the x-coordinate with it and then repeat the hashing in `finalize_transfer_to_private`.
let hiding_point_slot = note_setup_payload.hiding_point.x;

// We don't need to perform a check that the value overwritten by `_store_point_in_transient_storage_unsafe`
// is zero because the slot is the x-coordinate of the hiding point and hence we could only overwrite
// the value in the slot with the same value. This makes usage of the `unsafe` method safe.
NFT::at(context.this_address())._store_point_in_transient_storage_unsafe(hiding_point_slot, note_setup_payload.hiding_point).enqueue(&mut context);

hiding_point_slot
}

#[public]
#[internal]
fn _store_point_in_transient_storage(point: Point) {
let slot = point.x;
// We don't perform a check for the overwritten value to be zero because the slot is the x-coordinate
// of the hiding point and hence we could only overwrite the value in the slot with the same value.
fn _store_point_in_transient_storage_unsafe(slot: Field, point: Point) {
context.storage_write(slot, point);
}

Expand All @@ -201,7 +202,7 @@ contract NFT {

#[public]
#[internal]
fn _finalize_transfer_to_private_trusted(from: AztecAddress, token_id: Field, hiding_point_slot: Field) {
fn _finalize_transfer_to_private_unsafe(from: AztecAddress, token_id: Field, hiding_point_slot: Field) {
_finalize_transfer_to_private(from, token_id, hiding_point_slot, &mut context, storage);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ unconstrained fn transfer_to_private_to_self() {
#[test]
unconstrained fn transfer_to_private_external_orchestration() {
// Setup without account contracts. We are not using authwits here, so dummy accounts are enough
let (env, nft_contract_address, sender, recipient, token_id) = utils::setup_and_mint(/* with_account_contracts */ false);
let (env, nft_contract_address, _, recipient, token_id) = utils::setup_and_mint(/* with_account_contracts */ false);

let note_randomness = random();

// We mock the Oracle to return the note randomness such that later on we can manually add the note
let _ = OracleMock::mock("getRandomField").returns(note_randomness);

// We prepare the transfer
let hiding_point_slot: Field = env.call_private(NFT::at(nft_contract_address).prepare_transfer_to_private(sender, recipient));
let hiding_point_slot: Field = env.call_private(NFT::at(nft_contract_address).prepare_transfer_to_private(recipient));

// Finalize the transfer of the NFT
// Finalize the transfer of the NFT (message sender owns the NFT in public)
env.call_public(NFT::at(nft_contract_address).finalize_transfer_to_private(token_id, hiding_point_slot));

// TODO(#8771): We need to manually add the note because in the partial notes flow `notify_created_note_oracle`
Expand Down

0 comments on commit dfc07cf

Please sign in to comment.