Skip to content
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: check existence of vk in contract tree #167

Merged
merged 5 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,6 @@ template <typename NCT> std::ostream& operator<<(std::ostream& os, NewContractDa
<< "function_tree_root: " << new_contract_data.function_tree_root << "\n";
}

template <typename NCT> using ContractLeafPreimage = NewContractData<NCT>;

} // namespace aztec3::circuits::abis::private_kernel
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ template <typename NCT> struct PrivateCallData {
MembershipWitness<NCT, CONTRACT_TREE_HEIGHT> contract_leaf_membership_witness;

fr portal_contract_address; // an ETH address
fr acir_hash;

boolean operator==(PrivateCallData<NCT> const& other) const
{
Expand All @@ -45,7 +46,7 @@ template <typename NCT> struct PrivateCallData {
private_call_stack_preimages == other.private_call_stack_preimages && vk == other.vk &&
function_leaf_membership_witness == other.function_leaf_membership_witness &&
contract_leaf_membership_witness == other.contract_leaf_membership_witness &&
portal_contract_address == other.portal_contract_address;
portal_contract_address == other.portal_contract_address && acir_hash == other.acir_hash;
};

// WARNING: the `proof` does NOT get converted! (because the current implementation of `verify_proof` takes a proof
Expand All @@ -72,6 +73,7 @@ template <typename NCT> struct PrivateCallData {
to_circuit_type(contract_leaf_membership_witness),

to_ct(portal_contract_address),
to_ct(acir_hash),
};

return data;
Expand All @@ -89,6 +91,7 @@ template <typename NCT> void read(uint8_t const*& it, PrivateCallData<NCT>& obj)
read(it, obj.function_leaf_membership_witness);
read(it, obj.contract_leaf_membership_witness);
read(it, obj.portal_contract_address);
read(it, obj.acir_hash);
};

template <typename NCT> void write(std::vector<uint8_t>& buf, PrivateCallData<NCT> const& obj)
Expand All @@ -102,6 +105,7 @@ template <typename NCT> void write(std::vector<uint8_t>& buf, PrivateCallData<NC
write(buf, obj.function_leaf_membership_witness);
write(buf, obj.contract_leaf_membership_witness);
write(buf, obj.portal_contract_address);
write(buf, obj.acir_hash);
};

template <typename NCT> std::ostream& operator<<(std::ostream& os, PrivateCallData<NCT> const& obj)
Expand All @@ -118,7 +122,8 @@ template <typename NCT> std::ostream& operator<<(std::ostream& os, PrivateCallDa
<< obj.function_leaf_membership_witness << "\n"
<< "contract_leaf_membership_witness:\n"
<< obj.contract_leaf_membership_witness << "\n"
<< "portal_contract_address: " << obj.portal_contract_address << "\n";
<< "portal_contract_address: " << obj.portal_contract_address << "\n"
<< "acir_hash: " << obj.acir_hash << "\n";
}

} // namespace aztec3::circuits::abis::private_kernel
29 changes: 29 additions & 0 deletions cpp/src/aztec3/circuits/hash.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,33 @@ typename NCT::fr add_contract_address_to_nullifier(typename NCT::address contrac
return NCT::compress(inputs, aztec3::GeneratorIndex::OUTER_NULLIFIER);
}

/**
* @brief Calculate the Merkle tree root from the sibling path and leaf.
*
* @details The leaf is hashed with its sibling, and then the result is hashed
* with the next sibling etc in the path. The last hash is the root.
*
* @tparam NCT Operate on NativeTypes or CircuitTypes
* @tparam N The number of elements in the sibling path
* @param leaf The leaf element of the Merkle tree
* @param leafIndex The index of the leaf element in the Merkle tree
* @param siblingPath The nodes representing the merkle siblings of the leaf, its parent,
* the next parent, etc up to the sibling below the root
* @return The computed Merkle tree root.
*/
template <typename NCT, size_t N>
typename NCT::fr root_from_sibling_path(typename NCT::fr leaf,
typename NCT::uint32 leafIndex,
std::array<typename NCT::fr, N> siblingPath)
{
for (size_t i = 0; i < siblingPath.size(); i++) {
if (leafIndex & (1 << i)) {
leaf = NCT::merkle_hash(siblingPath[i], leaf);
} else {
leaf = NCT::merkle_hash(leaf, siblingPath[i]);
}
}
return leaf; // root
}

} // namespace aztec3::circuits
165 changes: 112 additions & 53 deletions cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
#include "aztec3/constants.hpp"
#include "init.hpp"

