Skip to content

Commit

Permalink
feat: modify contract instance to include public keys (#9153)
Browse files Browse the repository at this point in the history
In this PR we are doing the ground work for the new address scheme by
modifying the contract instance to include the full public keys instead
of only the public keys hash. We need the full public keys because we
need to verify the preimage of the new address, which requires the ivpk,
and we need to verify the ivpk's correctness by manually computing the
public keys hash.
  • Loading branch information
sklppy88 authored Oct 17, 2024
1 parent 35c66c9 commit 17c6127
Show file tree
Hide file tree
Showing 69 changed files with 1,463 additions and 1,245 deletions.
12 changes: 10 additions & 2 deletions barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2314,11 +2314,19 @@ TEST_F(AvmExecutionTests, opGetContractInstanceOpcodes)

// Generate Hint for call operation
// Note: opcode does not write 'address' into memory
// We store this random value since it's part of the return - we could return the rest as well but we don't need to.
auto returned_point = grumpkin::g1::affine_element::random_element();
PublicKeysHint public_keys_hints = {
returned_point,
grumpkin::g1::affine_element::random_element(),
grumpkin::g1::affine_element::random_element(),
grumpkin::g1::affine_element::random_element(),
};
auto execution_hints =
ExecutionHints().with_contract_instance_hints({ { address, { address, 1, 2, 3, 4, 5, 6 } } });
ExecutionHints().with_contract_instance_hints({ { address, { address, 1, 2, 3, 4, 5, public_keys_hints } } });

auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec, execution_hints);
EXPECT_EQ(returndata, std::vector<FF>({ 1, 2, 3, 4, 5, 6 })); // The first one represents true
EXPECT_EQ(returndata, std::vector<FF>({ 1, 2, 3, 4, 5, returned_point.x })); // The first one represents true

validate_trace(std::move(trace), public_inputs, calldata, returndata);
}
Expand Down
32 changes: 30 additions & 2 deletions barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#pragma once

#include "barretenberg/ecc/groups/affine_element.hpp"
#include "barretenberg/vm/avm/generated/flavor_settings.hpp"

