mirror of
https://github.com/logos-blockchain/logos-blockchain-circuits.git
synced 2026-05-20 16:29:31 +00:00
fix: Patch circuits' asserts (#26)
This commit is contained in:
parent
9d1f058338
commit
b5ed645243
@ -83,6 +83,7 @@ runs:
|
||||
cp "${SOURCES_ROOT}/circom_adapter.hpp" "${CIRCUIT_CPP_PATH}/circom_adapter.hpp"
|
||||
cp "${SOURCES_ROOT}/circom_fwd.hpp" "${CIRCUIT_CPP_PATH}/circom_fwd.hpp"
|
||||
cp "${SOURCES_ROOT}/types.hpp" "${CIRCUIT_CPP_PATH}/types.hpp"
|
||||
cp "${SOURCES_ROOT}/assert.h" "${CIRCUIT_CPP_PATH}/assert.h"
|
||||
|
||||
# TODO: Instead of replace, make a fork that generates the appropriate Makefile
|
||||
- name: Replace ${{ inputs.circuit-name-display }}'s Makefile
|
||||
|
||||
8
justfile
8
justfile
@ -35,7 +35,7 @@ poq: check-circom
|
||||
# circom-generated main() has no return on the success path; patch it before -O3 turns it into an infinite loop
|
||||
{{sed_i}} ':a;N;$!ba;s/\n}\n\n*$/\n return 0;\n}/' blend/poq_cpp/main.cpp
|
||||
cp -r {{src}}/poq blend/poq_cpp/poq
|
||||
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp blend/poq_cpp/
|
||||
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp {{src}}/assert.h blend/poq_cpp/
|
||||
cp {{ci_makefile}} blend/poq_cpp/Makefile
|
||||
cp blend/test_ffi.cpp blend/poq_cpp/test_ffi.cpp
|
||||
make -C blend/poq_cpp PROJECT=poq linux-lib
|
||||
@ -51,7 +51,7 @@ pol: check-circom
|
||||
# circom-generated main() has no return on the success path; patch it before -O3 turns it into an infinite loop
|
||||
{{sed_i}} ':a;N;$!ba;s/\n}\n\n*$/\n return 0;\n}/' mantle/pol_cpp/main.cpp
|
||||
cp -r {{src}}/pol mantle/pol_cpp/pol
|
||||
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp mantle/pol_cpp/
|
||||
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp {{src}}/assert.h mantle/pol_cpp/
|
||||
cp {{ci_makefile}} mantle/pol_cpp/Makefile
|
||||
cp mantle/test_pol.cpp mantle/pol_cpp/test_pol.cpp
|
||||
make -C mantle/pol_cpp PROJECT=pol linux-lib
|
||||
@ -67,7 +67,7 @@ poc: check-circom
|
||||
# circom-generated main() has no return on the success path; patch it before -O3 turns it into an infinite loop
|
||||
{{sed_i}} ':a;N;$!ba;s/\n}\n\n*$/\n return 0;\n}/' mantle/poc_cpp/main.cpp
|
||||
cp -r {{src}}/poc mantle/poc_cpp/poc
|
||||
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp mantle/poc_cpp/
|
||||
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp {{src}}/assert.h mantle/poc_cpp/
|
||||
cp {{ci_makefile}} mantle/poc_cpp/Makefile
|
||||
cp mantle/test_poc.cpp mantle/poc_cpp/test_poc.cpp
|
||||
make -C mantle/poc_cpp PROJECT=poc linux-lib
|
||||
@ -83,7 +83,7 @@ signature: check-circom
|
||||
# circom-generated main() has no return on the success path; patch it before -O3 turns it into an infinite loop
|
||||
{{sed_i}} ':a;N;$!ba;s/\n}\n\n*$/\n return 0;\n}/' mantle/signature_cpp/main.cpp
|
||||
cp -r {{src}}/signature mantle/signature_cpp/signature
|
||||
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp mantle/signature_cpp/
|
||||
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp {{src}}/assert.h mantle/signature_cpp/
|
||||
cp {{ci_makefile}} mantle/signature_cpp/Makefile
|
||||
cp mantle/test_signature.cpp mantle/signature_cpp/test_signature.cpp
|
||||
make -C mantle/signature_cpp PROJECT=signature linux-lib
|
||||
|
||||
@ -60,6 +60,29 @@ mod tests {
|
||||
static INPUTS: LazyLock<PathBuf> =
|
||||
LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json"));
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_invalid_json_returns_err() {
|
||||
let input = PocWitnessInput::new("{".to_owned()).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_missing_inputs_returns_err() {
|
||||
let input = PocWitnessInput::new("{}".to_owned()).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_constraint_violation_returns_err() {
|
||||
let json = std::fs::read_to_string(&*INPUTS).unwrap();
|
||||
let bad_json = json.replace(
|
||||
"\"voucher_root\": \"20810875415353676096192834577269613981524168537821543897016159330974871397924\"",
|
||||
"\"voucher_root\": \"1\"",
|
||||
);
|
||||
let input = PocWitnessInput::new(bad_json).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness() {
|
||||
let dat = LIB_DIR.join("witness_generator");
|
||||
|
||||
@ -60,6 +60,29 @@ mod tests {
|
||||
static INPUTS: LazyLock<PathBuf> =
|
||||
LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json"));
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_invalid_json_returns_err() {
|
||||
let input = PolWitnessInput::new("{".to_owned()).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_missing_inputs_returns_err() {
|
||||
let input = PolWitnessInput::new("{}".to_owned()).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_constraint_violation_returns_err() {
|
||||
let json = std::fs::read_to_string(&*INPUTS).unwrap();
|
||||
let bad_json = json.replace(
|
||||
"\"ledger_aged\": \"9907496234164738674719754286318998202143315407023653151112941050435603056651\"",
|
||||
"\"ledger_aged\": \"1\"",
|
||||
);
|
||||
let input = PolWitnessInput::new(bad_json).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness() {
|
||||
let dat = LIB_DIR.join("witness_generator");
|
||||
|
||||
@ -60,6 +60,31 @@ mod tests {
|
||||
static INPUTS: LazyLock<PathBuf> =
|
||||
LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json"));
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_invalid_json_returns_err() {
|
||||
let input = PoqWitnessInput::new("{".to_owned()).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_missing_inputs_returns_err() {
|
||||
let input = PoqWitnessInput::new("{}".to_owned()).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_constraint_violation_returns_err() {
|
||||
let json = std::fs::read_to_string(&*INPUTS).unwrap();
|
||||
// Swap core_root for a wrong value; the Merkle path no longer verifies,
|
||||
// so is_registered.out = 0 and the constraint at circom line 108 fires.
|
||||
let bad_json = json.replace(
|
||||
"\"core_root\": \"20423847801203321296654759690878714805328777188198550442842378955293864405749\"",
|
||||
"\"core_root\": \"1\"",
|
||||
);
|
||||
let input = PoqWitnessInput::new(bad_json).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness() {
|
||||
let dat = LIB_DIR.join("witness_generator");
|
||||
|
||||
@ -65,6 +65,18 @@ mod tests {
|
||||
static INPUTS: LazyLock<PathBuf> =
|
||||
LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json"));
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_invalid_json_returns_err() {
|
||||
let input = SignatureWitnessInput::new("{".to_owned()).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness_missing_inputs_returns_err() {
|
||||
let input = SignatureWitnessInput::new("{}".to_owned()).unwrap();
|
||||
assert!(generate_witness(&input).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_witness() {
|
||||
let dat = LIB_DIR.join("witness_generator");
|
||||
|
||||
@ -22,4 +22,23 @@ mod tests {
|
||||
let poq_result = lbc_poq_sys::generate_witness(&inputs_json);
|
||||
assert!(poq_result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_concurrent_poq_calls() {
|
||||
let inputs_json_raw = std::fs::read_to_string(inputs::POQ.as_path()).unwrap();
|
||||
|
||||
let handles: Vec<_> = (0..4)
|
||||
.map(|_| {
|
||||
let json = inputs_json_raw.clone();
|
||||
std::thread::spawn(move || {
|
||||
let input = PoqWitnessInput::new(json).unwrap();
|
||||
lbc_poq_sys::generate_witness(&input)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
for h in handles {
|
||||
assert!(h.join().unwrap().is_ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
src/assert.h
Normal file
27
src/assert.h
Normal file
@ -0,0 +1,27 @@
|
||||
// Assert-to-throw shim for circom-generated circuit code.
|
||||
//
|
||||
// Problem
|
||||
// -------
|
||||
// Circom generates C++ code that calls the standard assert() macro to enforce
|
||||
// circuit constraints (e.g. `assert(Fr_isTrue(&expaux[0]))`). When compiled
|
||||
// into a standalone binary, a failing assert aborts the subprocess and the
|
||||
// caller receives a non-zero exit code; an error. When compiled into a static
|
||||
// library and linked into the caller's process, the same abort kills the entire
|
||||
// process. A library must never call abort() on its caller.
|
||||
//
|
||||
// Mechanism
|
||||
// ---------
|
||||
// This file is copied into each circuit's build directory as assert.h. The
|
||||
// Makefile already passes -I. so the compiler finds this file before the
|
||||
// system assert.h when the generated code does `#include <assert.h>`.
|
||||
// #include_next <assert.h> pulls in the real system header (so all
|
||||
// declarations are present), then we redefine the assert macro to throw a
|
||||
// std::runtime_error instead of calling abort(). #pragma once prevents a
|
||||
// second include from re-running and undoing the redefinition.
|
||||
#pragma once
|
||||
#include_next <assert.h>
|
||||
#undef assert
|
||||
#include <stdexcept>
|
||||
#define assert(cond) \
|
||||
((cond) ? void(0) : throw std::runtime_error( \
|
||||
std::string("Circuit constraint violated in ") + __FILE__ + ":" + std::to_string(__LINE__) + ": " + #cond))
|
||||
@ -92,7 +92,13 @@ static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
|
||||
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
|
||||
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
|
||||
|
||||
loadJson(ctx, input->inputs_json);
|
||||
try {
|
||||
loadJson(ctx, input->inputs_json);
|
||||
} catch (...) {
|
||||
delete ctx;
|
||||
delete circuit;
|
||||
throw;
|
||||
}
|
||||
if (ctx->getRemaingInputsToBeSet()!=0) {
|
||||
const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + ".";
|
||||
delete ctx;
|
||||
|
||||
@ -92,7 +92,13 @@ static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
|
||||
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
|
||||
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
|
||||
|
||||
loadJson(ctx, input->inputs_json);
|
||||
try {
|
||||
loadJson(ctx, input->inputs_json);
|
||||
} catch (...) {
|
||||
delete ctx;
|
||||
delete circuit;
|
||||
throw;
|
||||
}
|
||||
if (ctx->getRemaingInputsToBeSet()!=0) {
|
||||
const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + ".";
|
||||
delete ctx;
|
||||
|
||||
@ -92,7 +92,13 @@ static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
|
||||
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
|
||||
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
|
||||
|
||||
loadJson(ctx, input->inputs_json);
|
||||
try {
|
||||
loadJson(ctx, input->inputs_json);
|
||||
} catch (...) {
|
||||
delete ctx;
|
||||
delete circuit;
|
||||
throw;
|
||||
}
|
||||
if (ctx->getRemaingInputsToBeSet()!=0) {
|
||||
const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + ".";
|
||||
delete ctx;
|
||||
|
||||
@ -92,7 +92,13 @@ static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
|
||||
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
|
||||
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
|
||||
|
||||
loadJson(ctx, input->inputs_json);
|
||||
try {
|
||||
loadJson(ctx, input->inputs_json);
|
||||
} catch (...) {
|
||||
delete ctx;
|
||||
delete circuit;
|
||||
throw;
|
||||
}
|
||||
if (ctx->getRemaingInputsToBeSet()!=0) {
|
||||
const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + ".";
|
||||
delete ctx;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user