-
Notifications
You must be signed in to change notification settings - Fork 297
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
feat: libraryfying historic access #3658
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
decbe5c
WIP
benesjan 0ad8886
note inclusion proof libraryfied
benesjan 5b7e116
nullifier non-inclusion libraryfied
benesjan 06ee451
nullifier inclusion libraryfied
benesjan 9b9d9f3
libraryfying public value inclusion
benesjan e2f9e8d
note validity helper + no camel case
benesjan e564d8b
formatting fix
benesjan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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,5 @@ | ||
mod note_inclusion; | ||
mod note_validity; | ||
mod nullifier_inclusion; | ||
mod nullifier_non_inclusion; | ||
mod public_value_inclusion; |
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,48 @@ | ||
use dep::protocol_types::constants::NOTE_HASH_TREE_HEIGHT; | ||
use dep::std::merkle::compute_merkle_root; | ||
|
||
use crate::{ | ||
context::PrivateContext, | ||
note::{ | ||
utils::compute_unique_siloed_note_hash, | ||
note_header::NoteHeader, | ||
note_interface::NoteInterface, | ||
}, | ||
oracle::get_membership_witness::{ | ||
get_membership_witness, | ||
MembershipWitness, | ||
}, | ||
}; | ||
|
||
pub fn prove_note_commitment_inclusion( | ||
note_commitment: Field, | ||
block_number: u32, // The block at which we'll prove that the note exists | ||
context: PrivateContext | ||
) { | ||
// 1) Get block header from oracle and ensure that the block is included in the archive. | ||
let block_header = context.get_block_header(block_number); | ||
|
||
// 2) Get the membership witness of the note in the note hash tree | ||
let note_hash_tree_id = 2; // TODO(#3443) | ||
let witness: MembershipWitness<NOTE_HASH_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT + 1> = | ||
get_membership_witness(block_number, note_hash_tree_id, note_commitment); | ||
|
||
// 3) Prove that the commitment is in the note hash tree | ||
assert( | ||
block_header.note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), | ||
"Proving note inclusion failed" | ||
); | ||
|
||
// --> Now we have traversed the trees all the way up to archive root. | ||
} | ||
|
||
pub fn prove_note_inclusion<Note, N>( | ||
note_interface: NoteInterface<Note, N>, | ||
note_with_header: Note, | ||
block_number: u32, // The block at which we'll prove that the note exists | ||
context: PrivateContext | ||
) { | ||
let note_commitment = compute_unique_siloed_note_hash(note_interface, note_with_header); | ||
|
||
prove_note_commitment_inclusion(note_commitment, block_number, context); | ||
} |
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,19 @@ | ||
use crate::{ | ||
context::PrivateContext, | ||
history::{ | ||
note_inclusion::prove_note_inclusion, | ||
nullifier_non_inclusion::prove_note_not_nullified, | ||
}, | ||
note::note_interface::NoteInterface, | ||
}; | ||
|
||
// A helper function that proves that a note is valid at the given block number | ||
pub fn prove_note_validity<Note, N>( | ||
note_interface: NoteInterface<Note, N>, | ||
note_with_header: Note, | ||
block_number: u32, // The block at which we'll prove that the note exists | ||
context: PrivateContext | ||
) { | ||
prove_note_inclusion(note_interface, note_with_header, block_number, context); | ||
prove_note_not_nullified(note_interface, note_with_header, block_number, context); | ||
} |
33 changes: 33 additions & 0 deletions
33
yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr
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,33 @@ | ||
use dep::std::merkle::compute_merkle_root; | ||
|
||
use crate::{ | ||
context::PrivateContext, | ||
oracle::get_nullifier_membership_witness::get_nullifier_membership_witness, | ||
}; | ||
|
||
pub fn prove_nullifier_inclusion( | ||
nullifier: Field, | ||
block_number: u32, // The block at which we'll prove that the note exists | ||
context: PrivateContext | ||
) { | ||
// 1) Get block header from oracle and ensure that the block hash is included in the archive. | ||
let block_header = context.get_block_header(block_number); | ||
|
||
// 2) Get the membership witness of the nullifier | ||
let witness = get_nullifier_membership_witness(block_number, nullifier); | ||
|
||
// 3) Check that the witness we obtained matches the nullifier | ||
assert(witness.leaf_data.value == nullifier, "Nullifier does not match value in witness"); | ||
|
||
// 4) Compute the nullifier tree leaf | ||
let nullifier_leaf = witness.leaf_data.hash(); | ||
|
||
// 5) Prove that the nullifier is in the nullifier tree | ||
assert( | ||
block_header.nullifier_tree_root == compute_merkle_root(nullifier_leaf, witness.index, witness.path), | ||
"Proving nullifier inclusion failed" | ||
); | ||
|
||
// --> Now we have traversed the trees all the way up to archive root and verified that the nullifier | ||
// was not yet included in the nullifier tree. | ||
} |
63 changes: 63 additions & 0 deletions
63
yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr
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,63 @@ | ||
use dep::std::merkle::compute_merkle_root; | ||
|
||
use crate::{ | ||
context::PrivateContext, | ||
note::{ | ||
utils::compute_siloed_nullifier, | ||
note_header::NoteHeader, | ||
note_interface::NoteInterface, | ||
}, | ||
oracle::get_nullifier_membership_witness::get_low_nullifier_membership_witness, | ||
utils::{ | ||
full_field_less_than, | ||
full_field_greater_than, | ||
}, | ||
}; | ||
|
||
pub fn prove_nullifier_non_inclusion( | ||
nullifier: Field, | ||
block_number: u32, // The block at which we'll prove that the nullifier does not exists | ||
context: PrivateContext | ||
) { | ||
// 1) Get block header from oracle and ensure that the block is included in the archive. | ||
let block_header = context.get_block_header(block_number); | ||
|
||
// 2) Get the membership witness of a low nullifier of the nullifier | ||
let witness = get_low_nullifier_membership_witness(block_number, nullifier); | ||
|
||
// 3) Prove that the nullifier is not included in the nullifier tree | ||
|
||
// 3.a) Compute the low nullifier leaf and prove that it is in the nullifier tree | ||
let low_nullifier_leaf = witness.leaf_data.hash(); | ||
assert( | ||
block_header.nullifier_tree_root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), | ||
"Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" | ||
); | ||
|
||
// 3.b) Prove that the low nullifier is smaller than the nullifier | ||
assert( | ||
full_field_less_than(witness.leaf_data.value, nullifier), | ||
"Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed" | ||
); | ||
|
||
// 3.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not | ||
// included in the nullifier tree (or to 0 if the to-be-inserted nullifier is the largest of all) | ||
assert( | ||
full_field_greater_than(witness.leaf_data.next_value, nullifier) | (witness.leaf_data.next_index == 0), | ||
"Proving nullifier non-inclusion failed: low_nullifier.next_value > nullifier.value check failed" | ||
); | ||
|
||
// --> Now we have traversed the trees all the way up to archive root and verified that the nullifier | ||
// was not yet included in the nullifier tree. | ||
} | ||
|
||
pub fn prove_note_not_nullified<Note, N>( | ||
note_interface: NoteInterface<Note, N>, | ||
note_with_header: Note, | ||
block_number: u32, // The block at which we'll prove that the note was not nullified | ||
context: PrivateContext | ||
) { | ||
let nullifier = compute_siloed_nullifier(note_interface, note_with_header); | ||
|
||
prove_nullifier_non_inclusion(nullifier, block_number, context); | ||
} |
44 changes: 44 additions & 0 deletions
44
yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr
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,44 @@ | ||
use dep::protocol_types::constants::{ | ||
PUBLIC_DATA_TREE_HEIGHT, | ||
GENERATOR_INDEX__PUBLIC_LEAF_INDEX, | ||
}; | ||
use dep::std::merkle::compute_merkle_root; | ||
|
||
use crate::{ | ||
context::PrivateContext, | ||
hash::pedersen_hash, | ||
oracle::get_sibling_path::get_sibling_path, | ||
}; | ||
|
||
pub fn prove_public_value_inclusion( | ||
value: Field, // The value that we want to prove is in the public data tree | ||
storage_slot: Field, // The storage slot in which the value is stored | ||
block_number: u32, // The block at which we'll prove that the note exists | ||
context: PrivateContext | ||
) { | ||
// 1) Get block header from oracle and ensure that the block hash is included in the archive. | ||
let block_header = context.get_block_header(block_number); | ||
|
||
// 2) Compute the public value leaf index. | ||
// We have to compute the leaf index here because unlike in the case of note commitments, public values are | ||
// not siloed with contract address so an oracle could cheat and give us a membership witness for arbitrary | ||
// value in the public data tree. | ||
let value_leaf_index = pedersen_hash( | ||
[context.this_address(), storage_slot], | ||
GENERATOR_INDEX__PUBLIC_LEAF_INDEX | ||
); | ||
|
||
// 3) Get the sibling path of the value leaf index in the public data tree at block `block_number`. | ||
let public_data_tree_id = 3; // TODO(#3443) | ||
let path: [Field; PUBLIC_DATA_TREE_HEIGHT] = | ||
get_sibling_path(block_number, public_data_tree_id, value_leaf_index); | ||
|
||
// 4) Prove that the value provided on input is in the public data tree at the given storage slot. | ||
assert( | ||
block_header.public_data_tree_root == compute_merkle_root(value, value_leaf_index, path), | ||
"Proving public value inclusion failed" | ||
); | ||
|
||
// --> Now we have traversed the trees all the way up to archive root and that way verified that a specific | ||
// `value` was really set in a given contract storage slot at block `block_number` in public data tree. | ||
} |
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 |
---|---|---|
|
@@ -2,6 +2,7 @@ mod abi; | |
mod address; | ||
mod context; | ||
mod hash; | ||
mod history; | ||
mod log; | ||
mod messaging; | ||
mod 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
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 |
---|---|---|
|
@@ -54,7 +54,7 @@ describe('e2e_inclusion_proofs_contract', () => { | |
{ | ||
// Prove note inclusion in a given block. | ||
const ignoredCommitment = 0; // Not ignored only when the note doesn't exist | ||
await contract.methods.proveNoteInclusion(owner, noteCreationBlockNumber, ignoredCommitment).send().wait(); | ||
await contract.methods.test_note_inclusion_proof(owner, noteCreationBlockNumber, ignoredCommitment).send().wait(); | ||
} | ||
|
||
{ | ||
|
@@ -63,12 +63,12 @@ describe('e2e_inclusion_proofs_contract', () => { | |
// possible because of issue https://github.com/AztecProtocol/aztec-packages/issues/3535 | ||
const blockNumber = await pxe.getBlockNumber(); | ||
const ignoredNullifier = 0; // Not ignored only when the note doesn't exist | ||
await contract.methods.proveNullifierNonInclusion(owner, blockNumber, ignoredNullifier).send().wait(); | ||
await contract.methods.test_nullifier_non_inclusion_proof(owner, blockNumber, ignoredNullifier).send().wait(); | ||
} | ||
|
||
{ | ||
// We test the failure case now --> The proof should fail when the nullifier already exists | ||
const receipt = await contract.methods.nullifyNote(owner).send().wait({ debug: true }); | ||
const receipt = await contract.methods.nullify_note(owner).send().wait({ debug: true }); | ||
const { newNullifiers } = receipt.debugInfo!; | ||
expect(newNullifiers.length).toBe(2); | ||
|
||
|
@@ -78,29 +78,54 @@ describe('e2e_inclusion_proofs_contract', () => { | |
// the low nullifier when the nullifier already exists in the tree and for this reason the execution fails | ||
// on low_nullifier.value < nullifier.value check. | ||
await expect( | ||
contract.methods.proveNullifierNonInclusion(owner, blockNumber, nullifier).send().wait(), | ||
contract.methods.test_nullifier_non_inclusion_proof(owner, blockNumber, nullifier).send().wait(), | ||
).rejects.toThrowError( | ||
/Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, | ||
); | ||
} | ||
}); | ||
|
||
it('proves note validity (note commitment inclusion and nullifier non-inclusion)', async () => { | ||
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. This is pretty nice 👍 |
||
// Owner of a note | ||
const owner = accounts[0].address; | ||
let noteCreationBlockNumber: number; | ||
{ | ||
// Create a note | ||
const value = 100n; | ||
const receipt = await contract.methods.create_note(owner, value).send().wait({ debug: true }); | ||
|
||
noteCreationBlockNumber = receipt.blockNumber!; | ||
const { newCommitments, visibleNotes } = receipt.debugInfo!; | ||
|
||
expect(newCommitments.length).toBe(1); | ||
expect(visibleNotes.length).toBe(1); | ||
const [receivedValue, receivedOwner, _randomness] = visibleNotes[0].note.items; | ||
expect(receivedValue.toBigInt()).toBe(value); | ||
expect(receivedOwner).toEqual(owner.toField()); | ||
} | ||
|
||
{ | ||
// Prove note validity | ||
await contract.methods.test_note_validity_proof(owner, noteCreationBlockNumber).send().wait(); | ||
} | ||
}); | ||
|
||
it('note existence failure case', async () => { | ||
// Owner of a note | ||
const owner = AztecAddress.random(); | ||
|
||
const blockNumber = await pxe.getBlockNumber(); | ||
const randomNoteCommitment = Fr.random(); | ||
await expect( | ||
contract.methods.proveNoteInclusion(owner, blockNumber, randomNoteCommitment).send().wait(), | ||
contract.methods.test_note_inclusion_proof(owner, blockNumber, randomNoteCommitment).send().wait(), | ||
).rejects.toThrow(/Leaf value: 0x[0-9a-fA-F]+ not found in NOTE_HASH_TREE/); | ||
}); | ||
|
||
it('proves an existence of a public value in private context', async () => { | ||
// Chose random block number between deployment and current block number to test archival node | ||
const blockNumber = await getRandomBlockNumberSinceDeployment(); | ||
|
||
await contract.methods.provePublicValueInclusion(publicValue, blockNumber).send().wait(); | ||
await contract.methods.test_public_value_inclusion_proof(publicValue, blockNumber).send().wait(); | ||
}); | ||
|
||
it('public value existence failure case', async () => { | ||
|
@@ -109,7 +134,7 @@ describe('e2e_inclusion_proofs_contract', () => { | |
|
||
const randomPublicValue = Fr.random(); | ||
await expect( | ||
contract.methods.provePublicValueInclusion(randomPublicValue, blockNumber).send().wait(), | ||
contract.methods.test_public_value_inclusion_proof(randomPublicValue, blockNumber).send().wait(), | ||
).rejects.toThrow(/Proving public value inclusion failed/); | ||
}); | ||
|
||
|
@@ -120,7 +145,7 @@ describe('e2e_inclusion_proofs_contract', () => { | |
const block = await pxe.getBlock(blockNumber); | ||
const nullifier = block?.newNullifiers[0]; | ||
|
||
await contract.methods.proveNullifierInclusion(nullifier!, blockNumber).send().wait(); | ||
await contract.methods.test_nullifier_inclusion_proof(nullifier!, blockNumber).send().wait(); | ||
}); | ||
|
||
it('nullifier existence failure case', async () => { | ||
|
@@ -129,9 +154,9 @@ describe('e2e_inclusion_proofs_contract', () => { | |
const blockNumber = await pxe.getBlockNumber(); | ||
const randomNullifier = Fr.random(); | ||
|
||
await expect(contract.methods.proveNullifierInclusion(randomNullifier, blockNumber).send().wait()).rejects.toThrow( | ||
/Low nullifier witness not found for nullifier 0x[0-9a-fA-F]+ at block/, | ||
); | ||
await expect( | ||
contract.methods.test_nullifier_inclusion_proof(randomNullifier, blockNumber).send().wait(), | ||
).rejects.toThrow(/Low nullifier witness not found for nullifier 0x[0-9a-fA-F]+ at block/); | ||
}); | ||
|
||
const getRandomBlockNumberSinceDeployment = async () => { | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This one should probably have an issue for fixing it when the underlying tree structure is changed from sparse trees in a few days.
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.
This will break when the tree is changed and it will be caught by the test so I feel like it's part of the tree structure change issue. Won't you agree?