-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit afe8cb4
Showing
12 changed files
with
2,603 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
defi-wonderland/aztec-codeowner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# PM files: | ||
node_modules | ||
yarn-error.log | ||
|
||
# build | ||
target/ | ||
|
||
# MacOS frens: | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "privateoracle" | ||
type = "contract" | ||
authors = ["wonderland"] | ||
compiler_version = "0.9.0" | ||
|
||
[dependencies] | ||
aztec = {path = "./node_modules/aztec-packages/yarn-project/aztec-nr/aztec"} | ||
value_note = {path = "./node_modules/aztec-packages/yarn-project/aztec-nr/value-note"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# aztec-contracts | ||
|
||
|
||
noirup -v aztec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "private-oracle", | ||
"version": "0.0.0", | ||
"type": "module", | ||
"license": "MIT", | ||
"types": "module", | ||
"scripts": { | ||
"test": "mocha --require ts-node/register 'src/**/*.test.ts'" | ||
}, | ||
"dependencies": { | ||
"@aztec/aztec.js": "^0.7.10", | ||
"@types/mocha": "^10.0.2", | ||
"aztec-packages": "https://github.com/AztecProtocol/aztec-packages", | ||
"mocha": "^10.2.0", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^5.2.2" | ||
}, | ||
"mocha": { | ||
"require": "ts-node/register", | ||
"extension": [ | ||
"ts" | ||
], | ||
"spec": "./src/**/*.test.ts" | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
mod types; | ||
|
||
/** | ||
This first version is "free for use" (no fee) and "free for all" (anyone can answer to any question). | ||
Overall design: | ||
- Requester Alice: I would like Bob to tell me X. Here is the fee. | ||
- Bob: Here is the answer Y to Alice, the fee is now mine | ||
or | ||
- Alice: nvm, I'm good, cancel and give me my money back | ||
Bob is whoever Alice wants. | ||
If Charlie asks X to Bob, Bob will answer Y too. | ||
Eve cannot know anything beside: the token to use, the fee amount | ||
Bob can withdraw it's token whenever. | ||
Side-side-quest | ||
If possible, Bob can post a proof of knowledge ("here is the proof that the thumbnail Z correspond to the high res picture Y") | ||
Tech TL;DR with stuff to clarify: | ||
Create request: | ||
- Fee: value_note balanceOf[Alice] += delta token | ||
- Request: | ||
-- Alice request notes: { request, Alice PK, Bob, randomSalt } - nullifier: Pedersen(note_hash, randomSalt) | ||
-- Bob request notes: adding the same note - nullifier is the same (P(note_hash, randomSalt)) | ||
Answer: | ||
- Fee: Should be the same nullifier pattern (then nullify Alice value_note and create for Bob)? | ||
- Check if previously answered (then reuse it, if not, use the answer passed as arg) | ||
- Replace note for a note with the answer | ||
- Add note to Alice notes, with the answer | ||
--> nullifier should be different here -> another note type (answer_note) | ||
*/ | ||
|
||
contract PrivateOracle { | ||
use crate::types::question_note:: {QuestionNote, QuestionNoteMethods, QUESTION_NOTE_LEN}; | ||
use crate::types::answer_note:: {AnswerNote, AnswerNoteMethods, ANSWER_NOTE_LEN}; | ||
|
||
use dep::std::option::Option; | ||
|
||
use dep::aztec::context::Context; | ||
use dep::aztec::log::emit_encrypted_log; | ||
use dep::aztec::note::{ | ||
note_header::NoteHeader, | ||
note_interface::NoteInterface, | ||
note_getter_options::NoteGetterOptions, | ||
note_viewer_options::NoteViewerOptions, | ||
utils::compute_note_hash_for_read_or_nullify, | ||
}; | ||
use dep::aztec::oracle::{ | ||
get_public_key::get_public_key, | ||
rand::rand | ||
}; | ||
use dep::aztec::state_vars::public_state::PublicState; | ||
use dep::aztec::state_vars::map::Map; | ||
use dep::aztec::state_vars::set::Set; | ||
use dep::aztec::state_vars::singleton::Singleton; | ||
use dep::aztec::state_vars::immutable_singleton::ImmutableSingleton; | ||
use dep::aztec::types::type_serialization::field_serialization::{ FieldSerializationMethods, FIELD_SERIALIZED_LEN}; | ||
|
||
struct Storage { | ||
// payment_token: PublicState<Field, FIELD_SERIALIZED_LEN>, | ||
// fee: PublicState<Field, FIELD_SERIALIZED_LEN>, | ||
// stored_balance: PublicState<Field, FIELD_SERIALIZED_LEN>, | ||
// balance_of: Map<Set<ValueNote, VALUE_NOTE_LEN>>, | ||
questions: Set<QuestionNote, QUESTION_NOTE_LEN>, | ||
answers: Set<AnswerNote, ANSWER_NOTE_LEN> // TODO: use a set of immutable singletons | ||
} | ||
|
||
impl Storage { | ||
fn init(context: Context) -> pub Self { | ||
Storage { | ||
// -- Public -- | ||
|
||
// -- Private -- | ||
// Set of questions | ||
questions: Set::new(context, 6, QuestionNoteMethods), | ||
// Set of answers | ||
answers: Set::new(context, 7, AnswerNoteMethods) | ||
} | ||
} | ||
} | ||
|
||
// Constructs the contract. | ||
#[aztec(private)] | ||
fn constructor() { | ||
} | ||
|
||
// Requester submit a question | ||
#[aztec(private)] | ||
fn submit_question(question: Field, divinity_address: Field) { | ||
let storage = Storage::init(Context::private(&mut context)); | ||
let address_this = context.this_address(); | ||
let sender = context.msg_sender(); | ||
|
||
// Assert if question doesn't already exist from the same requester | ||
let question_getter_option = NoteGetterOptions::new().select(0, question).set_limit(1); | ||
let question_note = storage.questions.get_notes(question_getter_option)[0]; | ||
assert(question_note.is_none()); | ||
|
||
// Store the question in the requester notes and send it to the oracle notes too | ||
let random_nullifier_shared_key = rand(); | ||
|
||
let mut newquestion: QuestionNote = QuestionNote::new( | ||
question, | ||
sender, | ||
divinity_address, | ||
random_nullifier_shared_key | ||
); | ||
|
||
storage.questions.insert(&mut newquestion); | ||
|
||
// Encrypt the question in the divinity notes | ||
let divinity_pub_key = get_public_key(divinity_address); | ||
let questionsstorage_slot = storage.questions.storage_slot; | ||
|
||
let encrypted = newquestion.serialize(); | ||
|
||
emit_encrypted_log( | ||
&mut context, | ||
address_this, | ||
questionsstorage_slot, | ||
divinity_pub_key, | ||
encrypted, | ||
); | ||
} | ||
|
||
|
||
// Oracle submit an answer to a given question | ||
#[aztec(private)] | ||
fn submit_answer(question: Field, answer: Field) { | ||
let storage = Storage::init(Context::private(&mut context)); | ||
let caller = context.msg_sender(); | ||
|
||
// Filter request note to check if existing (either wrong request or already answered) and accesible for this divinity (should't happen) | ||
let request_filter = NoteGetterOptions::new() | ||
.select(0, question) | ||
.select(3, caller) | ||
.set_limit(1); | ||
|
||
// Get the question note | ||
let request = storage.questions.get_notes(request_filter)[0].unwrap_unchecked(); | ||
|
||
assert(request.request == question); | ||
assert(request.divinity_address == caller); | ||
|
||
let mut answer_to_keep = 0; | ||
|
||
// Check if this question was previously answered | ||
let answerFilter = NoteGetterOptions::new() | ||
.select(0, question) | ||
.set_limit(1); | ||
|
||
|
||
let answerNote = storage.answers.get_notes(answerFilter)[0].unwrap_unchecked(); | ||
|
||
// TODO: this check might not be hit (fail before) if the note is uninit -> filter fn using is_some()? | ||
// This question has already been answered? | ||
if answerNote.answer != 0 { | ||
assert(answerNote.request == question); | ||
assert(answerNote.owner == caller); | ||
|
||
// Update the answer to the preexisting one | ||
answer_to_keep = answerNote.answer; | ||
// If not, use the answer passed as arg | ||
} else { | ||
// Create a new note | ||
answer_to_keep = answer; | ||
} | ||
|
||
// Store the answer in both sets (requester and oracle) | ||
let mut new_answer: AnswerNote = AnswerNote::new( | ||
question, | ||
answer, | ||
caller | ||
); | ||
|
||
storage.answers.insert(&mut new_answer); | ||
|
||
// Encrypt the answer in the requester notes | ||
let requester_pub_key = get_public_key(request.requester_address); | ||
let answers_storage_slot = storage.answers.storage_slot; | ||
|
||
let encrypted = new_answer.serialize(); | ||
|
||
emit_encrypted_log( | ||
&mut context, | ||
caller, | ||
answers_storage_slot, | ||
requester_pub_key, | ||
encrypted, | ||
); | ||
} | ||
|
||
// Requester consults the answer to one of their questions | ||
#[aztec(private)] | ||
fn consult_answer(question: Field) -> Field { | ||
|
||
let storage = Storage::init(Context::private(&mut context)); | ||
let caller = context.msg_sender(); | ||
|
||
// create the answer_getter_option: select answer and limit | ||
let answer_getter_option = NoteGetterOptions::new().select(0, question).select(2, caller).set_limit(1); | ||
|
||
// filter the notes - get_notes returns the note **that the account has access to** (if not, add it to the Option) | ||
let answered_note = storage.answers.get_notes(answer_getter_option)[0].unwrap_unchecked(); | ||
|
||
// constrain (owner == caller and _qeustion==note.request) | ||
assert(answered_note.owner == caller); | ||
assert(answered_note.request == question); | ||
|
||
// Return the answer | ||
answered_note.answer | ||
} | ||
|
||
// Requester cancels a question - no real utility here, will be usefull when request bound an amount, waiting for the answer | ||
#[aztec(private)] | ||
fn cancel_question(question: Field) { | ||
let storage = Storage::init(Context::private(&mut context)); | ||
let caller = context.msg_sender(); | ||
|
||
// create the Answer_getter_option: SELECT by address and max number of requests | ||
let question_getter_option = NoteGetterOptions::new().select(0, question).select(1, caller).set_limit(1); | ||
let question_note = storage.questions.get_notes(question_getter_option)[0].unwrap_unchecked(); | ||
|
||
// Assert that the question exists and is owned by the caller | ||
assert(question_note.request == question); | ||
assert(question_note.requester_address == caller); | ||
|
||
// nullify the note | ||
storage.questions.remove(question_note); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
mod question_note; | ||
mod answer_note; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
use dep::aztec::note::{ | ||
note_header::NoteHeader, | ||
note_interface::NoteInterface, | ||
utils::compute_note_hash_for_read_or_nullify, | ||
}; | ||
use dep::aztec::oracle::{ | ||
get_secret_key::get_secret_key, | ||
get_public_key::get_public_key, | ||
}; | ||
|
||
struct AnswerNote { | ||
request: Field, | ||
answer: Field, | ||
owner: Field, // Owner of this answer, either the divinity or the requester | ||
header: NoteHeader | ||
} | ||
|
||
global ANSWER_NOTE_LEN: Field = 3; | ||
|
||
impl AnswerNote { | ||
fn new(_request: Field, _answer: Field, _owner: Field) -> Self { | ||
AnswerNote { | ||
request: _request, | ||
answer: _answer, | ||
owner: _owner, | ||
header: NoteHeader::empty(), | ||
} | ||
} | ||
|
||
fn serialize(self) -> [Field; ANSWER_NOTE_LEN] { | ||
[self.request, self.answer, self.owner] | ||
} | ||
|
||
fn deserialize(preimage: [Field; ANSWER_NOTE_LEN]) -> Self { | ||
AnswerNote { | ||
request: preimage[0], | ||
answer: preimage[1], | ||
owner: preimage[2], | ||
header: NoteHeader::empty(), | ||
} | ||
} | ||
|
||
fn compute_note_hash(self) -> Field { | ||
dep::std::hash::pedersen([ | ||
self.request, | ||
self.answer, | ||
self.owner | ||
])[0] | ||
} | ||
|
||
// TODO: refactor to a set of immutable singleton instead | ||
// No nullifier needed -> only one divinity has access to the original request and the answer cannot change ever | ||
fn compute_nullifier(self) -> Field { | ||
0 | ||
} | ||
|
||
fn set_header(&mut self, header: NoteHeader) { | ||
self.header = header; | ||
} | ||
} | ||
|
||
fn deserialize(preimage: [Field; ANSWER_NOTE_LEN]) -> AnswerNote { | ||
AnswerNote::deserialize(preimage) | ||
} | ||
|
||
fn serialize(note: AnswerNote) -> [Field; ANSWER_NOTE_LEN] { | ||
note.serialize() | ||
} | ||
|
||
fn compute_note_hash(note: AnswerNote) -> Field { | ||
note.compute_note_hash() | ||
} | ||
|
||
fn compute_nullifier(note: AnswerNote) -> Field { | ||
note.compute_nullifier() | ||
} | ||
|
||
fn get_header(note: AnswerNote) -> NoteHeader { | ||
note.header | ||
} | ||
|
||
fn set_header(note: &mut AnswerNote, header: NoteHeader) { | ||
note.set_header(header) | ||
} | ||
|
||
global AnswerNoteMethods = NoteInterface { | ||
deserialize, | ||
serialize, | ||
compute_note_hash, | ||
compute_nullifier, | ||
get_header, | ||
set_header, | ||
}; |
Oops, something went wrong.