-
Notifications
You must be signed in to change notification settings - Fork 27
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
[Social Cipher] Profile.io proof personal information with NFT (age-verification) #17
base: main
Are you sure you want to change the base?
Changes from 11 commits
f610a96
a81da19
736af1f
faf06bf
0145de6
b655d1c
cf27b05
a00c24a
8115a8c
6c7476e
1cbcd6b
4cb9e5f
245d580
2251aad
506b551
771d0cf
e56e69b
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 |
---|---|---|
@@ -1,3 +1,6 @@ | ||
# Profile.io | ||
test | ||
|
||
# Alpha Build 2 | ||
|
||
Tracking issues and projects for Alpha Build 2 grants program. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "zkpTestContract" | ||
type = "contract" | ||
authors = [""] | ||
compiler_version = ">=0.33.0" | ||
|
||
[dependencies] | ||
aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="aztec-packages-v0.57.0", directory="noir-projects/aztec-nr/aztec" } | ||
value_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="aztec-packages-v0.57.0", directory="noir-projects/aztec-nr/value-note"} | ||
easy_private_state = { git="https://github.com/AztecProtocol/aztec-packages/", tag="aztec-packages-v0.57.0", directory="noir-projects/aztec-nr/easy-private-state"} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Profile.io: privacy preserving identity, nationality and age verification | ||
|
||
Using a profile.io user's indentity verification KYC results to prove user's adult verification and/or nationality, gender, any other user's personal information without revealing personal data by using ~~Aztec Connect~~ Noir circuit ZKP. Scoping service for third parties to verify such information privately, either through profile.io/verify or their own user flows (eg. exploring potentially using Frames.js). | ||
|
||
|
||
## Challenge Selection | ||
|
||
- [ ] ZKEmail Guardian | ||
- [x] Social Cipher | ||
|
||
**Note**: You can change which challenges you've selected during the competition if you'd like. You are not bound by your choices or descriptions entered during the one week check-in. | ||
|
||
## Team information | ||
|
||
[Profile.io](https://www.profile.io/) | ||
|
||
## Technical Approach | ||
|
||
We'd like to use Aztec ZKP for proving adult verification (or any other user info) on our app. | ||
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. Would be worth understand where the assumptions begin. Eg, are you assuming a public NFT has a verified age indication, and you want to use that? That is, proof of possession of that NFT, without revealing which NFT? 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. My idea is user has a private NFT that contains user's personal info such as age, nationalities, eye colour, etc as a note. When a verifier (another user) requests user's info (ex: isEuropean? isAdult? isFemale?), the NFT holder sends to the verifier by encrypting with verifier's address without revealing other sensitive info. The verifier is the only one who can open and see the info. But that's my theory and I am on the way to figuring out how to implement with Aztec tech. |
||
~~We have already smart contract that mints NFTs on Polygon mainnet~~ | ||
|
||
The plan is to use ZKP and Aztec Note when user inputs his/her identity including age then the user can use it when the age verification is required without revealing details. | ||
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. Will be worth digging a little deeper into what you want to be public/private in terms of inputs and state. 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. I discovered that using Aztec Connect is not recommended. Therefore, I had to change the plan. I will update the readme. |
||
By default, all user information is private. When a verifier request proof, the user can select data that want to reveal and send. Only the verifier can open and see the requested data. | ||
|
||
Here is the user flow: | ||
1. In FE (frontend), a user inputs his birthdate (ex: 20/04/1995) | ||
1. The FE calls Aztec contract to mint a private NFT with the Note which has his birthdate (at this step, PXE creates ZKP when a private function is called on the Aztec contract if I am understood properly) | ||
1. Now the user owns a private NFT that has a note | ||
1. Another user (verifier) wants to verify if the user is an adult or not. So the verifier sends an age-verification-request with his address. | ||
1. The user doesn't want to reveal his exact age but can prove whether he is adult or not by ~~using ZKP (or Note?)~~ sending a note that is encrypted with verifier's address. | ||
1. There is a "request age-verification" button on the UI and when verifier clicks that button -> Calling Aztec contract to request the user's age verification -> If the user accept the request -> FE displays whether the user is an adult or not on the UI. | ||
|
||
|
||
## Some technical questions | ||
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. For (a) and (b) below, you should be able to find relevant information in the docs. For (a), I think you could even just "Ask Aztec AI" (appears in the bottom right corner of the docs). 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. I could find the answers although I have many other questions still. I will update the readme. |
||
a. At above step 2, the created Note is an actual ZKP? I am confused the relation between Note and ZKP? | ||
|
||
b. At above step 6, can Aztec contract retrieve age-NFT data without the NFT owner's permission? Do I need the "Witness"? | ||
|
||
c. At above step 6, if Aztec contract can retrieve age-NFT data (birthdate), can the Aztec contract process calculation of the Note data? For example, `isUserAdult(note)`, `isUserOver50(note)`, `isUserOver70(note)`. | ||
|
||
## Expected Outcomes | ||
* A user should be able to add personal encrypted data and only the user can see the data by default. | ||
|
||
* The user should be able to mint NFTs that contains encrypted personal data | ||
|
||
* A verifier can request to personal information of users such as age, nationalities, gender, etc (For the first implementation, whether user is over 18 years or not). | ||
|
||
* The user can accept the request and send note that only can be opened by the verifier. | ||
|
||
Providing a quick age verification feature. | ||
|
||
## Lessons Learned (For Submission) | ||
|
||
- What are the most important takeaways from your project? | ||
- Are there any patterns or best practices that you've learned that would be useful for other projects? | ||
- Highlight reusable code patterns, key code snippets, and best practices - what are some of the ‘lego bricks’ you’ve built, and how could someone else best use them? | ||
|
||
## Project Links (For Submission) | ||
|
||
Please provide links to any relevant documentation, code, or other resources that you've used in your project. | ||
|
||
## Video Demo (For Submission) | ||
|
||
Please provide a link to a video demo of your project. The demo should be no longer than 5 minutes and should include a brief intro to your team and your project. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use dep::aztec::macros::aztec; | ||
|
||
#[aztec] | ||
contract Profile { | ||
use dep::aztec::{ | ||
note::utils::compute_note_hash_for_nullify, keys::getters::get_nsk_app, oracle::random::random, | ||
encrypted_logs::encrypted_note_emission::encode_and_encrypt_note, | ||
note::constants::MAX_NOTES_PER_PAGE, | ||
keys::getters::get_public_keys, prelude::{AztecAddress, PrivateSet, SharedImmutable, Map, NullifiableNote, NoteHeader, PrivateContext, NoteViewerOptions}, | ||
protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, hash::poseidon2_hash_with_separator, traits::{Empty, Eq}}, | ||
macros::notes::partial_note | ||
}; | ||
use dep::value_note::balance_utils; | ||
use dep::easy_private_state::EasyPrivateUint; | ||
use dep::aztec::macros::{storage::storage, functions::{initializer, private}}; | ||
use dep::value_note::value_note::ValueNote; | ||
|
||
// TODO: remove from this file and import from outside | ||
#[partial_note(quote { token_id})] | ||
struct ProfileNFT { | ||
token_id: Field, | ||
is_adult: bool, | ||
npk_m_hash: Field, | ||
} | ||
|
||
// TODO: remove from this file and import from outside | ||
impl ProfileNFT { | ||
pub fn new(token_id: Field, is_adult: bool, npk_m_hash: Field) -> Self { | ||
ProfileNFT { token_id, is_adult, npk_m_hash, header: NoteHeader::empty() } | ||
} | ||
} | ||
|
||
// TODO: remove from this file and import from outside | ||
impl Eq for ProfileNFT { | ||
fn eq(self, other: Self) -> bool { | ||
(self.token_id == other.token_id) | ||
& (self.npk_m_hash == other.npk_m_hash) | ||
} | ||
} | ||
|
||
// TODO: remove from this file and import from outside | ||
impl NullifiableNote for ProfileNFT { | ||
fn compute_nullifier(self, context: &mut PrivateContext, note_hash_for_nullify: Field) -> Field { | ||
let secret = context.request_nsk_app(self.npk_m_hash); | ||
poseidon2_hash_with_separator( | ||
[ | ||
note_hash_for_nullify, | ||
secret | ||
], | ||
GENERATOR_INDEX__NOTE_NULLIFIER as Field | ||
) | ||
} | ||
|
||
// TODO: remove from this file and import from outside | ||
unconstrained fn compute_nullifier_without_context(self) -> Field { | ||
let note_hash_for_nullify = compute_note_hash_for_nullify(self); | ||
let secret = get_nsk_app(self.npk_m_hash); | ||
poseidon2_hash_with_separator( | ||
[ | ||
note_hash_for_nullify, | ||
secret | ||
], | ||
GENERATOR_INDEX__NOTE_NULLIFIER as Field | ||
) | ||
} | ||
} | ||
|
||
#[storage] | ||
struct Storage<Context> { | ||
profile_nfts: Map<AztecAddress, PrivateSet<ProfileNFT, Context>, Context>, | ||
} | ||
|
||
/** | ||
* initialize the contract's initial state variables. | ||
*/ | ||
#[private] | ||
#[initializer] | ||
fn constructor(initial_supply: u64, owner: AztecAddress, outgoing_viewer: AztecAddress) { | ||
// leave it in case | ||
} | ||
|
||
// TODO: requester might not need since there is msg.sender | ||
#[public] | ||
fn request_age_verification(target_user: AztecAddress, requester: AztecAddress) { | ||
// WIP | ||
} | ||
|
||
#[private] | ||
fn mintNFT(to: AztecAddress, token_id: Field, is_adult: bool) { | ||
let profile_nfts = storage.profile_nfts; | ||
let to_keys = get_public_keys(to); | ||
let mut nft_note = ProfileNFT::new(token_id, is_adult, to_keys.npk_m.hash()); | ||
|
||
// TODO: sending an event to the NFT owner after update storage. Is this meaningful? Can it be a proof? | ||
// TODO: emitting event is a way to send message to someone? | ||
profile_nfts.at(to).insert(&mut nft_note).emit(encode_and_encrypt_note( | ||
&mut context, | ||
to_keys.ovpk_m, | ||
to_keys.ivpk_m, | ||
to | ||
)); | ||
} | ||
|
||
// get nft note info such as token_id, is_adult | ||
unconstrained fn get_profile_nfts(owner: AztecAddress, page_index: u32) -> [(Field, bool); 10] { | ||
let offset = page_index * MAX_NOTES_PER_PAGE; | ||
let mut options = NoteViewerOptions::new(); | ||
let profile_nfts = storage.profile_nfts; | ||
let notes = profile_nfts.at(owner).view_notes(options.set_offset(offset)); | ||
let mut owned_nft_ids = [(0, false); MAX_NOTES_PER_PAGE]; | ||
|
||
for i in 0..options.limit { | ||
if i < notes.len() { | ||
owned_nft_ids[i] = (notes.get_unchecked(i).token_id, notes.get_unchecked(i).is_adult); // returns as [token_id, is_adult] | ||
} | ||
} | ||
|
||
(owned_nft_ids) | ||
} | ||
} |
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.
could you please remove this change from the PR?
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.
Done