BOLT Backend API

Table of Contents

Include this API as

#include <wtk/bolt/Backend.h>

Members of this API live in the following namespace

namespace wtk { namespace bolt { ...

The BOLT API is parameterized on two templates.

Number_T

This template is the same integer-like template which the Parser API uses for numeric literals.

Wire_T

This template is an opaque structure supplied by the Backend for encapsulating data related to a wire. The Builder requires it to be default constructible/destructible, and the Evaluator requires it be mutable/overwritable.

template<typename Wire_T, typename Number_T> class Backend

An abstract class defining directives passed on to the ZK Backend. A backend can implement this interface for BOLT to use during evaluation. Each funciton is a handler for a different simple gate.

Wires are reported as Wire_T pointers, which are generally constructed ahead of time and reused, rather than allocated once. BOLT does ensure that output pointer != input pointer, but multiple input pointers could be equal.

All handlers return void, and errors must be cached until the end of the relation when check() is called by the user.

Number_T const prime

This public attribute must be set by the constructor to the characteristic/prime modulus.

Number_T const prime;

bool const isBoolean

This public attribute must be set by the constructor to true if the GateSet is Boolean.

Number_T const prime;

Backend(Number_T const p, bool const ib)

The wtk::Backend<Wire_T, Number_T> has only one constructor, with two parameters. The first parameter, p, is the prime modulus or characteristic. The second parameter, ib, is a flag indicating true when theGateSet is Boolean and false when Arithmetic.

Backend(Number_T const p, bool const ib) : prime(p), isBoolean(ib) { }

virtual void addGate(Wire_T* const out, Wire_T const* const left, Wire_T const* const right) = 0

The backend must handle an addition. out may be assumed to point to a different object from left or right. This method is guaranteed only to be called if this→isBoolean == false.

virtual void addGate(Wire_T* const out,
    Wire_T const* const left, Wire_T const* const right) = 0;

virtual void mulGate(Wire_T* const out, Wire_T const* const left, Wire_T const* const right) = 0

The backend must handle a multiplication. out may be assumed to point to a different object from left or right. This method is guaranteed only to be called if this→isBoolean == false.

virtual void mulGate(Wire_T* const out,
    Wire_T const* const left, Wire_T const* const right) = 0;

virtual void addcGate(Wire_T* const out, Wire_T const* const left, Number_T const right) = 0

The backend must handle an addition with a constant. out may be assumed to point to a different object from left. This method is guaranteed only to be called if this→isBoolean == false.

virtual void addcGate(Wire_T* const out,
    Wire_T const* const left, Number_T const right) = 0;

virtual void mulcGate(Wire_T* const out, Wire_T const* const left, Number_T const right) = 0

The backend must handle a multiplication with a constant. out may be assumed to point to a different object from left. This method is guaranteed only to be called if this→isBoolean == false.

virtual void addcGate(Wire_T* const out,
    Wire_T const* const left, Number_T const right) = 0;

virtual void xorGate(Wire_T* const out, Wire_T const* const left, Wire_T const* const right) = 0

The backend must handle an XOR. out may be assumed to point to a different object from left or right. This method is guaranteed only to be called if this→isBoolean == true.

virtual void xorGate(Wire_T* const out,
    Wire_T const* const left, Wire_T const* const right) = 0;

virtual void andGate(Wire_T* const out, Wire_T const* const left, Wire_T const* const right) = 0

The backend must handle an AND. out may be assumed to point to a different object from left or right. This method is guaranteed only to be called if this→isBoolean == true.

virtual void andGate(Wire_T* const out,
    Wire_T const* const left, Wire_T const* const right) = 0;

virtual void notGate(Wire_T* const out, Wire_T const* const left) = 0

The backend must handle an AND. out may be assumed to point to a different object from left. This method is guaranteed only to be called if this→isBoolean == true.

virtual void notGate(Wire_T* const out, Wire_T const* const left) = 0;

virtual void copy(Wire_T* const out, Wire_T const* const left) = 0

The backend must copy from left to right. out may be assumed to point to a different object from left.

virtual void copy(Wire_T* const out, Wire_T const* const left) = 0;

virtual void assign(Wire_T* const out, Number_T const left) = 0

The backend must assign out the constant value left.

virtual void assign(Wire_T* const out, Number_T const left) = 0;

virtual void instance(Wire_T* const out, Number_T const left) = 0

The backend must assign out the instance value left.

virtual void instance(Wire_T* const out, Number_T const left) = 0;

virtual void witness(Wire_T* const out, Number_T const left) = 0

The backend must assign out the witness value left.

virtual void witness(Wire_T* const out, Number_T const left) = 0;

virtual void assertZero(Wire_T const* const wire) = 0

The backend must check that wire is equal to 0, and if this is not the case, a failure must be cached until this→check() is called.

virtual void assertZero(Wire_T const* const wire) = 0;

virtual void caseSelect(Wire_T* const selected_bit, Number_T const case_number, Wire_T const* const select_wire) = 0

This function decides if a case is selected or active by comparison of the select_wire to the case_number. If they are equal then selected_bit must be set to 1, otherwise it must be set to 0.

The default implementation uses exponentiation and Fermat’s Little Theorem to make the computation.

virtual void caseSelect(Wire_T* const selected_bit,
    Number_T const case_number, Wire_T const* const select_wire);

virtual void multiplexHelper(Wire_T* const out, std::vector<LocalWireRange<Wire_T, Number_T*>* const dummies, std::vector<Wire_T> const* const selector_bits, wtk::index_t const dummy_place)

Implements a multiplexer by summing a column of dummy wires conditionally on each selector bit. This is repeated for each column of dummies. This method is guaranteed only to be called if this→isBoolean == false.

out

output wire (sum)

dummies

A rectangular matrix, although this method must access only the dummy_place column.

selector_bits

vector of condition bits, guaranteed to have the same length as the column.

dummy_place

the column to be summed

Unfortunately, this method must expose the wtk::bolt::LocalWireRange<Wire_T, Number_T> internal structure. The backend may call only the deref(…​) method (shown here) with dummy_place as the argument.

Wire_T* deref(wtk::index_t const idx);

Here is a pseudo-ish code this function should implement:

*out := sum(i, (*dummies)[i].deref(dummy_place) * (*selector_bits)[i] );

virtual void multiplexHelper(Wire_T* const out,
    std::vector<LocalWireRange<Wire_T, Number_T>*>* const dummies,
    std::vector<Wire_T> const* const selector_bits,
    wtk::index_t const dummy_place);

virtual bool check() = 0

This method is to be called by the caller (rather than the BOLT Builder or Evaluator. After the Evaluator completes successfully, this function should be called to indicate if evaluation validity holds for the witnessed-statement.

virtual bool check() = 0;

virtual void finish()

This method is to be called by the caller (rather than the BOLT Builder or Evaluator. After the Evaluator completes (successfully or not), this function must be called to perform cleanup tasks.

virtual void finish() { }