#include "aztec3/circuits/abis/function_leaf_preimage.hpp"
#include <aztec3/circuits/abis/private_kernel/private_inputs.hpp>
#include <aztec3/circuits/abis/private_kernel/public_inputs.hpp>
#include <aztec3/circuits/abis/private_kernel/new_contract_data.hpp>

#include <aztec3/utils/array.hpp>
#include <aztec3/circuits/hash.hpp>
#include "aztec3/constants.hpp"

#include <barretenberg/stdlib/merkle_tree/membership.hpp>

namespace aztec3::circuits::kernel::private_kernel {

using aztec3::circuits::abis::FunctionLeafPreimage;
using aztec3::circuits::abis::private_kernel::ContractLeafPreimage;
using aztec3::circuits::abis::private_kernel::NewContractData;
using aztec3::circuits::abis::private_kernel::PrivateInputs;
using aztec3::circuits::abis::private_kernel::PublicInputs;
Expand All @@ -22,6 +27,9 @@ using aztec3::utils::push_array_to_array;

using aztec3::circuits::compute_constructor_hash;
using aztec3::circuits::compute_contract_address;
using aztec3::circuits::root_from_sibling_path;

// using plonk::stdlib::merkle_tree::

// // TODO: NEED TO RECONCILE THE `proof`'s public inputs (which are uint8's) with the
// // private_call.call_stack_item.public_inputs!
Expand Down Expand Up @@ -62,73 +70,124 @@ void initialise_end_values(PrivateInputs<NT> const& private_inputs, PublicInputs
end.optionally_revealed_data = start.optionally_revealed_data;
}

void update_end_values(PrivateInputs<NT> const& private_inputs, PublicInputs<NT>& public_inputs)
void contract_logic(PrivateInputs<NT> const& private_inputs, PublicInputs<NT>& public_inputs)
{
const auto private_call_public_inputs = private_inputs.private_call.call_stack_item.public_inputs;

const auto& new_commitments = private_call_public_inputs.new_commitments;
const auto& new_nullifiers = private_call_public_inputs.new_nullifiers;

const auto& is_static_call = private_call_public_inputs.call_context.is_static_call;

if (is_static_call) {
// No state changes are allowed for static calls:
ASSERT(is_array_empty(new_commitments) == true);
ASSERT(is_array_empty(new_nullifiers) == true);
}

const auto& storage_contract_address = private_call_public_inputs.call_context.storage_contract_address;
const auto& portal_contract_address = private_inputs.private_call.portal_contract_address;
const auto& deployer_address = private_call_public_inputs.call_context.msg_sender;
const auto& contract_deployment_data =
private_inputs.signed_tx_request.tx_request.tx_context.contract_deployment_data;

{ // contract deployment
// input storage contract address must be 0 if its a constructor call and non-zero otherwise
auto is_contract_deployment = public_inputs.constants.tx_context.is_contract_deployment_tx;
// contract deployment

auto private_call_vk_hash = stdlib::recursion::verification_key<CT::bn254>::compress_native(
private_inputs.private_call.vk, GeneratorIndex::VK);
// input storage contract address must be 0 if its a constructor call and non-zero otherwise
auto is_contract_deployment = public_inputs.constants.tx_context.is_contract_deployment_tx;

auto constructor_hash = compute_constructor_hash(private_inputs.signed_tx_request.tx_request.function_data,
private_call_public_inputs.args,
private_call_vk_hash);
auto private_call_vk_hash = stdlib::recursion::verification_key<CT::bn254>::compress_native(
private_inputs.private_call.vk, GeneratorIndex::VK);

if (is_contract_deployment) {
ASSERT(contract_deployment_data.constructor_vk_hash == private_call_vk_hash);
}
auto constructor_hash = compute_constructor_hash(private_inputs.signed_tx_request.tx_request.function_data,
private_call_public_inputs.args,
private_call_vk_hash);

auto contract_address = compute_contract_address<NT>(deployer_address,
contract_deployment_data.contract_address_salt,
contract_deployment_data.function_tree_root,
constructor_hash);

if (is_contract_deployment) {
// must imply == derived address
ASSERT(storage_contract_address == contract_address);
} else {
// non-contract deployments must specify contract address being interacted with
ASSERT(storage_contract_address != 0);
}
if (is_contract_deployment) {
ASSERT(contract_deployment_data.constructor_vk_hash == private_call_vk_hash);
}

// compute contract address nullifier
auto blake_input = contract_address.to_field().to_buffer();
auto contract_address_nullifier = NT::fr::serialize_from_buffer(NT::blake3s(blake_input).data());
auto const new_contract_address = compute_contract_address<NT>(deployer_address,
contract_deployment_data.contract_address_salt,
contract_deployment_data.function_tree_root,
constructor_hash);

// push the contract address nullifier to nullifier vector
if (is_contract_deployment) {
array_push(public_inputs.end.new_nullifiers, contract_address_nullifier);
}
if (is_contract_deployment) {
// must imply == derived address
ASSERT(storage_contract_address == new_contract_address);
} else {
// non-contract deployments must specify contract address being interacted with
ASSERT(storage_contract_address != 0);
}

// compute contract address nullifier
auto const blake_input = new_contract_address.to_field().to_buffer();
auto const new_contract_address_nullifier = NT::fr::serialize_from_buffer(NT::blake3s(blake_input).data());

// push the contract address nullifier to nullifier vector
if (is_contract_deployment) {
array_push(public_inputs.end.new_nullifiers, new_contract_address_nullifier);
}

// Add new contract data if its a contract deployment function
NewContractData<NT> native_new_contract_data{ new_contract_address,
portal_contract_address,
contract_deployment_data.function_tree_root };

// Add new contract data if its a contract deployment function
auto native_new_contract_data = NewContractData<NT>{ contract_address,
portal_contract_address,
contract_deployment_data.function_tree_root };
array_push<NewContractData<NT>, KERNEL_NEW_CONTRACTS_LENGTH>(public_inputs.end.new_contracts,
native_new_contract_data);

array_push<NewContractData<NT>, KERNEL_NEW_CONTRACTS_LENGTH>(public_inputs.end.new_contracts,
native_new_contract_data);
/* We need to compute the root of the contract tree, starting from the function's VK:
* - Compute the vk_hash (done above)
* - Compute the function_leaf: hash(function_selector, is_private, vk_hash, acir_hash)
* - Hash the function_leaf with the function_leaf's sibling_path to get the function_tree_root
* - Compute the contract_leaf: hash(contract_address, portal_contract_address, function_tree_root)
* - Hash the contract_leaf with the contract_leaf's sibling_path to get the contract_tree_root
*/

const auto function_leaf_preimage = FunctionLeafPreimage<NT>{
.function_selector = private_inputs.private_call.call_stack_item.function_data.function_selector,
.is_private = true,
.vk_hash = private_call_vk_hash,
.acir_hash = private_inputs.private_call.acir_hash,
};

const auto function_leaf = function_leaf_preimage.hash();

const auto& function_leaf_index = private_inputs.private_call.function_leaf_membership_witness.leaf_index;
const auto& function_leaf_sibling_path = private_inputs.private_call.function_leaf_membership_witness.sibling_path;

const auto& function_tree_root =
root_from_sibling_path<NT>(function_leaf, function_leaf_index, function_leaf_sibling_path);

const ContractLeafPreimage<NT> contract_leaf_preimage{
storage_contract_address,
portal_contract_address,
function_tree_root,
};

const auto contract_leaf = contract_leaf_preimage.hash();

auto& contract_leaf_index = private_inputs.private_call.contract_leaf_membership_witness.leaf_index;
auto& contract_leaf_sibling_path = private_inputs.private_call.contract_leaf_membership_witness.sibling_path;

const auto& computed_contract_tree_root =
root_from_sibling_path<NT>(contract_leaf, contract_leaf_index, contract_leaf_sibling_path);

auto& purported_contract_tree_root =
private_inputs.private_call.call_stack_item.public_inputs.historic_contract_tree_root;
ASSERT(computed_contract_tree_root == purported_contract_tree_root);

auto& previous_kernel_contract_tree_root =
private_inputs.previous_kernel.public_inputs.constants.old_tree_roots.contract_tree_root;
ASSERT(purported_contract_tree_root == previous_kernel_contract_tree_root);
}

void update_end_values(PrivateInputs<NT> const& private_inputs, PublicInputs<NT>& public_inputs)
{
const auto private_call_public_inputs = private_inputs.private_call.call_stack_item.public_inputs;

const auto& new_commitments = private_call_public_inputs.new_commitments;
const auto& new_nullifiers = private_call_public_inputs.new_nullifiers;

const auto& is_static_call = private_call_public_inputs.call_context.is_static_call;

if (is_static_call) {
// No state changes are allowed for static calls:
ASSERT(is_array_empty(new_commitments) == true);
ASSERT(is_array_empty(new_nullifiers) == true);
}

const auto& storage_contract_address = private_call_public_inputs.call_context.storage_contract_address;

{
// Nonce nullifier
// DANGER: This is terrible. This should not be part of the protocol. This is an intentional bodge to reach a
Expand Down Expand Up @@ -183,11 +242,11 @@ void validate_this_private_call_hash(PrivateInputs<NT> const& private_inputs)
const auto& start = private_inputs.previous_kernel.public_inputs.end;
// TODO: this logic might need to change to accommodate the weird edge 3 initial txs (the 'main' tx, the 'fee' tx,
// and the 'gas rebate' tx).
const auto this_private_call_hash = array_pop(start.private_call_stack);
const auto popped_private_call_hash = array_pop(start.private_call_stack);
const auto calculated_this_private_call_hash = private_inputs.private_call.call_stack_item.hash();

ASSERT(this_private_call_hash ==
calculated_this_private_call_hash); // "this private_call_hash does not reconcile");
ASSERT(popped_private_call_hash ==
calculated_this_private_call_hash); // "this private_call_hash does not reconcile";
};

void validate_this_private_call_stack(PrivateInputs<NT> const& private_inputs)
Expand Down
17 changes: 17 additions & 0 deletions cpp/src/aztec3/utils/types/circuit_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <barretenberg/stdlib/recursion/verifier/verifier.hpp>
#include <barretenberg/stdlib/recursion/verification_key/verification_key.hpp>
#include <barretenberg/stdlib/commitment/pedersen/pedersen.hpp>
#include <barretenberg/stdlib/hash/pedersen/pedersen.hpp>
#include <barretenberg/stdlib/hash/blake2s/blake2s.hpp>
#include "native_types.hpp"

Expand Down Expand Up @@ -83,6 +84,22 @@ template <typename Composer> struct CircuitTypes {
return plonk::stdlib::pedersen_commitment<Composer>::compress(input_pairs);
};

/**
* @brief Compute the hash for a pair of left and right nodes in a merkle tree.
*
* @details Compress the two nodes using the default/0-generator which is reserved
* for internal merkle hashing.
*
* @param left The left child node
* @param right The right child node
* @return The computed Merkle tree hash for the given pair of nodes
*/
static fr merkle_hash(fr left, fr right)
{
// use 0-generator for internal merkle hashing
return plonk::stdlib::pedersen_hash<Composer>::hash_multiple({ left, right }, 0);
};

static grumpkin_point commit(const std::vector<fr>& inputs, const size_t hash_index = 0)
{
return plonk::stdlib::pedersen_commitment<Composer>::commit(inputs, hash_index);
Expand Down
16 changes: 16 additions & 0 deletions cpp/src/aztec3/utils/types/native_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ struct NativeTypes {
return crypto::pedersen_commitment::compress_native(input_pairs);
}

/**
* @brief Compute the hash for a pair of left and right nodes in a merkle tree.
*
* @details Compress the two nodes using the default/0-generator which is reserved
* for internal merkle hashing.
*
* @param left The left child node
* @param right The right child node
* @return The computed Merkle tree hash for the given pair of nodes
*/
static fr merkle_hash(fr left, fr right)
{
// use 0-generator for internal merkle hashing
return crypto::pedersen_hash::hash_multiple({ left, right }, 0);
}

static grumpkin_point commit(const std::vector<fr>& inputs, const size_t hash_index = 0)
{
return crypto::pedersen_commitment::commit_native(inputs, hash_index);
Expand Down