Skip to main content

Relation

Trait Relation 

Source
pub trait Relation: Clone {
    type Instance: Clone;
    type Witness: Clone;
    type Error: From<Error>;

    // Required methods
    fn format_instance(
        instance: &Self::Instance,
    ) -> Result<Vec<Fq>, Self::Error>;
    fn circuit(
        &self,
        std_lib: &ZkStdLib,
        layouter: &mut impl Layouter<Fq>,
        instance: Value<Self::Instance>,
        witness: Value<Self::Witness>,
    ) -> Result<(), Self::Error>;
    fn write_relation<W: Write>(&self, writer: &mut W) -> Result<()>;
    fn read_relation<R: Read>(reader: &mut R) -> Result<Self>;

    // Provided methods
    fn format_committed_instances(_witness: &Self::Witness) -> Vec<Fq> { ... }
    fn used_chips(&self) -> ZkStdLibArch { ... }
}
Expand description

Helper trait, used to abstract the circuit developer from Halo2’s boilerplate.

Relation has a default implementation for loading only the tables needed for the requested chips. The developer needs to implement the function Relation::circuit, which essentially contains the statement of the proof we are creating.

§Important note

The API provided here guarantees that the number of public inputs used during verification matches the number of public inputs (as raw scalars) declared in Relation::circuit through the PublicInputInstructions interface. Proof verification will fail if this requirement is not met.

§Example

type F = midnight_curves::Fq;

#[derive(Clone, Default)]
struct ShaPreImageCircuit;

impl Relation for ShaPreImageCircuit {
    // When defining a circuit, one must clearly state the instance and the witness
    // of the underlying NP-relation.
    type Instance = [u8; 32];
    type Witness = [u8; 24]; // 192 = 24 * 8
    type Error = Error;

    // We must specify how the instance is converted into raw field elements to
    // be process by the prover/verifier. The order here must be consistent with
    // the order in which public inputs are constrained/assigned in [circuit].
    fn format_instance(instance: &Self::Instance) -> Result<Vec<F>, Error> {
        Ok(instance.iter().flat_map(AssignedByte::<F>::as_public_input).collect())
    }

    // Define the logic of the NP-relation being proved.
    fn circuit(
        &self,
        std_lib: &ZkStdLib,
        layouter: &mut impl Layouter<F>,
        _instance: Value<Self::Instance>,
        witness: Value<Self::Witness>,
    ) -> Result<(), Error> {
        let assigned_input = std_lib.assign_many(layouter, &witness.transpose_array())?;
        let output = std_lib.sha2_256(layouter, &assigned_input)?;
        output.iter().try_for_each(|b| std_lib.constrain_as_public_input(layouter, b))
    }

    fn used_chips(&self) -> ZkStdLibArch {
        ZkStdLibArch {
            sha2_256: true,
            ..ZkStdLibArch::default()
        }
    }

    fn write_relation<W: std::io::Write>(&self, _writer: &mut W) -> std::io::Result<()> {
        Ok(())
    }

    fn read_relation<R: std::io::Read>(_reader: &mut R) -> std::io::Result<Self> {
        Ok(ShaPreImageCircuit)
    }
}

const K: u32 = 13;
let mut srs = filecoin_srs(K);

let relation = ShaPreImageCircuit;

let vk = midnight_zk_stdlib::setup_vk(&srs, &relation);
let pk = midnight_zk_stdlib::setup_pk(&relation, &vk);

let mut rng = ChaCha8Rng::from_entropy();
let witness: [u8; 24] = core::array::from_fn(|_| rng.gen());
let instance = sha2::Sha256::digest(witness).into();

let proof = midnight_zk_stdlib::prove::<ShaPreImageCircuit, blake2b_simd::State>(
    &srs, &pk, &relation, &instance, witness, OsRng,
)
.expect("Proof generation should not fail");

assert!(
    midnight_zk_stdlib::verify::<ShaPreImageCircuit, blake2b_simd::State>(
        &srs.verifier_params(),
        &vk,
        &instance,
        None,
        &proof
    )
    .is_ok()
)

Required Associated Types§

Source

type Instance: Clone

The instance of the NP-relation described by this circuit.

Source

type Witness: Clone

The witness of the NP-relation described by this circuit.

Source

type Error: From<Error>

The error type returned by Self::circuit and Self::format_instance.

Required Methods§

Source

fn format_instance(instance: &Self::Instance) -> Result<Vec<Fq>, Self::Error>

Produces a vector of field elements in PLONK format representing the given Self::Instance.

Source

fn circuit( &self, std_lib: &ZkStdLib, layouter: &mut impl Layouter<Fq>, instance: Value<Self::Instance>, witness: Value<Self::Witness>, ) -> Result<(), Self::Error>

Defines the circuit’s logic.

Source

fn write_relation<W: Write>(&self, writer: &mut W) -> Result<()>

Writes a relation to a buffer.

Source

fn read_relation<R: Read>(reader: &mut R) -> Result<Self>

Reads a relation from a buffer.

Provided Methods§

Source

fn format_committed_instances(_witness: &Self::Witness) -> Vec<Fq>

Produces a vector of field elements in PLONK format representing the data inside the committed instance.

Source

fn used_chips(&self) -> ZkStdLibArch

Specifies what chips are enabled in the standard library. A chip needs to be enabled if it is used in Self::circuit, but it can also be enabled even if it is not used (possibly to share the same architecture with other circuits).

The blanket implementation enables none of them.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§