Skip to content

Commit

Permalink
POC
Browse files Browse the repository at this point in the history
  • Loading branch information
just-mitch committed Jun 27, 2024
1 parent ed815a3 commit b1586b3
Show file tree
Hide file tree
Showing 27 changed files with 1,268 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,5 @@
"**/dest/**": true,
"**/noir/noir-repo/docs/versioned_docs/**": true
},
"cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp",
"cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp"
}
46 changes: 35 additions & 11 deletions noir-projects/aztec-nr/aztec/src/keys/public_keys.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use dep::protocol_types::{
address::PublicKeysHash, constants::GENERATOR_INDEX__PUBLIC_KEYS_HASH, hash::poseidon2_hash,
grumpkin_point::GrumpkinPoint, traits::{Deserialize, Serialize}
grumpkin_point::GrumpkinPoint, traits::{Deserialize, Serialize, Empty, is_empty}
};
use crate::keys::constants::{NUM_KEY_TYPES, NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX};

Expand All @@ -13,22 +13,46 @@ struct PublicKeys {
tpk_m: GrumpkinPoint,
}

impl Empty for PublicKeys {
fn empty() -> Self {
PublicKeys {
npk_m : GrumpkinPoint::empty(),
ivpk_m : GrumpkinPoint::empty(),
ovpk_m : GrumpkinPoint::empty(),
tpk_m : GrumpkinPoint::empty()
}
}
}

impl Eq for PublicKeys {
fn eq(self, other: PublicKeys) -> bool {
( self.npk_m == other.npk_m ) &
( self.ivpk_m == other.ivpk_m ) &
( self.ovpk_m == other.ovpk_m ) &
( self.tpk_m == other.tpk_m )
}
}

impl PublicKeys {
pub fn hash(self) -> PublicKeysHash {
PublicKeysHash::from_field(
if is_empty(self) {
0
} else {
poseidon2_hash(
[
self.npk_m.x,
self.npk_m.y,
self.ivpk_m.x,
self.ivpk_m.y,
self.ovpk_m.x,
self.ovpk_m.y,
self.tpk_m.x,
self.tpk_m.y,
GENERATOR_INDEX__PUBLIC_KEYS_HASH
]
self.npk_m.x,
self.npk_m.y,
self.ivpk_m.x,
self.ivpk_m.y,
self.ovpk_m.x,
self.ovpk_m.y,
self.tpk_m.x,
self.tpk_m.y,
GENERATOR_INDEX__PUBLIC_KEYS_HASH
]
)
}
)
}

