Skip to content
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
24 changes: 19 additions & 5 deletions barretenberg/cpp/src/barretenberg/api/aztec_process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,21 @@ bool process_aztec_artifact(const std::string& input_path, const std::string& ou
return true;
}

// Strip __aztec_nr_internals__ prefix from function names.
// The #[aztec] macro generates wrapper functions with this prefix; we strip it so
// the exported ABI exposes the original developer-written names.
const std::string internal_prefix = "__aztec_nr_internals__";
for (auto& function : artifact_json["functions"]) {
auto& name = function["name"];
if (name.is_string()) {
std::string fn_name = name.get<std::string>();
if (fn_name.size() >= internal_prefix.size() &&
fn_name.compare(0, internal_prefix.size(), internal_prefix) == 0) {
name = fn_name.substr(internal_prefix.size());
}
}
}

// Filter to private constrained functions
std::vector<nlohmann::json*> private_functions;
for (auto& function : artifact_json["functions"]) {
Expand All @@ -266,14 +281,13 @@ bool process_aztec_artifact(const std::string& input_path, const std::string& ou
}
}

if (private_functions.empty()) {
if (!private_functions.empty()) {
// Generate VKs
generate_vks_for_functions(cache_dir, private_functions, force);
} else {
info("No private constrained functions found");
return true;
}

// Generate VKs
generate_vks_for_functions(cache_dir, private_functions, force);

// Write updated JSON back to file
std::ofstream out_file(output_path);
out_file << artifact_json.dump(2) << std::endl;
Expand Down
1 change: 0 additions & 1 deletion docs/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ source $(git rev-parse --show-toplevel)/ci3/source_bootstrap
repo_root=$(git rev-parse --show-toplevel)
export BB=${BB:-$repo_root/barretenberg/cpp/build/bin/bb}
export NARGO=${NARGO:-$repo_root/noir/noir-repo/target/release/nargo}
export TRANSPILER=${TRANSPILER:-$repo_root/avm-transpiler/target/release/avm-transpiler}
export BB_HASH=${BB_HASH:-$($repo_root/barretenberg/cpp/bootstrap.sh hash)}

# We search the docs/*.md files to find included code, and use those as our rebuild dependencies.
Expand Down
2 changes: 0 additions & 2 deletions docs/examples/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ REPO_ROOT=$(git rev-parse --show-toplevel)

export BB=${BB:-"$REPO_ROOT/barretenberg/cpp/build/bin/bb"}
export NARGO=${NARGO:-"$REPO_ROOT/noir/noir-repo/target/release/nargo"}
export TRANSPILER=${TRANSPILER:-"$REPO_ROOT/avm-transpiler/target/release/avm-transpiler"}
export STRIP_AZTEC_NR_PREFIX=${STRIP_AZTEC_NR_PREFIX:-"$REPO_ROOT/noir-projects/noir-contracts/scripts/strip_aztec_nr_prefix.sh"}
export BB_HASH=${BB_HASH:-$("$REPO_ROOT/barretenberg/cpp/bootstrap.sh" hash)}
export NOIR_HASH=${NOIR_HASH:-$("$REPO_ROOT/noir/bootstrap.sh" hash)}

Expand Down
34 changes: 34 additions & 0 deletions noir-projects/aztec-nr/aztec/src/oracle/auth_witness.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,37 @@ unconstrained fn get_auth_witness_oracle<let N: u32>(_message_hash: Field) -> [F
pub unconstrained fn get_auth_witness<let N: u32>(message_hash: Field) -> [Field; N] {
get_auth_witness_oracle(message_hash)
}

/// Fetches an auth witness and casts each field to a byte.
///
/// Each field is range-checked to `[0, 256)` before casting to prevent silent truncation (e.g. a field value of
/// `b + 256` would truncate to the same byte as `b`).
pub unconstrained fn get_auth_witness_as_bytes<let N: u32>(message_hash: Field) -> [u8; N] {
let witness = get_auth_witness::<N>(message_hash);
let mut result: [u8; N] = [0; N];
for i in 0..N {
assert(witness[i].lt(256), "auth witness field is not a single byte");
result[i] = witness[i] as u8;
}
result
}

mod test {
use super::get_auth_witness_as_bytes;
use std::test::OracleMock;

#[test]
unconstrained fn get_auth_witness_as_bytes_casts_valid_witness() {
let witness: [Field; 3] = [0, 127, 255];
let _ = OracleMock::mock("aztec_utl_getAuthWitness").returns(witness);
let bytes: [u8; 3] = get_auth_witness_as_bytes(0);
assert_eq(bytes, [0, 127, 255]);
}

#[test(should_fail_with = "auth witness field is not a single byte")]
unconstrained fn get_auth_witness_as_bytes_rejects_field_above_byte_range() {
let witness: [Field; 1] = [256];
let _ = OracleMock::mock("aztec_utl_getAuthWitness").returns(witness);
let _: [u8; 1] = get_auth_witness_as_bytes(0);
}
}
106 changes: 7 additions & 99 deletions noir-projects/noir-contracts/bootstrap.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env bash
# TODO: THIS SCRIPT SHOULD NOW BE ABLE TO REPLACE TRANSPILATION AND VK GENERATION WITH 'bb aztec_process'.
#
# Some notes if you have to work on this script.
# - First of all, I'm sorry (edit: not sorry). It's a beautiful script but it's no fun to debug. I got carried away.
Expand Down Expand Up @@ -30,77 +29,12 @@ export PLATFORM_TAG=any

export BB=${BB:-../../barretenberg/cpp/build/bin/bb}
export NARGO=${NARGO:-../../noir/noir-repo/target/release/nargo}
export TRANSPILER=${TRANSPILER:-../../avm-transpiler/target/release/avm-transpiler}
export STRIP_AZTEC_NR_PREFIX=${STRIP_AZTEC_NR_PREFIX:-./scripts/strip_aztec_nr_prefix.sh}
export BB_HASH=${BB_HASH:-$(../../barretenberg/cpp/bootstrap.sh hash)}
export NOIR_HASH=${NOIR_HASH:-$(../../noir/bootstrap.sh hash)}

export tmp_dir=./target/tmp

# Remove our tmp dir from last run.
# Note: This can use BASH 'trap' for better cleanliness, but the script has been hitting edge-cases so is (temporarily?) simplified.
rm -rf $tmp_dir
mkdir -p $tmp_dir

# Set common flags for parallel.
export PARALLEL_FLAGS="-j${PARALLELISM:-16} --halt now,fail=1 --memsuspend $(memsuspend_limit)"

# This computes a vk and adds it to the input function json if it's private, else returns same input.
# stdin has the function json.
# stdout receives the function json with the vk added (if private).
# The function is exported and called by a sub-shell in parallel, so we must "set -eu" etc..
# If debugging, a set -x at the start can help.
function process_function {
set -euo pipefail
local func name bytecode_b64 hash vk

contract_hash=$1
# Read the function json.
func="$(cat)"
name=$(echo "$func" | jq -r '.name')
echo_stderr "Processing function: $name..."

# Check if the function is neither public nor unconstrained.
# TODO: Why do we need to gen keys for functions that are not marked private?
# We allow the jq call to error (set +e) because it returns an error code if the result is false.
# We then differentiate between a real error, and the result being false.
set +e
make_vk=$(echo "$func" | jq -e '(.custom_attributes | index("public") == null) and (.is_unconstrained == false)')
if [ $? -ne 0 ] && [ "$make_vk" != "false" ]; then
echo_stderr "Failed to check function $name is neither public nor unconstrained."
exit 1
fi
set -e

if [ "$make_vk" == "true" ]; then
# It's a private function.
# Build hash, check if in cache.
# If it's in the cache it's extracted to $tmp_dir/$hash
bytecode_b64=$(echo "$func" | jq -r '.bytecode')
hash=$((echo "$BB_HASH"; echo "$bytecode_b64") | sha256sum | tr -d ' -')

if ! cache_download vk-$contract_hash-$hash.tar.gz >&2; then
# It's not in the cache. Generate the vk file and upload it to the cache.
echo_stderr "Generating vk for function: $name..."

local outdir=$(mktemp -d -p $tmp_dir)
echo "$bytecode_b64" | base64 -d | gunzip | $BB write_vk --scheme chonk -b - -o $outdir -v
mv $outdir/vk $tmp_dir/$contract_hash/$hash

cache_upload vk-$contract_hash-$hash.tar.gz $tmp_dir/$contract_hash/$hash
fi

# Return (echo) json containing the base64 encoded verification key.
vk=$(cat $tmp_dir/$contract_hash/$hash | base64 -w 0)
echo "$func" | jq -c --arg vk "$vk" '. + {verification_key: $vk}'
else
echo_stderr "Function $name is neither public nor unconstrained, skipping."
# Not a private function. Return the original function json.
echo "$func"
fi
}
export -f process_function

# Compute hash for a given contract.
# $1 is the contract name, $2 is the folder name (e.g. "contracts" or "examples")
function get_contract_hash {
Expand Down Expand Up @@ -159,51 +93,26 @@ function get_contract_path {
}
export -f get_contract_path

# This compiles a noir contract, transpile's public functions, and generates vk's for private functions.
# This compiles a noir contract, transpiles public functions, strips internal prefixes,
# and generates verification keys for private functions via 'bb aztec_process'.
# $1 is the input package name, $2 is the folder name (e.g. "contracts" or "examples")
# On exit it's fully processed json artifact is in the target dir.
# On exit its fully processed json artifact is in the target dir.
# The function is exported and called by a sub-shell in parallel, so we must "set -eu" etc..
function compile {
set -euo pipefail
local contract_name contract_hash

local contract_path=$(get_contract_path "$1" "$2")
local contract=$(grep -oP '(?<=^name = ")[^"]+' "$2/$contract_path/Nargo.toml")
# Calculate filename because nargo...
contract_name=$(cat $2/$contract_path/src/main.nr | awk '/^contract / { print $2 } /^pub contract / { print $3 }')
local contract_name=$(cat $2/$contract_path/src/main.nr | awk '/^contract / { print $2 } /^pub contract / { print $3 }')
local filename="$contract-$contract_name.json"
local json_path="./target/$filename"
contract_hash=$(get_contract_hash $1 $2)
local contract_hash=$(get_contract_hash $1 $2)
if ! cache_download contract-$contract_hash.tar.gz; then
$NARGO compile --package $contract --inliner-aggressiveness 0 --deny-warnings
$TRANSPILER $json_path $json_path
$STRIP_AZTEC_NR_PREFIX $json_path
$NARGO compile --package $contract --inliner-aggressiveness 0 --deny-warnings
$BB aztec_process -i $json_path
cache_upload contract-$contract_hash.tar.gz $json_path
fi

# We segregate equivalent vk's created by process_function. This was done to narrow down potential edge cases with identical VKs
# reading from cache at the same time. Create this folder up-front.
mkdir -p $tmp_dir/$contract_hash

# Pipe each contract function, one per line (jq -c), into parallel calls of process_function.
# The returned jsons from process_function are converted back to a json array in the second jq -s call.
# When slurping (-s) in the last jq, we get an array of two elements:
# .[0] is the original json (at $json_path)
# .[1] is the updated functions on stdin (-)
# * merges their fields.
# Write each function to a separate temp file to avoid pipe/stdin issues with large JSON
local func_dir=$(mktemp -d -p $tmp_dir)
local i=0
while IFS= read -r func_json; do
echo "$func_json" > "$func_dir/$i.json"
((i++)) || true
done < <(jq -c '.functions[]' $json_path)

# Process each function file in parallel
ls "$func_dir"/*.json | sort -V | \
parallel $PARALLEL_FLAGS --keep-order 'cat {} | process_function '"$contract_hash" | \
jq -s '{functions: .}' | jq -s '.[0] * {functions: .[1].functions}' $json_path - > $tmp_dir/$filename
mv $tmp_dir/$filename $json_path
}
export -f compile

Expand All @@ -220,7 +129,6 @@ function build {

if [ "$#" -eq 0 ]; then
rm -rf target
mkdir -p $tmp_dir
local contracts=$(grep -oP "(?<=$folder_name/)[^\"]+" Nargo.toml)
else
local contracts="$@"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ pub contract EcdsaKAccount {
storage::storage,
},
messages::message_delivery::MessageDelivery,
oracle::{auth_witness::get_auth_witness, notes::{get_sender_for_tags, set_sender_for_tags}},
oracle::{
auth_witness::get_auth_witness_as_bytes,
notes::{get_sender_for_tags, set_sender_for_tags},
},
state_vars::SinglePrivateImmutable,
};

Expand Down Expand Up @@ -81,14 +84,9 @@ pub contract EcdsaKAccount {
let storage = Storage::init(context);
let public_key = storage.signing_public_key.get_note();

// Load auth witness
// Safety: The witness is only used as a "magical value" that makes the signature verification below pass.
// Hence it's safe.
let witness: [Field; 64] = unsafe { get_auth_witness(outer_hash) };
let mut signature: [u8; 64] = [0; 64];
for i in 0..64 {
signature[i] = witness[i] as u8;
}
let signature: [u8; 64] = unsafe { get_auth_witness_as_bytes(outer_hash) };

// Verify payload signature using Ethereum's signing scheme
// Note that noir expects the hash of the message/challenge as input to the ECDSA verification.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ pub contract EcdsaRAccount {
storage::storage,
},
messages::message_delivery::MessageDelivery,
oracle::{auth_witness::get_auth_witness, notes::{get_sender_for_tags, set_sender_for_tags}},
oracle::{
auth_witness::get_auth_witness_as_bytes,
notes::{get_sender_for_tags, set_sender_for_tags},
},
state_vars::SinglePrivateImmutable,
};

Expand Down Expand Up @@ -79,14 +82,9 @@ pub contract EcdsaRAccount {
let storage = Storage::init(context);
let public_key = storage.signing_public_key.get_note();

// Load auth witness
// Safety: The witness is only used as a "magical value" that makes the signature verification below pass.
// Hence it's safe.
let witness: [Field; 64] = unsafe { get_auth_witness(outer_hash) };
let mut signature: [u8; 64] = [0; 64];
for i in 0..64 {
signature[i] = witness[i] as u8;
}
let signature: [u8; 64] = unsafe { get_auth_witness_as_bytes(outer_hash) };

// Verify payload signature using Ethereum's signing scheme
// Note that noir expects the hash of the message/challenge as input to the ECDSA verification.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub contract SchnorrAccount {
},
messages::message_delivery::MessageDelivery,
oracle::{
auth_witness::get_auth_witness,
auth_witness::get_auth_witness_as_bytes,
get_nullifier_membership_witness::get_low_nullifier_membership_witness,
notes::{get_sender_for_tags, set_sender_for_tags},
},
Expand Down Expand Up @@ -95,14 +95,9 @@ pub contract SchnorrAccount {
let storage = Storage::init(context);
let public_key = storage.signing_public_key.get_note();

// Load auth witness
// Safety: The witness is only used as a "magical value" that makes the signature verification below pass.
// Hence it's safe.
let witness: [Field; 64] = unsafe { get_auth_witness(outer_hash) };
let mut signature: [u8; 64] = [0; 64];
for i in 0..64 {
signature[i] = witness[i] as u8;
}
let signature: [u8; 64] = unsafe { get_auth_witness_as_bytes(outer_hash) };

let pub_key =
std::embedded_curve_ops::EmbeddedCurvePoint { x: public_key.x, y: public_key.y };
Expand All @@ -128,11 +123,7 @@ pub contract SchnorrAccount {
inner_hash,
);

let witness: [Field; 64] = get_auth_witness(message_hash);
let mut signature: [u8; 64] = [0; 64];
for i in 0..64 {
signature[i] = witness[i] as u8;
}
let signature: [u8; 64] = get_auth_witness_as_bytes(message_hash);
let pub_key =
std::embedded_curve_ops::EmbeddedCurvePoint { x: public_key.x, y: public_key.y };
let valid_in_private =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub contract SchnorrHardcodedAccount {
authwit::{account::AccountActions, entrypoint::app::AppPayload},
context::PrivateContext,
macros::functions::{allow_phase_change, external, view},
oracle::{auth_witness::get_auth_witness, notes::set_sender_for_tags},
oracle::{auth_witness::get_auth_witness_as_bytes, notes::set_sender_for_tags},
};
use std::embedded_curve_ops::EmbeddedCurvePoint;

Expand Down Expand Up @@ -38,15 +38,9 @@ pub contract SchnorrHardcodedAccount {

#[contract_library_method]
fn is_valid_impl(_context: &mut PrivateContext, outer_hash: Field) -> bool {
// Load auth witness and format as an u8 array

// Safety: The witness is only used as a "magical value" that makes the signature verification below pass.
// Hence it's safe.
let witness: [Field; 64] = unsafe { get_auth_witness(outer_hash) };
let mut signature: [u8; 64] = [0; 64];
for i in 0..64 {
signature[i] = witness[i] as u8;
}
let signature: [u8; 64] = unsafe { get_auth_witness_as_bytes(outer_hash) };

// Verify signature using hardcoded public key
schnorr::verify_signature(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ echo "Compiling contract..."
NARGO=${NARGO:-../../../noir/noir-repo/target/release/nargo}
$NARGO compile --silence-warnings --inliner-aggressiveness 0 --package $CONTRACT_PACKAGE_NAME

# Strip __aztec_nr_internals__ prefix from function names in the ABI.
echo "Stripping aztec nr prefix..."
./strip_aztec_nr_prefix.sh "../target/$JSON_NAME.json"

# Transpile public functions and generate VKs for private functions.
# Transpile public functions, strip internal prefixes, and generate VKs for private functions.
echo "Processing contract artifact..."
BB=${BB:-../../../barretenberg/cpp/build/bin/bb}
"$BB" aztec_process -i "../target/$JSON_NAME.json" -o "../target/$JSON_NAME.json" -f
Expand Down
Loading
Loading