namespace bb::avm_trace {

using FF = AvmFlavorSettings::FF;
using AffinePoint = grumpkin::g1::affine_element;

struct ExternalCallHint {
FF success;
Expand All @@ -26,16 +28,42 @@ inline void read(uint8_t const*& it, ExternalCallHint& hint)
read(it, hint.end_side_effect_counter);
}

struct PublicKeysHint {
AffinePoint nullifier_key;
/** Incoming viewing public key */
AffinePoint incoming_viewing_key;
/** Outgoing viewing public key */
AffinePoint outgoing_viewing_key;
/** Tagging viewing public key */
AffinePoint tagging_key;

std::vector<FF> to_fields() const
{
return { nullifier_key.x, nullifier_key.y, incoming_viewing_key.x, incoming_viewing_key.y,
outgoing_viewing_key.x, outgoing_viewing_key.y, tagging_key.x, tagging_key.y };
}
};

struct ContractInstanceHint {
FF address;
FF instance_found_in_address;
FF salt;
FF deployer_addr;
FF contract_class_id;
FF initialisation_hash;
FF public_key_hash;
PublicKeysHint public_keys;
};

inline void read(uint8_t const*& it, PublicKeysHint& hint)
{
using serialize::read;
// CAREFUL: We assume we never receive a point at infinity here
// TS does not serialize the infinity flag when converting to buffer
read(it, hint.nullifier_key);
read(it, hint.incoming_viewing_key);
read(it, hint.outgoing_viewing_key);
read(it, hint.tagging_key);
}
// Add support for deserialization of ContractInstanceHint.
inline void read(uint8_t const*& it, ContractInstanceHint& hint)
{
Expand All @@ -46,7 +74,7 @@ inline void read(uint8_t const*& it, ContractInstanceHint& hint)
read(it, hint.deployer_addr);
read(it, hint.contract_class_id);
read(it, hint.initialisation_hash);
read(it, hint.public_key_hash);
read(it, hint.public_keys);
}

struct ExecutionHints {
Expand Down
6 changes: 3 additions & 3 deletions barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2403,14 +2403,14 @@ void AvmTraceBuilder::op_get_contract_instance(uint8_t indirect, uint32_t addres

// Read the contract instance
ContractInstanceHint contract_instance = execution_hints.contract_instance_hints.at(read_address.val);

std::vector<FF> public_key_fields = contract_instance.public_keys.to_fields();
// NOTE: we don't write the first entry (the contract instance's address/key) to memory
std::vector<FF> contract_instance_vec = { contract_instance.instance_found_in_address,
contract_instance.salt,
contract_instance.deployer_addr,
contract_instance.contract_class_id,
contract_instance.initialisation_hash,
contract_instance.public_key_hash };
contract_instance.initialisation_hash };
contract_instance_vec.insert(contract_instance_vec.end(), public_key_fields.begin(), public_key_fields.end());
write_slice_to_memory(resolved_dst_offset, AvmMemoryTag::FF, contract_instance_vec);

debug("contract_instance cnt: ", side_effect_counter);
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ library Constants {
uint256 internal constant GAS_SETTINGS_LENGTH = 7;
uint256 internal constant CALL_CONTEXT_LENGTH = 5;
uint256 internal constant CONTENT_COMMITMENT_LENGTH = 4;
uint256 internal constant CONTRACT_INSTANCE_LENGTH = 5;
uint256 internal constant CONTRACT_INSTANCE_LENGTH = 16;
uint256 internal constant CONTRACT_STORAGE_READ_LENGTH = 3;
uint256 internal constant CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 3;
uint256 internal constant ETH_ADDRESS_LENGTH = 1;
Expand Down
16 changes: 12 additions & 4 deletions noir-projects/aztec-nr/aztec/src/deploy.nr
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,25 @@ pub fn deploy_contract(context: &mut PrivateContext, target: AztecAddress) {
// Adapted from noir-contracts/contracts/contract_instance_deployer_contract/src/interface/ContractInstanceDeployer.nr
// That file was autogenerated running the following command from noir-projects/noir-contracts:
// ../../yarn-project/node_modules/.bin/aztec-cli codegen target/contract_instance_deployer_contract-ContractInstanceDeployer.json --nr -o ./contracts/contract_instance_deployer_contract/src/interface
let mut serialized_args = [0; 5];
let mut serialized_args = [0; 16];
serialized_args[0] = instance.salt;
serialized_args[1] = instance.contract_class_id.to_field();
serialized_args[2] = instance.initialization_hash;
serialized_args[3] = instance.public_keys_hash.to_field();
serialized_args[4] = universal_deploy as Field;

let serialized_public_keys = instance.public_keys.serialize();

for i in 0..12 {
serialized_args[i + 3] = serialized_public_keys[i];
}

serialized_args[15] = universal_deploy as Field;

let _call_result = context.call_private_function(
DEPLOYER_CONTRACT_ADDRESS,
comptime {
FunctionSelector::from_signature("deploy(Field,(Field),Field,(Field),bool)")
FunctionSelector::from_signature(
"deploy(Field,(Field),Field,(((Field,Field,bool)),((Field,Field,bool)),((Field,Field,bool)),((Field,Field,bool))),bool)"
)
},
serialized_args
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,18 @@ contract AvmTest {
assert(fields[2] == 0x456);
assert(fields[3] == 0x789);
assert(fields[4] == 0x101112);
assert(fields[5] == 0x161718);
assert(fields[5] == 0x131415);
assert(fields[6] == 0x161718);
assert(fields[7] == 0x00);
assert(fields[8] == 0x192021);
assert(fields[9] == 0x222324);
assert(fields[10] == 0x00);
assert(fields[11] == 0x252627);
assert(fields[12] == 0x282930);
assert(fields[13] == 0x00);
assert(fields[14] == 0x313233);
assert(fields[15] == 0x343536);
assert(fields[16] == 0x00);
}

#[public]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use dep::aztec::macros::aztec;
#[aztec]
contract ContractInstanceDeployer {
use dep::aztec::protocol_types::{
address::{AztecAddress, PublicKeysHash, PartialAddress}, contract_class_id::ContractClassId,
constants::DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE, abis::log_hash::LogHash,
traits::Serialize
address::{AztecAddress, PublicKeysHash, PartialAddress}, public_keys::PublicKeys,
contract_class_id::ContractClassId, constants::DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE,
abis::log_hash::LogHash, traits::Serialize
};
use dep::aztec::{
hash::compute_unencrypted_log_hash, oracle::logs::emit_unencrypted_log_private,
Expand All @@ -14,25 +14,74 @@ contract ContractInstanceDeployer {
use std::meta::derive;

#[event]
#[derive(Serialize)]
struct ContractInstanceDeployed {
DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE: Field,
address: AztecAddress,
version: u8,
salt: Field,
contract_class_id: ContractClassId,
initialization_hash: Field,
public_keys_hash: PublicKeysHash,
public_keys: PublicKeys,
deployer: AztecAddress,
}

// We need to impl this separately because ts deserializes a point as two fields only.
// We had issues that:
// Notice how the 'is_infinite' field is deserialized as the next point.
// {
// masterNullifierPublicKey: Point {
// x: Fr<0x0000000000000000000000000000000000000000000000000000000000000012>,
// y: Fr<0x0000000000000000000000000000000000000000000000000000000000000034>,
// isInfinite: false,
// kind: 'point'
// },
// masterIncomingViewingPublicKey: Point {
// x: Fr<0x0000000000000000000000000000000000000000000000000000000000000000>,
// y: Fr<0x0000000000000000000000000000000000000000000000000000000000000056>,
// isInfinite: false,
// kind: 'point'
// },
// masterOutgoingViewingPublicKey: Point {
// x: Fr<0x0000000000000000000000000000000000000000000000000000000000000078>,
// y: Fr<0x0000000000000000000000000000000000000000000000000000000000000000>,
// isInfinite: false,
// kind: 'point'
// },
// masterTaggingPublicKey: Point {
// x: Fr<0x0000000000000000000000000000000000000000000000000000000000000910>,
// y: Fr<0x0000000000000000000000000000000000000000000000000000000000001112>,
// isInfinite: false,
// kind: 'point'
// }
impl Serialize<15> for ContractInstanceDeployed {
fn serialize(self) -> [Field; 15] {
[
self.DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE,
self.address.to_field(),
self.version.to_field(),
self.salt,
self.contract_class_id.to_field(),
self.initialization_hash,
self.public_keys.npk_m.serialize()[0],
self.public_keys.npk_m.serialize()[1],
self.public_keys.ivpk_m.serialize()[0],
self.public_keys.ivpk_m.serialize()[1],
self.public_keys.ovpk_m.serialize()[0],
self.public_keys.ovpk_m.serialize()[1],
self.public_keys.tpk_m.serialize()[0],
self.public_keys.tpk_m.serialize()[1],
self.deployer.to_field()
]
}
}

#[private]
#['private]
fn deploy(
salt: Field,
contract_class_id: ContractClassId,
initialization_hash: Field,
public_keys_hash: PublicKeysHash,
public_keys: PublicKeys,
universal_deploy: bool
) {
// TODO(@spalladino): assert nullifier_exists silo(contract_class_id, ContractClassRegisterer)
Expand All @@ -45,7 +94,7 @@ contract ContractInstanceDeployer {

let partial_address = PartialAddress::compute(contract_class_id, salt, initialization_hash, deployer);

let address = AztecAddress::compute(public_keys_hash, partial_address);
let address = AztecAddress::compute(public_keys.hash(), partial_address);

// Emit the address as a nullifier to be able to prove that this instance has been (not) deployed
context.push_nullifier(address.to_field());
Expand All @@ -55,7 +104,7 @@ contract ContractInstanceDeployer {
DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE,
contract_class_id,
address,
public_keys_hash,
public_keys,
initialization_hash,
salt,
deployer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ global GAS_LENGTH: u32 = 2;
global GAS_SETTINGS_LENGTH: u32 = GAS_LENGTH * 2 + GAS_FEES_LENGTH + /* inclusion_fee */ 1;
global CALL_CONTEXT_LENGTH: u32 = 5;
global CONTENT_COMMITMENT_LENGTH: u32 = 4;
global CONTRACT_INSTANCE_LENGTH: u32 = 5;
global CONTRACT_INSTANCE_LENGTH: u32 = 16;
global CONTRACT_STORAGE_READ_LENGTH: u32 = 3;
global CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH: u32 = 3;
global ETH_ADDRESS_LENGTH: u32 = 1;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
address::{aztec_address::AztecAddress, partial_address::PartialAddress, public_keys_hash::PublicKeysHash},
contract_class_id::ContractClassId, constants::CONTRACT_INSTANCE_LENGTH,
public_keys::PublicKeys, contract_class_id::ContractClassId, constants::CONTRACT_INSTANCE_LENGTH,
traits::{Deserialize, Hash, Serialize}
};

Expand All @@ -9,12 +9,12 @@ pub struct ContractInstance {
deployer: AztecAddress,
contract_class_id : ContractClassId,
initialization_hash : Field,
public_keys_hash : PublicKeysHash,
public_keys : PublicKeys,
}

impl Eq for ContractInstance {
fn eq(self, other: Self) -> bool {
self.public_keys_hash.eq(other.public_keys_hash)
self.public_keys.eq(other.public_keys)
& self.initialization_hash.eq(other.initialization_hash)
& self.contract_class_id.eq(other.contract_class_id)
& self.salt.eq(other.salt)
Expand All @@ -23,12 +23,24 @@ impl Eq for ContractInstance {

impl Serialize<CONTRACT_INSTANCE_LENGTH> for ContractInstance {
fn serialize(self) -> [Field; CONTRACT_INSTANCE_LENGTH] {
let public_keys_serialized = self.public_keys.serialize();
[
self.salt,
self.deployer.to_field(),
self.contract_class_id.to_field(),
self.initialization_hash,
self.public_keys_hash.to_field()
public_keys_serialized[0],
public_keys_serialized[1],
public_keys_serialized[2],
public_keys_serialized[3],
public_keys_serialized[4],
public_keys_serialized[5],
public_keys_serialized[6],
public_keys_serialized[7],
public_keys_serialized[8],
public_keys_serialized[9],
public_keys_serialized[10],
public_keys_serialized[11]
]
}
}
Expand All @@ -40,7 +52,22 @@ impl Deserialize<CONTRACT_INSTANCE_LENGTH> for ContractInstance {
deployer: AztecAddress::from_field(serialized[1]),
contract_class_id: ContractClassId::from_field(serialized[2]),
initialization_hash: serialized[3],
public_keys_hash: PublicKeysHash::from_field(serialized[4])
public_keys: PublicKeys::deserialize(
[
serialized[4],
serialized[5],
serialized[6],
serialized[7],
serialized[8],
serialized[9],
serialized[10],
serialized[11],
serialized[12],
serialized[13],
serialized[14],
serialized[15]
]
)
}
}
}
Expand All @@ -54,7 +81,7 @@ impl Hash for ContractInstance {
impl ContractInstance {
fn to_address(self) -> AztecAddress {
AztecAddress::compute(
self.public_keys_hash,
self.public_keys.hash(),
PartialAddress::compute(
self.contract_class_id,
self.salt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ impl ToPoint for TpkM {
}
}

impl Serialize<POINT_LENGTH> for TpkM {
fn serialize(self) -> [Field; POINT_LENGTH] {
self.inner.serialize()
}
}

impl Empty for PublicKeys {
fn empty() -> Self {
PublicKeys {
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
TxReceipt,
UnencryptedL2BlockL2Logs,
} from '@aztec/circuit-types';
import { FunctionSelector, Header } from '@aztec/circuits.js';
import { FunctionSelector, Header, PublicKeys } from '@aztec/circuits.js';
import { NoteSelector } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Buffer32 } from '@aztec/foundation/buffer';
Expand Down Expand Up @@ -44,6 +44,7 @@ export function createAztecNodeRpcServer(node: AztecNode) {
TxHash,
Buffer32,
PublicDataWitness,
PublicKeys,
SiblingPath,
},
{
Expand Down
Loading

0 comments on commit 17c6127

Please sign in to comment.