Expand Down
2 changes: 2 additions & 0 deletions noir-projects/noir-contracts/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ members = [
"contracts/parent_contract",
"contracts/pending_note_hashes_contract",
"contracts/price_feed_contract",
"contracts/private_fpc_contract",
"contracts/private_token_contract",
"contracts/schnorr_account_contract",
"contracts/schnorr_hardcoded_account_contract",
"contracts/schnorr_single_key_account_contract",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "private_fpc_contract"
authors = [""]
compiler_version = ">=0.25.0"
type = "contract"

[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
authwit = { path = "../../../aztec-nr/authwit" }
private_token = { path = "../private_token_contract" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use dep::aztec::context::interface::PublicContextInterface;

pub fn calculate_fee<TPublicContext>(_context: TPublicContext) -> U128 where TPublicContext: PublicContextInterface {
U128::from_integer(1)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
contract PrivateFPC {
use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::is_empty};
use dep::aztec::state_vars::SharedImmutable;
use dep::private_token::PrivateToken;
use dep::aztec::context::gas::GasOpts;

#[aztec(storage)]
struct Storage {
other_asset: SharedImmutable<AztecAddress>,
admin_npk_m_hash: SharedImmutable<Field>
}

#[aztec(public)]
#[aztec(initializer)]
fn constructor(other_asset: AztecAddress, admin_npk_m_hash: Field) {
storage.other_asset.initialize(other_asset);
storage.admin_npk_m_hash.initialize(admin_npk_m_hash);
}

#[aztec(private)]
fn fund_transaction_privately(amount: Field, asset: AztecAddress, nonce: Field) {
assert(asset == storage.other_asset.read_private());
// convince the FPC we are not cheating
context.push_new_nullifier(nonce, 0);
// allow the FPC to reconstruct their fee note
context.emit_unencrypted_log(nonce);
PrivateToken::at(asset).setup_refund(
storage.admin_npk_m_hash.read_private(),
context.msg_sender(),
amount,
nonce
).call(&mut context);
context.set_as_fee_payer();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "private_token_contract"
authors = [""]
compiler_version = ">=0.25.0"
type = "contract"

[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
compressed_string = { path = "../../../aztec-nr/compressed-string" }
authwit = { path = "../../../aztec-nr/authwit" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
mod types;
mod test;

// Minimal token implementation that supports `AuthWit` accounts and private refunds

contract PrivateToken {
use dep::compressed_string::FieldCompressedString;
use dep::aztec::{
hash::compute_secret_hash,
prelude::{NoteGetterOptions, Map, PublicMutable, SharedImmutable, PrivateSet, AztecAddress},
protocol_types::{
abis::function_selector::FunctionSelector, hash::pedersen_hash,
constants::GENERATOR_INDEX__INNER_NOTE_HASH
},
oracle::unsafe_rand::unsafe_rand,
encrypted_logs::encrypted_note_emission::{encode_and_encrypt_note, encode_and_encrypt_note_with_keys}
};
use dep::authwit::{auth::{assert_current_call_valid_authwit, assert_current_call_valid_authwit_public}};
use crate::types::{token_note::{TokenNote, TOKEN_NOTE_LEN}, balances_map::BalancesMap};
use dep::std::embedded_curve_ops::EmbeddedCurvePoint;
use dep::std::ec::tecurve::affine::Point;

#[aztec(storage)]
struct Storage {
admin: PublicMutable<AztecAddress>,
minters: Map<AztecAddress, PublicMutable<bool>>,
balances: BalancesMap<TokenNote>,
total_supply: PublicMutable<U128>,
symbol: SharedImmutable<FieldCompressedString>,
name: SharedImmutable<FieldCompressedString>,
decimals: SharedImmutable<u8>,
}

#[aztec(public)]
#[aztec(initializer)]
fn constructor(admin: AztecAddress, name: str<31>, symbol: str<31>, decimals: u8) {
assert(!admin.is_zero(), "invalid admin");
storage.admin.write(admin);
storage.minters.at(admin).write(true);
storage.name.initialize(FieldCompressedString::from_string(name));
storage.symbol.initialize(FieldCompressedString::from_string(symbol));
storage.decimals.initialize(decimals);
}

#[aztec(public)]
fn set_admin(new_admin: AztecAddress) {
assert(storage.admin.read().eq(context.msg_sender()), "caller is not admin");
storage.admin.write(new_admin);
}

#[aztec(public)]
fn public_get_name() -> pub FieldCompressedString {
storage.name.read_public()
}

#[aztec(private)]
fn private_get_name() -> pub FieldCompressedString {
storage.name.read_private()
}

unconstrained fn un_get_name() -> pub [u8; 31] {
storage.name.read_public().to_bytes()
}

#[aztec(public)]
fn public_get_symbol() -> pub FieldCompressedString {
storage.symbol.read_public()
}

#[aztec(private)]
fn private_get_symbol() -> pub FieldCompressedString {
storage.symbol.read_private()
}

unconstrained fn un_get_symbol() -> pub [u8; 31] {
storage.symbol.read_public().to_bytes()
}

#[aztec(public)]
fn public_get_decimals() -> pub u8 {
storage.decimals.read_public()
}

#[aztec(private)]
fn private_get_decimals() -> pub u8 {
storage.decimals.read_private()
}

unconstrained fn un_get_decimals() -> pub u8 {
storage.decimals.read_public()
}

#[aztec(public)]
fn set_minter(minter: AztecAddress, approve: bool) {
assert(storage.admin.read().eq(context.msg_sender()), "caller is not admin");
storage.minters.at(minter).write(approve);
}

#[aztec(private)]
fn privately_mint_private_note(amount: Field) {
let caller = context.msg_sender();
let header = context.get_header();
let caller_npk_m_hash = header.get_npk_m_hash(&mut context, caller);
storage.balances.add(caller_npk_m_hash, U128::from_integer(amount)).emit(encode_and_encrypt_note(&mut context, caller, caller));
PrivateToken::at(context.this_address()).assert_minter_and_mint(context.msg_sender(), amount).enqueue(&mut context);
}

#[aztec(public)]
fn assert_minter_and_mint(minter: AztecAddress, amount: Field) {
assert(storage.minters.at(minter).read(), "caller is not minter");
let supply = storage.total_supply.read() + U128::from_integer(amount);
storage.total_supply.write(supply);
}

#[aztec(private)]
fn transfer_from(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) {
if (!from.eq(context.msg_sender())) {
assert_current_call_valid_authwit(&mut context, from);
} else {
assert(nonce == 0, "invalid nonce");
}

// By fetching the keys here, we can avoid doing an extra read from the storage, since from_ovpk would
// be needed twice.
let header = context.get_header();
let from_ovpk = header.get_ovpk_m(&mut context, from);
let from_ivpk = header.get_ivpk_m(&mut context, from);
let from_npk_m_hash = header.get_npk_m_hash(&mut context, from);
let to_ivpk = header.get_ivpk_m(&mut context, to);
let to_npk_m_hash = header.get_npk_m_hash(&mut context, to);

let amount = U128::from_integer(amount);
// docs:start:increase_private_balance
// docs:start:encrypted
storage.balances.sub(from_npk_m_hash, amount).emit(encode_and_encrypt_note_with_keys(&mut context, from_ovpk, from_ivpk));
// docs:end:encrypted
// docs:end:increase_private_balance
storage.balances.add(to_npk_m_hash, amount).emit(encode_and_encrypt_note_with_keys(&mut context, from_ovpk, to_ivpk));
}

#[aztec(private)]
fn transfer(to: AztecAddress, amount: Field) {
let from = context.msg_sender();
let header = context.get_header();
let from_ovpk = header.get_ovpk_m(&mut context, from);
let from_ivpk = header.get_ivpk_m(&mut context, from);
let from_npk_m_hash = header.get_npk_m_hash(&mut context, from);
let to_ivpk = header.get_ivpk_m(&mut context, to);
let to_npk_m_hash = header.get_npk_m_hash(&mut context, to);

let amount = U128::from_integer(amount);
storage.balances.sub(from_npk_m_hash, amount).emit(encode_and_encrypt_note_with_keys(&mut context, from_ovpk, from_ivpk));
storage.balances.add(to_npk_m_hash, amount).emit(encode_and_encrypt_note_with_keys(&mut context, from_ovpk, to_ivpk));
}

#[aztec(private)]
fn balance_of_private(owner: AztecAddress) -> pub Field {
let header = context.get_header();
let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner);
storage.balances.to_unconstrained().balance_of(owner_npk_m_hash).to_integer()
}

unconstrained fn balance_of_unconstrained(owner_npk_m_hash: Field) -> pub Field {
storage.balances.balance_of(owner_npk_m_hash).to_integer()
}

#[aztec(private)]
fn setup_refund(
fee_payer_npk_m_hash: Field,
sponsored_user: AztecAddress,
funded_amount: Field,
refund_nonce: Field
) {
assert_current_call_valid_authwit(&mut context, sponsored_user);
let header = context.get_header();
let sponsored_user_npk_m_hash = header.get_npk_m_hash(&mut context, sponsored_user);
let sponsored_user_ovpk = header.get_ovpk_m(&mut context, sponsored_user);
let sponsored_user_ivpk = header.get_ivpk_m(&mut context, sponsored_user);
storage.balances.sub(sponsored_user_npk_m_hash, U128::from_integer(funded_amount)).emit(encode_and_encrypt_note_with_keys(&mut context, sponsored_user_ovpk, sponsored_user_ivpk));
let points = TokenNote::generate_refund_points(
fee_payer_npk_m_hash,
sponsored_user_npk_m_hash,
funded_amount,
refund_nonce
);
context.set_public_teardown_function(
context.this_address(),
FunctionSelector::from_signature("complete_refund(Field,Field,Field,Field)"),
[points[0].x, points[0].y, points[1].x, points[1].y]
);
}

#[aztec(public)]
#[aztec(internal)]
fn complete_refund(
fpc_point_x: Field,
fpc_point_y: Field,
user_point_x: Field,
user_point_y: Field
) {
let fpc_point = EmbeddedCurvePoint { x: fpc_point_x, y: fpc_point_y, is_infinite: false };
let user_point = EmbeddedCurvePoint { x: user_point_x, y: user_point_y, is_infinite: false };
let tx_fee = context.transaction_fee();
let note_hashes = TokenNote::complete_refund(fpc_point, user_point, tx_fee);

// `compute_inner_note_hash` manually, without constructing the note
// `3` is the storage slot of the balances
context.push_new_note_hash(pedersen_hash([3, note_hashes[0]], GENERATOR_INDEX__INNER_NOTE_HASH));
context.push_new_note_hash(pedersen_hash([3, note_hashes[1]], GENERATOR_INDEX__INNER_NOTE_HASH));
}

/// Internal ///

#[aztec(public)]
#[aztec(internal)]
fn _reduce_total_supply(amount: Field) {
// Only to be called from burn.
let new_supply = storage.total_supply.read().sub(U128::from_integer(amount));
storage.total_supply.write(new_supply);
}

/// Unconstrained ///

unconstrained fn admin() -> pub Field {
storage.admin.read().to_field()
}

unconstrained fn is_minter(minter: AztecAddress) -> pub bool {
storage.minters.at(minter).read()
}

unconstrained fn total_supply() -> pub Field {
storage.total_supply.read().to_integer()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod basic;
mod utils;
Loading

0 comments on commit b1586b3

Please sign in to comment.