-
Notifications
You must be signed in to change notification settings - Fork 6
proof of note creation #9
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
node_modules | ||
target | ||
codegenCache.json | ||
artifacts | ||
dist |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "circuits" | ||
type = "bin" | ||
authors = [""] | ||
|
||
[dependencies] | ||
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v2.0.3", directory = "noir-projects/aztec-nr/aztec" } | ||
poseidon = { tag = "v0.1.1", git = "https://github.com/noir-lang/poseidon" } | ||
uint_note = { path = "../uint-note" } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
settled_note_hash: "0x109ebaffd4e6aa4e4c305606addd352dd432d37fd5541b67e86e7310cdf80053" | ||
contract_address: "0x0f140443f51fe2a1b9ca04b279cda12e2bbdd073efb6b09aed79a6a4315957d0" | ||
recipient: "0x116586dbcc81434e8b0727cbe282be01271886275e347489947af5d1b94618a4" | ||
randomness: "0x0000000000000000000000000000000000000000000000000000000000001b39", | ||
value: 69, | ||
storage_slot: "0x134f661dca40dad62341c230269bbed01da64b506e8a0e6e0a452a9262023012" | ||
note_nonce: "0x0fe225cdf1421a3266faf50f46025b73bb57858d10f7cd62875b47e988c64edd" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use dep::aztec::protocol_types::{ | ||
address::AztecAddress, | ||
constants::{GENERATOR_INDEX__SILOED_NOTE_HASH, GENERATOR_INDEX__UNIQUE_NOTE_HASH}, | ||
traits::FromField, | ||
}; | ||
use dep::aztec::note::note_interface::NoteHash; | ||
use dep::poseidon; | ||
|
||
use dep::uint_note::uint_note::UintNote; | ||
|
||
fn main( | ||
settled_note_hash: pub Field, | ||
contract_address: pub Field, | ||
recipient: pub Field, | ||
randomness: Field, | ||
value: pub u128, | ||
storage_slot: Field, | ||
note_nonce: Field, | ||
) { | ||
let note = UintNote::new_with_randomness(value, AztecAddress::from_field(recipient), randomness); | ||
let note_hash = note.compute_note_hash(storage_slot); | ||
|
||
let siloed_note_hash = poseidon::poseidon2::Poseidon2::hash([GENERATOR_INDEX__SILOED_NOTE_HASH as Field, contract_address, note_hash], 3); | ||
let unique_note_hash = poseidon::poseidon2::Poseidon2::hash([GENERATOR_INDEX__UNIQUE_NOTE_HASH as Field, note_nonce, siloed_note_hash], 3); | ||
|
||
assert_eq(settled_note_hash, unique_note_hash); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,16 @@ | ||||||
This proves note hash delivery | ||||||
|
||||||
Prerequisites: | ||||||
aztec-up @ 2.0.3 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a link to the docs on how to install aztec-up |
||||||
|
||||||
Steps: | ||||||
1. aztec-nargo compile in circuits/ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you wrap the commands in backticks (`) so that they show up as code blocks? it makes it more clear |
||||||
2. aztec-nargo compile in sample-contract | ||||||
3. aztec-postprocess-contract in sample-contract | ||||||
4. yarn codegen in scripts | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
4. yarn copy-target in scripts | ||||||
5. aztec start --sandbox in terminal 2 | ||||||
6. yarn start in scripts | ||||||
7. copy details outputted by step 6 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where am i supposed to copy this? continuing through, I see its meant to be copy pasted in the web app, but that isnt clear at this point Also i see the output in my terminal like this: ![]() Can you log it in a way that makes it easier to copy-paste? as is, when i double click on the value it highlights this whole line |
||||||
8. yarn dev in vite | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
9. paste details copied in step 7 to the frontend started by step 8 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "getting_started_contract" | ||
authors = [""] | ||
compiler_version = ">=1.0.0" | ||
type = "contract" | ||
|
||
[dependencies] | ||
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v2.0.3", directory = "noir-projects/aztec-nr/aztec" } | ||
uint_note = { path = "../uint-note" } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
use aztec::macros::aztec; | ||
|
||
#[aztec] | ||
pub contract GettingStarted { | ||
use aztec::{ | ||
state_vars::{private_mutable::PrivateMutable, private_set::PrivateSet, public_mutable::PublicMutable, map::Map}, | ||
messages::logs::note::encode_and_encrypt_note_unconstrained, | ||
note::{constants::MAX_NOTES_PER_PAGE, note_viewer_options::NoteViewerOptions}, | ||
|
||
macros::{ | ||
functions::{initializer, private, public, utility, internal}, | ||
storage::storage, | ||
}, | ||
protocol_types::{ | ||
address::AztecAddress, | ||
}, | ||
}; | ||
|
||
use dep::uint_note::uint_note::UintNote; | ||
|
||
#[storage] | ||
struct Storage<Context> { | ||
contract_private_state: PrivateMutable<UintNote, Context>, | ||
user_private_state: Map<AztecAddress, PrivateSet<UintNote, Context>, Context>, | ||
contract_public_state: PublicMutable<u128, Context>, | ||
user_public_state: Map<AztecAddress, PublicMutable<u128, Context>, Context>, | ||
owner: PublicMutable<AztecAddress, Context>, | ||
} | ||
|
||
#[initializer] | ||
#[public] | ||
fn setup() { | ||
storage.owner.write(context.msg_sender()); | ||
} | ||
|
||
#[private] | ||
fn create_note_for_user(value: u128) { | ||
let note_owner = context.msg_sender(); | ||
storage.user_private_state.at(note_owner) | ||
// randomness should actually be random, but is a workaround because we can't recover it now | ||
.insert(UintNote::new_with_randomness(value, note_owner, 6969)) | ||
.emit(encode_and_encrypt_note_unconstrained(&mut context, note_owner)); | ||
} | ||
|
||
#[public] | ||
fn modify_public_state_for_user(value: u128) { | ||
storage.user_public_state.at(context.msg_sender()).write(value); | ||
} | ||
|
||
#[private] | ||
fn modify_private_state_for_contract(value: u128) { | ||
let maybe_contract_owner = context.msg_sender(); | ||
|
||
storage.contract_private_state.initialize_or_replace(UintNote::new(value, maybe_contract_owner)) | ||
.emit(encode_and_encrypt_note_unconstrained(&mut context, maybe_contract_owner)); | ||
|
||
GettingStarted::at(context.this_address())._assert_is_owner(maybe_contract_owner).enqueue(&mut context); | ||
} | ||
|
||
#[public] | ||
#[internal] | ||
fn _assert_is_owner(maybe_owner: AztecAddress) { | ||
assert_eq(maybe_owner, storage.owner.read()); | ||
} | ||
|
||
#[public] | ||
fn modify_public_state_for_contract(value: u128) { | ||
let maybe_contract_owner = context.msg_sender(); | ||
|
||
GettingStarted::at(context.this_address())._assert_is_owner(maybe_contract_owner).call(&mut context); | ||
|
||
storage.user_public_state.at(context.msg_sender()).write(value); | ||
} | ||
|
||
#[utility] | ||
unconstrained fn view_created_notes(owner: AztecAddress) -> BoundedVec<UintNote, MAX_NOTES_PER_PAGE> { | ||
storage.user_private_state.at(owner).view_notes(NoteViewerOptions::new()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "scripts", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"scripts": { | ||
"build": "npx tsc && npm run copy-target-after-build", | ||
"clean": "rimraf dist", | ||
"start": "npm run clean && npm run build && node dist/src/index.js", | ||
"lint": "eslint .", | ||
"codegen": "aztec codegen -f ../sample-contract/target/getting_started_contract-GettingStarted.json && npm run copy-target", | ||
"copy-target": "mkdir -p ./artifacts && cp -r ../sample-contract/target ./artifacts", | ||
"copy-target-after-build": "cp -r ../sample-contract/target ./dist/artifacts" | ||
}, | ||
"dependencies": { | ||
"@aztec/accounts": "2.0.3", | ||
"@aztec/aztec.js": "2.0.3", | ||
"@aztec/foundation": "2.0.3", | ||
"@aztec/stdlib": "2.0.3" | ||
}, | ||
"devDependencies": { | ||
"@eslint/js": "^9.29.0", | ||
"eslint": "^9.29.0", | ||
"rimraf": "^6.0.1", | ||
"typescript": "~5.8.3", | ||
"typescript-eslint": "^8.34.1" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { GettingStartedContract } from '../artifacts/target/GettingStarted.js'; | ||
import { | ||
createPXEClient, | ||
waitForPXE, | ||
createAztecNodeClient, | ||
Fr, | ||
} from '@aztec/aztec.js'; | ||
import { getInitialTestAccountsWallets } from '@aztec/accounts/testing'; | ||
import { computeNoteHashNonce, computeUniqueNoteHash, deriveStorageSlotInMap, siloNoteHash } from '@aztec/stdlib/hash'; | ||
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; | ||
|
||
const NOTE_HASH_SEPARATOR = 1; | ||
|
||
export const SANDBOX_URL = 'http://localhost:8080'; | ||
|
||
const pxe = createPXEClient('http://localhost:8080'); | ||
await waitForPXE(pxe); | ||
|
||
const wallets = await getInitialTestAccountsWallets(pxe); | ||
const deployerWallet = wallets[0]; | ||
const deployerAddress = deployerWallet.getAddress(); | ||
|
||
const gettingStarted = await GettingStartedContract.deploy(deployerWallet).send({ | ||
from: deployerAddress, | ||
}).wait(); | ||
|
||
console.log('CONTRACT DEPLOYED AT', gettingStarted.contract.address); | ||
|
||
const NOTE_VALUE = 69; | ||
|
||
const tx = gettingStarted.contract.methods.create_note_for_user(NOTE_VALUE); | ||
|
||
const txExecutionRequest = await tx.create(); | ||
|
||
const txRequestHash = await txExecutionRequest.toTxRequest().hash(); | ||
|
||
console.log('TX REQUEST HASH', txRequestHash); | ||
|
||
const sentTx = await tx.send({ from: deployerAddress }).wait(); | ||
|
||
const node = createAztecNodeClient(SANDBOX_URL); | ||
|
||
const txEffect = await node.getTxEffect(sentTx.txHash); | ||
|
||
if (txEffect === undefined) { | ||
throw new Error('Cannot find txEffect from tx hash'); | ||
} | ||
|
||
const storageSlot = await deriveStorageSlotInMap(GettingStartedContract.storage.user_private_state.slot, deployerAddress); | ||
|
||
const NOTE_RANDOMNESS = new Fr(6969); | ||
|
||
const commitment = await poseidon2HashWithSeparator([deployerAddress.toField(), NOTE_RANDOMNESS, storageSlot], NOTE_HASH_SEPARATOR); | ||
|
||
const noteHash = await poseidon2HashWithSeparator([commitment, new Fr(NOTE_VALUE)], NOTE_HASH_SEPARATOR); | ||
|
||
const INDEX_OF_NOTE_HASH_IN_TRANSACTION = 0; | ||
|
||
const nonceGenerator = txEffect?.data.nullifiers[0] ?? txRequestHash; | ||
|
||
const noteHashNonce = await computeNoteHashNonce(nonceGenerator, INDEX_OF_NOTE_HASH_IN_TRANSACTION); | ||
|
||
const siloedNoteHash = await siloNoteHash(gettingStarted.contract.address, noteHash); | ||
|
||
const computedUniqueNoteHash = await computeUniqueNoteHash( | ||
noteHashNonce, | ||
siloedNoteHash, | ||
); | ||
|
||
console.log('NOTE HASH', noteHash) | ||
console.log('NONCE GENERATOR', nonceGenerator); | ||
console.log('NONCE', noteHashNonce); | ||
console.log('SILOED NOTE HASH', siloedNoteHash); | ||
console.log('COMPUTED UNIQUE NOTE HASH', computedUniqueNoteHash); | ||
console.log('ACTUAL UNIQUE NOTE HASH', txEffect.data.noteHashes[0]); | ||
|
||
console.log('REQUIRED INPUT', { | ||
settled_note_hash: txEffect.data.noteHashes[0], | ||
contract_address: gettingStarted.contract.address, | ||
recipient: deployerAddress, | ||
randomness: NOTE_RANDOMNESS, | ||
value: NOTE_VALUE, | ||
storage_slot: storageSlot, | ||
note_nonce: noteHashNonce, | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ | ||
"module": "nodenext", /* Specify what module code is generated. */ | ||
"moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */ | ||
"resolveJsonModule": true, /* Enable importing .json files. */ | ||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ | ||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ | ||
"strict": true, /* Enable all strict type-checking options. */ | ||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */ | ||
"outDir": "dist" | ||
}, | ||
"include": [ "src/**/*", "artifacts/**/*"] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add some more context. Why would someone want to do this? It might be helpful to explain the question that instigated the development of this example.