Skip to content

Commit

Permalink
Custom notes. (#991)
Browse files Browse the repository at this point in the history
* Remove public_private contract.

* Add send_note util.

* Add claim note.
  • Loading branch information
LeilaWang authored Jul 10, 2023
1 parent d239ce9 commit f656079
Show file tree
Hide file tree
Showing 21 changed files with 427 additions and 472 deletions.
32 changes: 29 additions & 3 deletions yarn-project/acir-simulator/src/client/private_execution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ describe('Private Execution test suite', () => {
return trees[name];
};

const hash = (data: Buffer[]) => pedersenPlookupCommitInputs(circuitsWasm, data);

beforeAll(async () => {
circuitsWasm = await CircuitsWasm.get();
logger = createDebugLogger('aztec:test:private_execution');
Expand Down Expand Up @@ -283,6 +285,32 @@ describe('Private Execution test suite', () => {
expect(recipientNote.preimage[0]).toEqual(new Fr(amountToTransfer));
expect(changeNote.preimage[0]).toEqual(new Fr(balance - amountToTransfer));
}, 30_000);

it('Should be able to claim a note by providing the correct secret', async () => {
const contractAddress = AztecAddress.random();
const amount = 100n;
const secret = Fr.random();
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'claim')!;

const storageSlot = 2n;
const innerNoteHash = hash([toBufferBE(amount, 32), secret.toBuffer()]);
const noteHash = Fr.fromBuffer(hash([toBufferBE(storageSlot, 32), innerNoteHash]));

const result = await runSimulator({
origin: contractAddress,
abi,
args: [amount, secret, recipient],
});

// Check a nullifier has been created.
const newNullifiers = result.callStackItem.publicInputs.newNullifiers.filter(field => !field.equals(Fr.ZERO));
expect(newNullifiers).toHaveLength(1);

// Check the read request was created successfully.
const readRequests = result.callStackItem.publicInputs.readRequests.filter(field => !field.equals(Fr.ZERO));
expect(readRequests).toHaveLength(1);
expect(readRequests[0]).toEqual(noteHash);
}, 30_000);
});

describe('nested calls', () => {
Expand Down Expand Up @@ -373,9 +401,7 @@ describe('Private Execution test suite', () => {
const wasm = await CircuitsWasm.get();
const secret = new Fr(1n);
const secretHash = computeSecretMessageHash(wasm, secret);
const commitment = Fr.fromBuffer(
pedersenPlookupCommitInputs(wasm, [toBufferBE(amount, 32), secretHash.toBuffer()]),
);
const commitment = Fr.fromBuffer(hash([toBufferBE(amount, 32), secretHash.toBuffer()]));
const siloedCommitment = siloCommitment(wasm, contractAddress, commitment);

const tree = await insertLeaves([siloedCommitment.toBuffer()]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use dep::std::hash::sha256;

// Computes a content hash of a deposit/mint message.
fn get_mint_content_hash(amount: Field, owner_address: Field, canceller: Field) -> pub Field {
let mut hash_bytes: [u8; 100] = [0; 100];
let amount_bytes = amount.to_be_bytes(32);
let recipient_bytes = owner_address.to_be_bytes(32);
let canceller_bytes = canceller.to_be_bytes(32);

for i in 0..32 {
hash_bytes[i + 4] = amount_bytes[i];
hash_bytes[i + 36] = recipient_bytes[i];
hash_bytes[i + 68] = canceller_bytes[i];
}

// Function selector: 0xeeb73071 keccak256('mint(uint256,bytes32,address)')
hash_bytes[0] = 0xee;
hash_bytes[1] = 0xb7;
hash_bytes[2] = 0x30;
hash_bytes[3] = 0x71;

let content_sha256 = sha256(hash_bytes);

// // Convert the content_sha256 to a field element
let mut v = 1;
let mut high = 0 as Field;
let mut low = 0 as Field;

for i in 0..16 {
high = high + (content_sha256[15 - i] as Field) * v;
low = low + (content_sha256[16 + 15 - i] as Field) * v;
v = v * 256;
}

// Abuse that a % p + b % p = (a + b) % p and that low < p
let content_hash = low + high * v;
content_hash
}

// Computes a content hash of a withdraw message.
fn get_withdraw_content_hash(amount: Field, recipient: Field, callerOnL1: Field) -> pub Field {
// Compute the content hash
// Compute sha256(selector || amount || recipient)
// then convert to a single field element
// add that to the l2 to l1 messages
let mut hash_bytes: [u8; 100] = [0; 100];
let amount_bytes = amount.to_be_bytes(32);
let recipient_bytes = recipient.to_be_bytes(32);
let callerOnL1_bytes = callerOnL1.to_be_bytes(32);

// 0xb460af94, selector for "withdraw(uint256,address,address)"
hash_bytes[0] = 0xb4;
hash_bytes[1] = 0x60;
hash_bytes[2] = 0xaf;
hash_bytes[3] = 0x94;

for i in 0..32 {
hash_bytes[i + 4] = amount_bytes[i];
hash_bytes[i + 36] = recipient_bytes[i];
hash_bytes[i + 68] = callerOnL1_bytes[i];
}
let content_sha256 = sha256(hash_bytes);

// Convert the content_sha256 to a field element
let mut v = 1;
let mut high = 0 as Field;
let mut low = 0 as Field;

for i in 0..16 {
high = high + (content_sha256[15 - i] as Field) * v;
low = low + (content_sha256[16 + 15 - i] as Field) * v;
v = v * 256;
}

// Abuse that a % p + b % p = (a + b) % p and that low < p
let content = low + high * v;
content
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod hash;
mod storage;

// Represents an ERC20 token bridged from L1 to L2 via `l1-contracts/test/portals/TokenPortal.sol`. The bridged user
Expand All @@ -18,14 +19,12 @@ contract NonNativeToken {
// Libs
use dep::token_utils::{
balance_utils,
value_note::{
value_note::ValueNote,
utils::spend_notes,
}
value_note::utils::{send_note, spend_notes},
};
use dep::custom_notes::transparent_note::TransparentNote;

use crate::storage::Storage;
use crate::hash::{get_mint_content_hash, get_withdraw_content_hash};

use dep::aztec::context::Context;
use dep::aztec::types::point::Point;
Expand All @@ -40,17 +39,13 @@ contract NonNativeToken {
// oracles
use dep::aztec::oracle::{
create_l2_to_l1_message::create_l2_to_l1_message,
create_commitment::create_commitment,
create_nullifier::create_nullifier,
get_commitment::get_commitment,
};

// public messaging
use dep::aztec::messaging::consume_l1_to_l2_message_public;
use dep::aztec::public_call_stack_item::PublicCallStackItem;

use dep::aztec::log::emit_encrypted_log;
use dep::aztec::note::note_getter::NoteGetterOptions;
use dep::aztec::oracle::create_commitment::create_commitment;

fn constructor(
//*********************************/
Expand All @@ -64,12 +59,8 @@ contract NonNativeToken {

let mut context = Context::new(inputs, abi::hash_args([initial_supply, owner.x, owner.y]));

let owner_balance = storage.balances.at(owner.x);
let note = ValueNote::new(initial_supply, owner);

// Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call.
context = owner_balance.insert(context, note);
context = emit_encrypted_log(context, inputs.call_context.storage_contract_address, owner_balance.storage_slot, note.owner, note.serialise());
let balance = storage.balances.at(owner.x);
context = send_note(context, inputs, balance, initial_supply, owner);

// Return private circuit public inputs. All private functions need to return this as it is part of the input of the private kernel.
context.finish()
Expand Down Expand Up @@ -97,25 +88,19 @@ contract NonNativeToken {
amount, owner.x, owner.y, owner_address, msg_key, secret, canceller
]));

let content_hash = _get_mint_content_hash(amount, owner_address, canceller);
let content_hash = get_mint_content_hash(amount, owner_address, canceller);

// Get the l1 message from an oracle call
let updated_context = context.consume_l1_to_l2_message(inputs, msg_key, content_hash, secret);
context = updated_context;

let owner_balance = storage.balances.at(owner.x);
let note = ValueNote::new(amount, owner);

// Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call.
context = owner_balance.insert(context, note);
context = emit_encrypted_log(context, inputs.call_context.storage_contract_address, owner_balance.storage_slot, note.owner, note.serialise());
let balance = storage.balances.at(owner.x);
context = send_note(context, inputs, balance, amount, owner);

// Return private circuit public inputs. All private functions need to return this as it is part of the input of the private kernel.
context.finish()
}



// Withdraws using user's private balance.
// @dev Destroys 2 of user's notes and sends a message to the L1 portal contract. That message can then be consumed
// by calling the `withdraw` function on the L1 portal contract (assuming the args are set correctly).
Expand All @@ -137,7 +122,7 @@ contract NonNativeToken {
let sender_balance = storage.balances.at(sender.x);
context = spend_notes(context, inputs, sender_balance, amount, sender);

let content = _get_withdraw_content_hash(amount, recipient, callerOnL1);
let content = get_withdraw_content_hash(amount, recipient, callerOnL1);
context = context.message_portal(content);

// Return private circuit public inputs. All private functions need to return this as it is part of the input of the private kernel.
Expand All @@ -162,7 +147,7 @@ contract NonNativeToken {
let storage = Storage::init();
let public_balances = storage.public_balances;

let content_hash = _get_mint_content_hash(amount, owner_address, canceller);
let content_hash = get_mint_content_hash(amount, owner_address, canceller);

// Consume message and emit nullifier
consume_l1_to_l2_message_public(inputs, msg_key, content_hash, secret);
Expand Down Expand Up @@ -199,7 +184,7 @@ contract NonNativeToken {
}
// TODO: Revert if there is not enough balance

let content = _get_withdraw_content_hash(amount, recipient, callerOnL1);
let content = get_withdraw_content_hash(amount, recipient, callerOnL1);

// Emit the l2 to l1 message
create_l2_to_l1_message(content);
Expand All @@ -226,17 +211,8 @@ contract NonNativeToken {
let sender_balance = storage.balances.at(sender.x);
context = spend_notes(context, inputs, sender_balance, amount, sender);

// Creates 2 new notes, one for the recipient and one for the sender (change note).
let recipient_note = ValueNote::new(amount, recipient);

// Get the recipient's set of notes.
let recipient_balance = storage.balances.at(recipient.x);

// Insert the 2 new notes to the recipient's and sender's sets of notes.
context = recipient_balance.insert(context, recipient_note);

// Emit the newly created encrypted note preimages via oracle calls.
context = emit_encrypted_log(context, inputs.call_context.storage_contract_address, recipient_balance.storage_slot, recipient_note.owner, recipient_note.serialise());
let balance = storage.balances.at(recipient.x);
context = send_note(context, inputs, balance, amount, recipient);

// Return private circuit public inputs. All private functions need to return this as it is part of the input of the private kernel.
context.finish()
Expand Down Expand Up @@ -287,12 +263,8 @@ contract NonNativeToken {
context = public_note.consume_in_secret(context, inputs.roots.private_data_tree_root, secret);

// Mint the tokens
let owner_balance = storage.balances.at(owner.x);
let note = ValueNote::new(amount, owner);

// Insert note and emit encrypted preimage
context = owner_balance.insert(context, note);
context = emit_encrypted_log(context, inputs.call_context.storage_contract_address, owner_balance.storage_slot, note.owner, note.serialise());
let balance = storage.balances.at(owner.x);
context = send_note(context, inputs, balance, amount, owner);

context.finish()
}
Expand Down Expand Up @@ -337,7 +309,6 @@ contract NonNativeToken {
recipient_balance.write(new_balance);
}


/// ABI getBalance type "unconstrained"
fn getBalance(
owner: Point,
Expand All @@ -347,81 +318,4 @@ contract NonNativeToken {

balance_utils::get_balance(owner_balance.storage_slot)
}

// Computes a content hash of a deposit/mint message.
fn _get_mint_content_hash(amount: Field, owner_address: Field, canceller: Field) -> pub Field {
let mut hash_bytes: [u8; 100] = [0; 100];
let amount_bytes = amount.to_be_bytes(32);
let recipient_bytes = owner_address.to_be_bytes(32);
let canceller_bytes = canceller.to_be_bytes(32);

for i in 0..32 {
hash_bytes[i + 4] = amount_bytes[i];
hash_bytes[i + 36] = recipient_bytes[i];
hash_bytes[i + 68] = canceller_bytes[i];
}

// Function selector: 0xeeb73071 keccak256('mint(uint256,bytes32,address)')
hash_bytes[0] = 0xee;
hash_bytes[1] = 0xb7;
hash_bytes[2] = 0x30;
hash_bytes[3] = 0x71;

let content_sha256 = dep::std::hash::sha256(hash_bytes);

// // Convert the content_sha256 to a field element
let mut v = 1;
let mut high = 0 as Field;
let mut low = 0 as Field;

for i in 0..16 {
high = high + (content_sha256[15 - i] as Field) * v;
low = low + (content_sha256[16 + 15 - i] as Field) * v;
v = v * 256;
}

// Abuse that a % p + b % p = (a + b) % p and that low < p
let content_hash = low + high * v;
content_hash
}

// Computes a content hash of a withdraw message.
fn _get_withdraw_content_hash(amount: Field, recipient: Field, callerOnL1: Field) -> pub Field {
// Compute the content hash
// Compute sha256(selector || amount || recipient)
// then convert to a single field element
// add that to the l2 to l1 messages
let mut hash_bytes: [u8; 100] = [0; 100];
let amount_bytes = amount.to_be_bytes(32);
let recipient_bytes = recipient.to_be_bytes(32);
let callerOnL1_bytes = callerOnL1.to_be_bytes(32);

// 0xb460af94, selector for "withdraw(uint256,address,address)"
hash_bytes[0] = 0xb4;
hash_bytes[1] = 0x60;
hash_bytes[2] = 0xaf;
hash_bytes[3] = 0x94;

for i in 0..32 {
hash_bytes[i + 4] = amount_bytes[i];
hash_bytes[i + 36] = recipient_bytes[i];
hash_bytes[i + 68] = callerOnL1_bytes[i];
}
let content_sha256 = dep::std::hash::sha256(hash_bytes);

// Convert the content_sha256 to a field element
let mut v = 1;
let mut high = 0 as Field;
let mut low = 0 as Field;

for i in 0..16 {
high = high + (content_sha256[15 - i] as Field) * v;
low = low + (content_sha256[16 + 15 - i] as Field) * v;
v = v * 256;
}

// Abuse that a % p + b % p = (a + b) % p and that low < p
let content = low + high * v;
content
}
}

This file was deleted.

Loading

0 comments on commit f656079

Please sign in to comment.