make
make install
WizToolKit Manual
WizToolKit
This is a collection of tools, provided by team Wizkit, for working with the SIEVE IR.
It In addition to a few command-line tools, it has an extensive C++ API for manipulating the SIEVE IR.
This release of WizToolKit corresponds to version 1.0.0
of the SIEVE IR Specification.
If you’re not familiar with the SIEVE IR, have a look at our introduction to the SIEVE IR. It represents ZK relations using circuits, and the instance and short-witness as input streams.
Command-line
The following tools are available on the command-line from WizToolKit.
wtk-firealarm
(checking and evaluating)-
Checks an individual IR resource for well-formedness. For a triple of relation, instance, and short-witness it checks for evaluation validity.
wtk-press
(format and feature conversions)-
Converts between the text and binary formats of the IR. It can also replace IR
@switch
directives with a multiplexer implementation using@loop
s and a@function
. wtk-viz
(visualization)-
Visualize the IR by converting it to Graphviz.
wtk-bolt
(example and testing)-
Invokes the BOLT or PLASMASnooze interpreters without ZK. For use in testing or as an example. See WizToolKit for Backends for more information on these.
For more information on these tools see WizToolKit on the command-line.
Parsing the IR
WizToolKit enables parsing both text and binary profiles of the SIEVE IR. The parsing API is designed for maximal flexibility, parsing the header first and then allowing the user to decide how to proceed.
In addition to both IR-Formats, WizToolKit can parse a resource with either a streaming parser (when grammar is limited to IR-Simple) or a syntax-tree parser.
It also allows the user to supply a numeric type for parsing field-element literals via a template parameter, and after parsing the header to possibly downgrade to uint64_t
or uint32_t
for the remaining IR body.
Parser | Format | Comment |
---|---|---|
ANTLR |
Text |
Uses the ANTLR generator. "Obviously" correct, parses line-numbers, slow |
IRRegular |
Text |
Fast, minimal memory use, no line-numbers |
FlatBuffer |
Binary |
FlatBuffer parser |
For more information see Parsing the IR.
For ZK Backends
For ZK Backends, WizToolKit provides a simple Backend API, and two interpreters to drive the API.
- BOLT
-
first builds an augmented representation of the relation while checking for well-formedness. Then it evaluates without needing any well-formedness checks or having to stop and translate indices to actual wires. This is ideal for relations which make heavy use of loops.
- PLASMASnooze
-
(A pun on FIREALARM) simply traverses the IR syntax-tree doing on-the-fly well-formedness checks.
For more information see WizToolKit for ZK Backends.
Other APIs
The entirety of the WizToolKit command-line is available as API, however not all of it is well documented.
Building
WizToolKit uses CMake, Python3, Java (optional), and a number of other utilities during build, of course in addition to a C++ compiler.
This all is orchestrated using make
, and the familiar pattern should work.
Build artifacts are typically deposited in the target/
directoy.
See the Install Guide for details, and for a list of necessary dependencies.
Licensing
The source code and other substantial material in this directory is Copyright © 2020-2022 Stealth Software Technologies, Inc.
This version of WizToolKit has been approved for public release under the open-source MIT License.
Distribution Statement A: Approved for Public Release, Distribution Unlimited.
This material is based upon work supported by DARPA under Contract No. HR001120C0087. Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of DARPA.
Installing WizToolKit
Linux and Unix derivitives
We’ve compiled a list of installation instructions for a few common systems.
RedHat/CentOS-Stream/Rocky/Alma 8
These systems are regularly tested by CI.
Starting from a 'minimal installation', You will need to install these dependencies with yum
.
yum install \
epel-release \
yum-utils
yum config-manager --set-enabled powertools
yum install \
git \
tar \
make \
wget \
unzip \
cmake \
libuuid \
libuuid-devel \
gcc-c++ \
diffutils \
which \
java-11-openjdk-devel \
java-11-openjdk-headless \
python39 \
openssl \
openssl-devel
Once installed, you can invoke make as normal.
Keep in mind, that make
will download additional dependencies.
make
make install
RedHat/CentOS 7
These systems are tested regularly in CI, however the ANTLR parser does not build due to differences in C++ dialects.
Starting with a 'minimal installation' You will need to install these dependencies with yum
.
yum install epel-release
yum install \
git \
tar \
make \
wget \
unzip \
cmake3 \
libuuid \
libuuid-devel \
gcc-c++ \
diffutils \
which \
java-11-openjdk-devel \
java-11-openjdk-headless \
python3 \
openssl \
openssl-devel
To use make
, you will need to disable ANTLR.
Keep in mind, that make
will download additional dependencies.
make ENABLE_ANTLR=0 CMAKE_CMD=cmake3
make install
Ubuntu 18.04 LTS
This platform is regularly tested in CI. To install dependencies
apt-get install \
git \
build-essential \
make \
wget \
unzip \
cmake \
g++ \
uuid-dev \
default-jdk \
python3 \
libssl-dev
Build is as normal.
Keep in mind, that make
will download additional dependencies.
make
make install
Ubuntu 20.04 LTS
This platform is regularly tested in CI. To install dependencies
apt-get install \
git \
build-essential \
make \
wget \
unzip \
cmake \
g++ \
uuid-dev \
default-jdk \
python3 \
libssl-dev
Build is as normal.
Keep in mind, that make
will download additional dependencies.
make
make install
Windows 10
We are not able to regularly test this platform as of yet, but we do some manual testing on occasion. We find that windows builds are best done within the MSYS2’s MinGW64 environment. After installing MSYS2, launch "MSYS2 MinGW 64-bit" from the "start-button".
To install the necessary dependencies use pacman
.
pacman -S --needed \
tar \
git \
wget \
make \
unzip \
diffutil \
mingw-w64-x86_64-gcc \
mingw-w64-x86_64-make \
mingw-w64-x86_64-cmake \
mingw-w64-x86_64-openssl
Then you will need to invoke make
with the correct variables.
-
ANTLR doesn’t compile in MSYS2 because Java doesn’t exist.
-
make
should be invoked as normal, but when it invokes make it should usemingw32-make
. -
CMake should use the
'MinGW Makefiles'
generator. -
The C compiler should be `/mingw64/bin/x86_64-w64-mingw32-g.exe`
To do all this, the following make
command is best.
Keep in mind, that make
will download additional dependencies.
make ENABLE_ANTLR=0 MAKE_CMD=mingw32-make CMAKE_GENERATOR='MinGW Makefiles' CXX=/mingw64/bin/x86_64-w64-mingw32-g++.exe
make ENABLE_ANTLR=0 MAKE_CMD=mingw32-make CMAKE_GENERATOR='MinGW Makefiles' CXX=/mingw64/bin/x86_64-w64-mingw32-g++.exe install
To use WizToolKit commandline tools beyond the realm of MSYS2, (for example from cmd.exe
) you will need to add to your PATH
.
Then you can invoke wtk-
commands by their absolute path (or add their path to the PATH
as well).
PATH=%PATH%;C:\msys64\mingw64\bin;C:\msys64\mingw64\lib
C:\msys64\usr\local\bin\wtk-firealarm.exe
Generic Build Guide
Dependencies
The build system for WizToolKit depends on a number of tools. See also the WizToolKit Software Bill-of-Materials
-
GNU Make: for task orchestration
-
CMake: for C++ build orchestration
-
C++ Compiler (tested against
g+` and `clang+
) -
wget
: for downloading dependencies -
Java Runtime 1.8 (or higher): for running the ANTLR tool (required only at build time)
-
Python 3: for generating portions of the IRRegular text parser (required only at build time).
-
pkg-config
: is required by CMake, and apparently not always installed by default on Ubuntu. -
OpenSSL libcrypto and
sst::bignum
:-
OpenSSL should be provided by the system.
-
sst::bignum
is downloaded automatically. -
Only the WizToolKit command line tools use the unlimited precision number library
openssl/bn.h
and C++ wrappersst::bignum
. -
the WizToolKit API does not link to these library.
-
-
ANTLR 4: for generating and running the
wtk/antlr/Parser.h
parser implementation.-
downloaded automatically at build time.
-
the runtime may require the
libuuid-devel
package (or equivalent from your system’s package manager). -
may be disabled with
make ENABLE_ANTLR=0
-
-
FlatBuffers 2.0.0: to implement the binary format of the IR specification via the FlatBuffer Parser.
-
may be disabled with
make ENABLE_FLATBUFFER=0
-
-
Stealth logging: For reporting errors and other conditions at runtime.
-
this is used by the WizToolKit API.
-
downloaded automatically at build time.
-
-
Google Test: for running unit tests.
-
downloaded automatically at build time.
-
Only used during testing.
-
Make Targets and Options
After downloading or git clone
ing a WizToolKit package, to quickly install run the following commands.
> make
> make install # as root
The makefile has the following more specific targets,
-
deps
: calls scripts to download all the non-system dependencies. -
gen_parser
: calls scripts which do code-generation for the parser implementations. -
configure
: calls CMake to configure the C++ build system. The following environment variables are respected.-
BUILD_TYPE
: indicates whether to useDebug
orRelease
mode. Defaults toRelease
. -
PREFIX
: is the installation prefix to use when callinginstall
. Defaults to/usr/local
. -
CXX
: to change the compiler -
ENABLE_ANTLR
: enables compile of the ANTLR parser, otherwise only IRRegular is produced. (default 1) -
ENABLE_FLATBUFFER
: enables compile of the FlatBuffer parser (default 1).
-
-
build
: calls CMake generate make files. -
test
: (default target) will run the unit tests. -
install
: installs all WizToolKit files to the system. -
clean
: will remove all build files. -
deps-clean
: will remove all dependencies.
WizToolKit on the Command-Line
FIREALARM (Checking and Evaluating)
FIREALARM stands for Friendly IR Evaluator And Logic Assertion and Rejection Machine, it checks and evaluates IR resources without ZK.
Its intention is to be as obviously compliant with the IR Specification as possible.
When given a relation, wtk-firealarm
will also print gate counts before completion.
When given a single resource wtk-firealarm
will checks for well-formedness.
For a relation, this means that each wire is assigned before it is used, and that it is never reused ("topological ordering", and "single static assignment (SSA)").
For an instance or short witness, this simply means that each value in the stream is a field-element (e.g. does not exceed the field’s characteristic).
When given all three resources, wtk-firealarm
will evaluate the relation with the instance and short witness, indicating if the proof should be valid or not (evaluation validity).
It can also generate a program trace at two levels of detail.
wtk-firealarm
and other WizToolKit utilities may identify resource types using the following file-suffixes.
Although the IR Specification calls for all binary resources to be identified by a .sieve
extension (when applicable), WizToolKit uses these suffixes instead.
The IR Specification does not prescribe any file-suffixes for text resources.
*.rel
|
relation |
*.ins
|
instance |
*.wit
|
short witness |
Command-Line Options/Flags
-i
|
Use the IRRegular text parser, instead of the ANTLR text parser. |
-f
|
Use the FlatBuffer binary parser, instead of the ANTLR text parser. This option is required in order to check binary resources. |
-t
|
Produces a "light" trace, showing function-boundaries, instance and witness values, and assertions. |
-T
|
Produces a "heavy" trace, all wire assignments, in addition to a "light" trace. |
-h or --help
|
Print the help text. |
-v or --version
|
Print the version text. |
Example Invocations
To check a relation, instance, or witness for well-formedness.
wtk-firealarm relation_file.rel
wtk-firealarm instance_file.ins
wtk-firealarm witness_file.wit
To check all three for evaluation validity.
wtk-firealarm relation_file.rel instance_file.ins witness_file.wit
To check with binary IR.
wtk-firealarm -f bin_relation_file.rel bin_instance_file.ins bin_witness_file.wit
PRESS (Format and Feature Conversion)
PRESS, as in printing-press, is short for Printing and Representation Exchange Software Suite.
wtk-press
is a tool for changing the format of the IR or to remove unwanted features from the IR.
Press can convert between the text and binary formats of the IR, or it can replace @switch
directives with a multiplexer.
The multiplexer is composed with @for
loops, and a @function(check_case, ….)
, which does have to exponentiate by p-1 when using arithmetic gates.
Command-Line Modes
wtk-press
uses a <mode> <input-file> [ <output-file> ]
format on its command line.
Available modes are as follows.
Text parsing is always done with IRRegular.
b2b
|
Binary to binary |
b2mux
|
Binary to binary, with multiplex converter (if necessary) |
b2t
|
Binary to text |
t2b
|
Text to binary |
t2mux
|
Text to text, with multiplex converter (if necessary) |
t2t
|
Text to text |
It also has some more testing oriented modes, which convert to nothing. Essentially they are for testing and profiling the parsers.
a2n
|
ANTLR to nothing |
b2n
|
Binary to nothing |
t2n
|
Text (IRRegular) to nothing |
Example invocations
To convert from a text file to a binary file, or reverse.
wtk-press t2b relation_file.rel bin_relation_file.rel
wtk-press b2t bin_relation_file.rel > relation_file_copy.rel
To remove the @switch
features.
wtk-press t2mux relation_file.rel relation_file_mux.rel
wtk-press b2mux bin_relation_file.rel bin_relation_file_mux.rel
Viz (Visualization)
For assistance in visualizing the IR, we came up with an IR to Graphviz converter (called wtk-viz
).
wtk-viz
expects its input to be a relation, and produces a dot graph as output.
It traverses the IR Syntax Tree, and in most cases makes a fairly naive translation into the Dot language.
In the case of for-loops, wtk-viz
repeats the loop scope mappings for each iteration of the loop, as it shows a better picture of the web created by repeated mappings.
Command Line Usage
wtk-viz
accepts arguments in a [ options… ] <input.rel> [ <output.rel> ]
form.
These are the available options.
--fg <color>
|
Changes the color of nodes, edges, and text ("foreground") to the given HTML color. |
--bg <color>
|
Changes the color of the background to the given HTML color. |
-f
|
Use FlatBuffers to parse the binary relation (defaults to Text/IRRegular). |
-h or --help`
|
Print the help text. |
-v or --version`
|
Print the version information. |
Example invocations
wtk-viz
is invoked first to produce dot source, followed by the dot
compiler to generate an image.
dot
invocation may take a long time for large circuits.
wtk-viz relation_file.rel relation_graph.dot
dot -Tsvg relation_graph.dot -o relation_graph.svg
BOLT (Example and Testing)
WizToolKit delivers the wtk::bolt
API for implementing ZK Backends in the IR.
The wtk-bolt
command-line tool serves for testing this API in a non-ZK and as an example of invoking the wtk::bolt
API.
More information may be found in WizToolKit for Backends.
wtk::bolt
implements two intepreters, BOLT for relations with heavy use of loops and PLASMASnooze for relations which are largely IR-Simple.
PLASMASnooze also implements the Parser’s streaming API for additional performance when parsing IR-Simple (rather than nearly simple).
Invocation is wtk-bolt [ bolt | plasmasnooze | stream ] <relation.rel> <instance.ins> <witness.wit>
.
Parsing the IR
WizToolKit has a simple unified API for parsing both forms of the IR. This page will start with number representations in the IR, then explain the options available for parsing, and finally a deeper dive into common paths for parsing.
Numbers in the IR
The SIEVE IR has two distint categories of numbers
- Wire indexes
-
These are represented with
wtk::index_t
, a typedef ofuint64_t
. - Field element literals
-
These are numbers mod the relations
characteristic
, which may be arbitrarily large. For maximal flexibility, WizToolKit uses aNumber_T
template parameter, which should be integer-like (e.g. implement operators for+
,*
,<<
, etc. Enough to implement string-to-int conversions).
Parsing Options
WizToolKit has three parser implementations, however all implement a common interface through the wtk::Parser<Number_T>
abstract class (#include <wtk/Parser.h>
).
It is responsible for parsing the IR’s front matter, returning control for the user to decide how to parse the IR body, and then delegating the body to a helper, also with a common interface.
As an abstract class, wtk::Parser<Number_T>
must be instantiated as one of its concrete implementations. WizToolKit offers three such implementations.
-
wtk::antlr::Parser<Number_T>
(from#include <wtk/antlr/Parser.h>
) -
wtk::irregular::Parser<Number_T>
(from#include <wtk/irregular/Parser.h>
) -
wtk::flatbuffer::Parser<Number_T>
(from#include <wtk/flatbuffer/Parser.h>
)
All have a constructor which takes a std::string
naming the file to open.
Some have additional constructors.
Before going any further, it is worth thinking about the context for parsing the IR. Is just a relation being parsed? Are all three of relation, instance, and short witness being parsed? When parsing all three, we have found it easiest to expect them listed in a defined order and fail if they deviate rather than attempting to reorder them on the fly. Later in this page we will show the expected order approach.
Here is the creation of an IRRegular parser with unsigned long
as its Number_T
(field-literal type).
std::string relation_name = /* ... */;
wtk::irregular::Parser<unsigned long> parser(relation_name);
Once the parser is created, the front matter is easy to parse.
parser.parseHdrResParams()
returns true
on success, and sets public attributes of the parser
for front matter attributes.
To check that it is a relation, the parser has a resource
attribute.
if(!parser.parseHdrResParams()
|| parser.resource != wtk::Resource::relation)
{
/* handle errors */
}
Here is a summary of the front matter attributes.
-
parser.version
is the IR version number. Its a triplet ofmajor
,minor
, andpatch
.
Warning
|
WizToolKit makes little attempt to handle older IR releases. |
-
parser.characteristic
(Number_T
) andparser.degree
(size_t
) form the field. -
parser.resource
is the resource type.wtk::Resource
is an enum with the following values-
wtk::Resource::relation
-
wtk::Resource::instance
-
wtk::Resource::shortWitness
-
-
parser.gateSet
andparser.featureToggles
are structs with indicators for which gates and which IR features are used by this relation. Seestruct GateSet
andstruct FeatureToggles
Caution
|
The parser.gateSet and parser.featureToggles attributes are assigned only for the relation, not for the instance or short witness.
|
Now the user may check the front matter for acceptability. For example, that the characteristic is prime, or from a specific family of primes. The user may also want to error here if the Gate Set or a Feature Toggle is unsupported.
Next, the user must choose how to parse the body, the instance, and the short witness. The headers of the instance and short witness can be parsed similarly to the relation’s. However, the instance and short_witness do not have Gate Set or Feature Toggles, and must defer to the relation.
A critical decision is whether the Gate Set is Boolean or arithmetic. This is because the parsing API splits on these boundaries. Another potential decision is whether or not the relation is IR-Simple. WizToolKit’s parsers have a streaming API for IR-Simple, which may improve performance for some relations. However, the streaming API cannot handle non-simple relations.
std::string instance_name = /* ... */;
std::string short_witness_name = /* ... */;
wtk::irregular::Parser<unsigned long> instance_parser(instance_name);
wtk::irregular::Parser<unsigned long> short_witness_parser(short_witness_name);
if(!instance_parser.parseHdrResParams()
|| !short_witness_parser.parseHdrResParams()
|| parser.characteristic != instance_parser.characteristic
|| parser.characteristic != short_witness_parser.characteristic)
{
/* handle error */
}
if(parser.resource == wtk::Resource::relation)
&& parser.gateSet.gateSet == wtk::GateSet::arithmetic)
{
// Parse arithmetic relation
wtk::AritmeticParser<unsigned long>* arith_parser = parser.arithmetic();
if(parser.featureToggles.simple()) // select the streaming API
{
// See Streaming API
}
else
{
// See Syntax Tree API
}
}
else if(parser.resource == wtk::Resource::relation)
&& parser.gateSet.gateSet == wtk::GateSet::boolean)
{
// Parse boolean relation, Note that the template is dropped,
// because Boolean literals are always given as uint8_t.
wtk::BooleanParser* bool_parser = parser.arithmetic();
/* Omitted... */
}
Parsing the Instance and Short Witness
Both the instance and short-witness are abstracted as streams in the SIEVE IR.
WizToolKit handles these streams through the wtk::InputStream<Number_T>
API.
The wtk::InputStream<Number_T>
objects are obtained through the parser after specializing to arithmetic or boolean.
// Get an arithmetic parser for an instance
wtk::ArithmeticParser<unsigned long>* a_ins_parser = instance_parser.arithmetic();
wtk::InputStream<unsigned long>* a_ins = a_ins_parser->instance();
// Similarly, for Boolean short witness (as an example; instance and short
// witness should have the same type).
wtk::BooleanParser* b_wit_parser = short_witness_parser.arithmetic();
wtk::InputStream<uint8_t>* b_wit = b_wit_parser->shortWitness();
Once a stream is obtained, its next(…)
method may be called repeatedly for each element of the stream.
The return type of next(…)
will indicate if it succeeded, failed, or reached the end of the stream. It has a return-by-pointer argument for the stream element.
unsigned long ins_val = 0;
wtk::StreamStatus status = a_ins->next(&ins_val);
if(status == wtk::StreamStatus::end) { /* End of stream */ }
else if(status == wtk::StreamStatus::error) { /* mid-stream parser error */ }
Some of the parsers (namely ANTLR and FlatBuffer) will parse the entire stream up front, in which case a parser error is indicated on the first element, regardless of its place in the stream. Other parsers (namely IRRegular) work in a more true stream-wise fashion, reporting the error as it occurs.
Streaming API for Relations
The Streaming API, IR-Simple relations are parsed one gate at a time and reported immediately. This only works for IR-Simple because these relations have no nested scopes or repetition.
To report gates, the user must implement the wtk::ArithmeticStreamHandler<Number_T>
or wtk::BooleanStreamHandler
abstract class (depending on Gate Set, obviously).
A brief example for Arithmetic is shown here.
class UserArithmeticStreamHandler : public wtk::ArithmeticStreamHandler<unsigned long>
{
void handleAdd(wtk::index_t const out, // output wire-number
wtk::index_t const left_in, // left input wire-number
wtk::index_t const right_in) // right input wire-number
override
{
/* omitted */
}
void handleMul(wtk::index_t const out, // output wire-number
wtk::index_t const left_in, // left input wire-number
wtk::index_t const right_in) // right input wire-number
override
{
/* omitted */
}
/* Remaining methods omitted... */
};
Invocation for the streaming API is through the wtk::ArithmeticParser<Number_T>
method parseStream(…)
or wtk::BooleanParser
method parseStream(…)
.
// For an arithmetic simple relation
UserArithmeticStreamHandler handler;
if(!arith_parser->parseStream(&handler)) { /* Parse Error */ }
// For a Boolean simple relation
UserBooleanStreamHandler handler;
if(!bool_parser->parseStream(&handler)) { /* Parse Error */ }
Syntax Tree API for Relations
The Syntax Tree API can handle any relation, regardless of its feature set. However, it must parse the entire relation ahead of time, and allocate a syntax tree, which for very long relations can consume a lot of memory.
The syntax tree is defined by #include <wtk/IRTree.h>.
At a top-level it is defined by the wtk::IRTree<Number_T>
, which aggregates function definitions and the relation’s main body.
Each scope is a wtk::DirectiveList<Number_T>
with the ability to retrieve gates such as wtk::BinaryGate
(binary referring to its cardinality) or wtk::Input
(for the instance or short-witness).
The Tree is again retrieved through the wtk::ArithmeticParser<Number_T>
or wtk::BooleanParser
parseTree()
method (only Arithmetic shown).
wtk::IRTree<unsigned long>* ir_tree = arith_parser->parseTree();
if(ir_tree == nullptr) { /* Parser error */ }
// To be implemented by the user.
process_ir_tree(ir_tree);
To process the relation, the user must do a tree traversal.
At a top level, the wtk::IRTree<Number_T>
holds a list of function declarations (we’ll get to these later), and the relation’s main body.
The main body is just a wtk::DirectiveList<Number_T>
, which is an indirectly-recursive type, defining much of the syntax tree.
void process_ir_tree(wtk::IRTree<unsigned long>* tree)
{
// See Syntax Tree API for Functions for function-declarations.
// The circuit's entry point is its body.
process_directive_list(tree->body());
}
Processing the wtk::DirectiveList<Number-T>
is a simple matter of traversing each directive in the scope, and switching on its type.
Here is an example.
void process_directive_list(wtk::DirectiveList<unsigned long>* dir_list)
{
// The DirectiveList is a tree type, leaf-nodes are typically gates
// (@and/@xor/@mul/etc.) with other nodes taking the form of higher
// level features.
for(size_t i = 0; i < dir_list->size(); i++)
{
switch(dir_list->type(i))
{
case wtk::DirectiveList<unsigned long>::BINARY_GATE:
{
// the "binary" (in signature, not value) gate describes
// @and/@xor/@mul/@add gates.
wtk::BinaryGate* gate = dir_list->binaryGate(i);
// these are the input and output wire numbers of the gate
wtk::index_t left_input_wire = gate->leftWire();
wtk::index_t right_input_wire = gate->rightWire();
wtk::index_t output_wire = gate->outputWire();
// The gate's calculation is an enum
switch(gate->calculation())
{
case wtk::BinaryGate::AND: { break; }
case wtk::BinaryGate::XOR: { break; }
case wtk::BinaryGate::ADD: { break; }
case wtk::BinaryGate::MUL: { break; }
}
break;
}
/* other gate-types omitted for brevity */
case wtk::DirectiveList<unsigned long>::ANON_FUNCTION:
{
wtk::AnonFunction<unsigned long>* anon_func = dir_list->anonFunction(i);
// The signature of the function, along with its inputs/outputs
// is easily retrievable.
wtk::WireList* output_wires = anon_func->outputList();
wtk::WireList* input_wires = anon_func->inputList();
wtk::index_t num_instance_vals = anon_func->instanceCount();
wtk::index_t num_witness_vals = anon_func->shortWitnessCount();
// To process the body of the anonymous function, use recursion.
process_directive_list(anon_func->body());
break;
}
/* Other feature-types omitted for brevity */
}
}
}
Syntax Tree API for Functions
For IR named-functions, the body of a function and its invocation are split.
All function-declarations are listed at the top of a relation.
Correspondingly, the wtk::IRTree<Number_T>
has a functionDeclare(i)
method for retrieval, and size()
indicating how many may be retrieved.
For easy access, we suggest entering the function declarations into a std::map
or std::unordered_map
.
// declare a map to hold them
std::map<std::string, wtk::FunctionDeclare<unsigned long>*> functions_map;
// enter each function-declaration into the map
for(size_t i = 0; i < tree->size(); i++)
{
wtk::FunctionDeclare<unsigned long>* function = tree->functionDeclare(i);
// function->name() is a char* while std::map works only with std::strings
// with c++17, it should be okay to use std::string_view so long as
// *tree outlives functions_map
std::string name(function->name());
// check that the function wasn't previously declared
auto finder = functions_map.find(name);
if(finder != functions_map.end()) { /* Error */ }
functions_map[name] = function;
}
When a function is invoked (with a wtk::FunctionInvoke
directive), its is now just a matter of looking up its name in the map.
case wtk::DirectiveList<unsigned long>::FUNCTION_INVOKE:
{
wtk::FunctionInvoke* invoke = dir_list->functionInvoke(i);
std::string name(invoke->name());
auto finder = functions_map.find(name);
if(finder == functions_map.end()) { /* Error */ }
wtk::FunctionDeclare<unsigned long>* declaration = finder->second;
// To process the body of the function, use recursion.
process_directive_list(declaration->body());
break;
}
For more information about the IR Syntax Tree, See #include <wtk/IRTree.h>
.
WizToolKit for Backends
WizToolKit’s BOLT and PLASMASnooze IR interpreters share a common Backend API. The advantage of this is it allows the backend to focus on proofs and reuse IR semantics from WizToolKit. BOLT is a "two-pass" interpreter and is most ideally suited for relations which rely heavily on loops. PLASMASnooze is a "single-pass" interpreter and picks up the slack when very few gates are reused.
The following includes are required.
#include <wtk/bolt/Builder.h>
-
the build phase of the BOLT interpreter.
#include <wtk/bolt/Evaluator.h>
-
the evaluate phase of BOLT interpreter.
#include <wtk/bolt/PLASMASnooze.h>
-
the PLASMASnooze interpreter.
#include <wtk/bolt/ArithmeticPLASMASnoozeHandler.h>
, and#include <wtk/bolt/BooleanPLASMASnoozeHandler.h>
-
the PLASMASnooze interpreter, adapted to the Streaming Parser API.
#include <wtk/bolt/Backend.h>
-
the interface which a backend implementor must implement.
For those who are curious about the naming scheme of these, BOLT is short for Better Optimization via Lookup-reuse and Two-pass.
PLASMASnooze is an improvement over FIREALARM in the sense of performance and of plugability with ZK Backends.
However, it is also a disprovement in the sense that it relaxes certain IR semantics, for example, it allows reassignment to a wire, after the wire was @delete
d.
Thus PLASMA is an improvement upon FIRE, and Snooze is the opposite of ALARM.
PLASMASnooze stands for Practical Local Acceleration for Single-pass with Malleable Assumtions Snooze.
Numeric Representations
In this API there are generally three numeric representation types.
- Wire numbers
-
A
typedef
namedwtk::index_t
defines the 64-bit unsigned integers (with wraparound) required by the IR specification for indices in the wire numbering system. These are almost entirely hidden from the backend. - Wire values
-
A template parameter which is typically referred to as
Wire_T
must be specified by the backend to carry the value across each wire. - Parser literals
-
A template parameter which is typically referred to as
Number_T
must be the same numeric type as parameterizing the parser.
The Backend API
The wtk::bolt::Backend<Wire_T, Number_T>
is a simple abstract class which must be implemented by the backend.
It declares a number of methods which must be overridden by the backend.
Each method handles a simple gate, such as @mul
or @xor
.
WizToolKit guarantees that it will only call methods of one Gate Set.
If an implementor can only handle one Gate Set it is acceptable to have empty or failing implementations for the other Gate Set.
typedef MyWire /* something that is default-constructible */;
struct MyBackend : public wtk::bolt::Backend<MyWire, uint64_t>
{
void addGate(MyWire* const out, MyWire const* const left, MyWire const* const right) override
{
/* implementation */
}
void mulGate(MyWire* const out, MyWire const* const left, MyWire const* const right) override
{
/* implementation */
}
/* remaining methods omitted for brevity */
};
Assert Zero
The @assert_zero
directive indicates that the proof would fail if its input were non-zero.
The Backend API expects the implementor to cache assertions and check them at the end.
This is done with a pair of assertZero(…)
and check()
methods.
Here is an example.
bool failureCache = false;
void assertZero(MyWire const* const wire) override
{
this->failureCache = (MyWire != 0) || this->failureCache;
}
bool check() override
{
return this->failureCache;
}
Replacing Exponentiation in Switch-Statements
By default, WizToolKit uses exponentiation (and Fermat’s Little Theorem) to test which case is selected in an arithmetic switch-statement.
This means that there are n log(P)
many additional multiplications per switch statement (n
being number of cases and P
being the prime).
If an implementor’s ZK system can improve upon this, they may override the caseSelect(…)
function.
The selected_bit
output wire must be assigned 1
or 0
when the select_wire
input wire is or is not equal to the case_number
field-literal.
void caseSelect(MyWire* const selected_bit,
uint64_t const case_number, MyWire const* const select_wire) override
{
// Implement this
}
Invoking BOLT
BOLT has two phases of invocation. First build translates the IR syntax tree to a more accelerated form, then evaluate processes the relation, invoking methods of the backend as necessary. Here is an example.
// Collect these from the parser
uint64_t characteristic = /* ... */;
bool is_boolean = /* ... */;
wtk::IRTree<uint64_t>* relation = /* ... */;
wtk::InputStream<uint64_t>* instance = /* ... */;
wtk::InputStream<uint64_t>* witness = /* ... */;
// Build the relation. *bolt_relation has the same lifetime as builder
wtk::bolt::Builder<MyWire, uint64_t> builder(characteristic);
wtk::bolt::Bolt<MyWire, uint64_t>* bolt_relation = builder.build(relation);
// Check that build succeeded and then evaluate.
if(bolt_relation != nullptr)
{
// Evaluate the relation
MyBackend backend(characteristic, is_boolean, /* ... */);
wtk::bolt::Evaluator<MyWire, uint64_t> evaluator(&backend);
if(!evaluator.evaluate(bolt_relation, instance, witness))
{
/* Instance or witness is poorly formed */
}
else if(!backend.check())
{
/* an assert zero failed, or other things happened to invalidate the proof */
}
else
{
/* success */
}
backend.finish();
}
At invocation time, it may be nonsensical for the a verifier to have a witness stream.
To handle this, evaluate(…)
may be called with a nullptr
.
In this case, the evaluator will feed the backend zeroes in place of the witness.
if(!evaluator.evaluate(bolt_relation, instance, nullptr))
Invoking PLASMASnooze
PLASMASnooze has just a single phase of execution. Instead of returning true/false for success or failure, it returns an enumeration indicating which resource caused the failure. As with BOLT, PLASMASnooze indicates only failures of each individual resource, leaving the backend to indicate failure of the proof.
// Collect these from the parser
uint64_t characteristic = /* ... */;
bool is_boolean = /* ... */;
wtk::IRTree<uint64_t>* relation = /* ... */;
wtk::InputStream<uint64_t>* instance = /* ... */;
wtk::InputStream<uint64_t>* witness = /* ... */;
// Evaluate the relation
MyBackend backend(characteristic, is_boolean, /* ... */);
wtk::bolt::PLASMASnooze<MyWire, uint64_t> snooze(&backend);
wtk::bolt::PLASMASnoozeStatus status =
snooze.evaluate(relation, instance, witness);
if(wtk::bolt::PLASMASnoozeStatus::bad_relation == status)
{
/* Relation is poorly formed */
}
if(wtk::bolt::PLASMASnoozeStatus::bad_stream == status)
{
/* Instance or witness is poorly formed */
}
else if(!backend.check())
{
/* an assert zero failed, or other things happened to invalidate the proof */
}
else
{
/* success */
}
backend.finish();
Similarly to BOLT, PLASMASnooze may be invoked with a null witness, to feed a verifier zeroes in the place of witnesses.
wtk::bolt::PLASMASnoozeStatus status =
snooze.evaluate(relation, instance, nullptr);
Streaming PLASMASnooze
A further optimization to PLASMASnooze, when processing strict IR-Simple, is to use the parser’s streaming API.
When the relation is IR-Simple (a completely "flat" list of gates), the parser can pass each gate to a handler immediately after its parsed, rather than adding it to a syntax tree.
The wtk::bolt::ArithmeticPLASMASnoozeHandler<Wire_T, Number_T>
and wtk::bolt::BooleanPLASMASnoozeHandler<Wire_T>
(Number_T
is fixed as uint8_t
) implement the Streaming API for PLASMASnooze. the check()
method of each must be used to collect the status code (wtk::bolt::PLASMASnoozeStatus
).
Here is an example invocation for arithmetic PLASMASnooze streaming.
// Collect these from the parser
uint64_t characteristic = /* ... */;
bool is_boolean = /* ... */;
wtk::ArithemticParser<uint64_t>* relation_parser = /* ... */;
wtk::InputStream<uint64_t>* instance = /* ... */;
wtk::InputStream<uint64_t>* witness = /* ... */;
// Evaluate the relation
MyBackend backend(characteristic, is_boolean, /* ... */);
wtk::bolt::ArithmeticPLASMASnoozeHandler<MyWire, uint64_t> snooze(&backend, instance, witness);
if(!relation_parser->parseStream(&snooze))
{
/* Syntax error */
}
else
{
wtk::bolt::PLASMASnoozeStatus status = snooze.check();
if(wtk::bolt::PLASMASnoozeStatus::bad_relation == status)
{
/* Relation is poorly formed */
}
if(wtk::bolt::PLASMASnoozeStatus::bad_stream == status)
{
/* Instance or witness is poorly formed */
}
else if(!backend.check())
{
/* an assert zero failed, or other things happened to invalidate the proof */
}
else
{
/* success */
}
}
backend.finish();
For verifiers, who have access to the instance but not the witness, the handler may be constructed with a nullptr
witness.
wtk::bolt::ArithmeticPLASMASnoozeHandler<MyWire, uint64_t> snooze(&backend, instance, nullptr);
Appendix A: API Documentation
Parser API
Include this API as
#include <wtk/Parser.h>
Members of this API live in the following namespace
namespace wtk { ...
The Parser API is parameterized on a Number_T
template.
Number_T
should be integer-like enough to be parsed from a string.
This API is abstract and implemented by parsers for various IR formats.
template<typename Number_T> struct Parser
The Parser will read the IR’s front matter before delegating to an wtk::ArithmeticParser<Number_T>
or a wtk::BooleanParser
.
The struct’s public data members are set to default values (mostly 0) at construction and assigned concrete values as parse methods are invoked.
The parser methods are expected to be called in the correct order (parseHeader()
, parseResource()
, and finally for relations only parseParameters()
).
A helper for calling them in the correct order is provided as parseHdrResParams()
.
When the front-matter has been parsed, one of arithmetic()
, arithmetic64()
, arithmetic32()
, or boolean()
may be invoked to delegate parsing the body to either of wtk::ArithemticParser<…>
or wtk::BooleanParser
.
bool parseHeader()
Parses the resources header section setting the following attributes.
Returns true
on success, false if a parse error occurs.
virtual bool parseHeader() = 0;
bool parseResource()
Parses the resource type and sets the wtk::Resource resource
attribute.
Returns true
on success, false if a parse error occurs.
virtual bool parseResource() = 0;
bool parseParameters()
Parses the relation’s parameters and sets the following attributes.
Returns true
on success, false if a parse error occurs.
- WARNING
-
parseParameters()
should only be used ifresource == wtk::Resource::relation
.
virtual bool parseParameters() = 0;
bool parseHdrResParams()
This is a helper to call parseHeader()
, parseResource()
, and, if necessary, parseParameters()
in sequence.
Returns true
on success, false if a parse error occurs.
virtual bool parseHdrResParams() = 0;
struct { … } version
The version is the first line of an IR resource. Although WizToolKit tends to be tied to a particular IR version, the Parser does not make any checks for the particular IR version.
The version consists of major
, minor
, and patch
elements, all of type size_t
.
struct {
size_t major = 0;
size_t minor = 0;
size_t patch = 0;
} version;
Number_T characteristic
The field’s prime modulus or "characteristic". The parser does not check for primality.
Number_T characteristic = Number_T(0);
size_t degree
The field’s degree.
The IR requires this to be 1
, although the parser does not test this.
size_t degree = 0;
wtk::Resource resource
The resource type which is currently being parsed.
This has type wtk::Resource
.
wtk::Resource resource = Resource::invalid;
wtk::GateSet gateSet
The set of gates to be allowed within the relation.
This has type wtk::GateSet
.
- WARNING
-
gateSet
may only be used ifresource == wtk::Resource::relation
.
wtk::GateSet gateSet;
wtk::FeatureToggles featureToggles
The IR structural features to be allowed within the relation.
This has type wtk::FeatureToggles
.
- WARNING
-
featureToggles
may only be used ifresource == wtk::Resource::relation
.
wtk::FeatureToggles featureToggles;
wtk::ArithemticParser<Number_T>* arithmetic()
This method returns a delegate parser for an arithmetic resource.
- WARNING
-
When parsing a relation, use of an arithmetic parser for parsing a non-arithmetic gateset will result in parse errors.
void wtk::ArithmeticParser<Number_T>* arithmetic() = 0;
wtk::ArithemticParser<uint64_t>* arithmetic()
This method returns a delegate parser for an arithmetic resource. However the Number_T
template is downgraded to a uint64_t
.
- WARNING
-
When parsing a relation, use of an arithmetic parser for parsing a non-arithmetic gateset will result in parse errors.
void wtk::ArithmeticParser<uint64_t>* arithmetic64() = 0;
wtk::ArithemticParser<uint32_t>* arithmetic()
This method returns a delegate parser for an arithmetic resource. However the Number_T
template is downgraded to a uint32_t
.
- WARNING
-
When parsing a relation, use of an arithmetic parser for parsing a non-arithmetic gateset will result in parse errors.
void wtk::ArithmeticParser<uint32_t>* arithmetic32() = 0;
wtk::BooleanParser* boolean()
This method returns a delegate parser for a boolean resource.
- NOTE
-
The
Number_T
template is removed forwtk::BooleanParser
, anduint8_t
is always used in its place. - WARNING
-
When parsing a relation, use of a boolean parser for parsing a non-boolean gateset will result in parse errors.
void wtk::BooleanParser* boolean() = 0;
template<typename Number_T> struct ArithmeticParser
This is a delegate parser for the body of an arithmetic IR resource.
Its interface is substantially similar to wtk::BooleanParser
.
In general, this type should not be constructed, but rather obtained from parser.arithmetic()
.
bool parseStream(wtk::ArithmeticStreamHandler<Number_T>* handler)
Parses the body of an arithmetic IR-Simple relation, passing each gate off to the handler in a streaming fashion.
The parser.resource
must be wtk::Resource::relation
.
The handler
parameter must be nonnull.
The method returns false
on failure, including if either the parser.gateSet
or parser.featureToggles
are violated.
It does not make any other well-formedness checks.
virtual bool parseStream(wtk::ArithmeticStreamHandler<Number_T>* handler) = 0;
wtk:IRTree<Number_T> parseTree()
Parses the body of any arithmetic relation, constructing a syntax tree.
The parser.resource
must be wtk::Resource::relation
.
If a parse failure occurs, nullptr
is returned, including if either the parser.gateSet
or parser.featureToggles
are violated.
It does not make any other well-formedness checks.
virtual wtk::IRTree<Number_T>* parseTree() = 0;
wtk::InputStream<Number_T>* instance()
Returns an wtk::InputStream<Number_T>*
which will parse the instance one value at a time.
The parser.resource
must be wtk::Resource::instance
.
This method will never return nullptr
, instead returned wtk::InputStream<Number_T>*
will return wtk::StreamStatus::error
as necessary.
virtual wtk::InputStream<Number_T>* instance() = 0;
wtk::InputStream<Number_T>* shortWitness()
Returns an wtk::InputStream<Number_T>*
which will parse the short witness one value at a time.
The parser.resource
must be wtk::Resource::shortWitness
.
This method will never return nullptr
, instead returned wtk::InputStream<Number_T>*
will return wtk::StreamStatus::error
as necessary.
virtual wtk::InputStream<Number_T>* shortWitness() = 0;
struct BooleanParser
This is a delegate parser for the body of an boolean IR resource.
Its interface is substantially similar to wtk::ArithmeticParser<Number_T>
.
In general, this type should not be constructed, but rather obtained from parser.boolean()
.
Note that this struct is not parameterized by Number_T
.
Instead anywhere the a field/numeric literal would be expected, uint8_t
is used instead.
bool parseStream(wtk::BooleanStreamHandler* handler)
Parses the body of an boolean IR-Simple relation, passing each gate off to the handler in a streaming fashion.
The parser.resource
must be wtk::Resource::relation
.
The handler
parameter must be nonnull.
The method returns false
on failure, including if either the parser.gateSet
or parser.featureToggles
are violated.
It does not make any other well-formedness checks.
virtual bool parseStream(wtk::BooleanStreamHandler* handler) = 0;
wtk:IRTree<uint8_t> parseTree()
Parses the body of any arithmetic relation, constructing a syntax tree.
The parser.resource
must be wtk::Resource::relation
.
If a parse failure occurs, nullptr
is returned, including if either the parser.gateSet
or parser.featureToggles
are violated.
It does not make any other well-formedness checks.
virtual wtk::IRTree<uint8_t>* parseTree() = 0;
wtk::InputStream<uint8_t>* instance()
Returns an wtk::InputStream<uint8_t>*
which will parse the instance one value at a time.
The parser.resource
must be wtk::Resource::instance
.
This method will never return nullptr
, instead returned wtk::InputStream<Number_T>*
will return wtk::StreamStatus::error
as necessary.
virtual wtk::InputStream<uint8_t>* instance() = 0;
wtk::InputStream<uint8_t>* shortWitness()
Returns an wtk::InputStream<uint8_t>*
which will parse the short witness one value at a time.
The parser.resource
must be wtk::Resource::shortWitness
.
This method will never return nullptr
, instead returned wtk::InputStream<Number_T>*
will return wtk::StreamStatus::error
as necessary.
virtual wtk::InputStream<uint8_t>* shortWitness() = 0;
template<typename Number_T> struct InputStream
The InputStream represents either an instance or a short witness, and and allows the user to consume one value at a time from the stream.
Do not attempt to construct one manually.
Instead retrieve one from arithmeticParser.instance()
, arithmeticParser.shortWitness()
, booleanParser.instance()
, or booleanParser.shortWitness()
.
StreamStatus next(Number_T* num)
Consumes a single value from the stream, placing it in the num
parameter.
This operation may fail if a parse error or the end of file is reached, returning an wtk::StreamStatus
error code.
virtual StreamStatus next(Number_T* num) = 0;
size_t lineNum()
If the parser supports line numbering, then this method returns the line number corresponding to the prior invocation of this→next(…)
.
If line numbering is not supported then 0
is returned.
If this→next(…)
has not been called, it returned wtk::StreamStatus::end
, or it returned wtk::StreamStatus::error
then this→lineNum()
may return any of 0
, the line number on which the error or end occurred, or the line number of the most recent successful call to this→next(…)
.
virtual size_t lineNum();
enum StreamStatus
This enumeration indicates the success or means of failure for an wtk::InputStream<Number_T>
.
It may take one of the following values.
wtk::StreamStatus::success
-
Successfully retrieved an item from the stream.
wtk::StreamStatus::end
-
Reached the end of the stream.
wtk::StreamStatus::error
-
A parse error occurred.
IR Tree API
Include this API as
#include <wtk/IRTree.h>
Members of this API live in the following namespace
namespace wtk { ...
The IR Tree API is parameterized on a Number_T
template.
Number_T
should be integer-like enough to be parsed from a string.
This API is abstract and implemented by parsers for various IR formats.
As an abstract interface many "attributes" must be accessed through accessor methods due to different storage methods of various IR formats.
At a top level, the wtk::IRTree<Number_T>
is the root of an abstract syntax tree for a relation in the SIEVE IR.
The wtk::DirectiveList<Number_T>
defines scope block within the AST and other structs such as wtk::BinaryGate
defining individual directives.
Pointer members provided by the IR Tree API may be expected to be nonnull, taking the lifetime of the parser which provided a top-level wtk::IRTree<Number_T>*
.
However, as defined by the parser, the wtk::IRTree<Number_T>*
may itself be null.
The parser retains ownership of all objects from this API, the caller may not free(…)
them.
template<typename Number_T> struct IRTree
The wtk::IRTree<Number_T>
struct is the root of an IR syntax tree.
The encapsulates both a list of named functions and the top-level scope of an IR relation.
size_t size()
This method indicates how many named function declarations are defined by this relation.
It is an integer greater than or equal to 0
.
virtual size_t size() = 0;
wtk::FunctionDeclare<Number_T>* functionDeclare(size_t n)
Retrieve a named function declaration by index.
n
must be between 0
(inclusive) and this→size()
(exclusive) or else undefined behavior occurs.
It returns a nonnull wtk::FunctionDeclare<Number_T>*
.
virtual wtk::FunctionDeclare<Number_T>* functionDeclare(size_t n) = 0;
wtk::DirectiveList<Number_T>* body()
Retrieve the body of the relation.
It returns a nonnull wtk::DirectiveList<Number_T>*
.
virtual wtk::DirectiveList<Number_T>* body() = 0;
size_t lineNum()
Returns the line number at which the IRTree begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct DirectiveList
The wtk::DirectiveList<Number_T>
is a list type for directives.
Since directives must be differentiated, each index of the list has a tag indicating a type, through which the element must be retrieved.
size_t size()
Indicates the number of elements in the list.
virtual size_t size() = 0;
enum Type
This enumerates the various types which an element may take.
wtk::DirectiveList<Number_T>::BINARY_GATE
-
corresponds to
wtk::BinaryGate
andthis→binaryGate(n)
. wtk::DirectiveList<Number_T>::UNARY_GATE
-
corresponds to
wtk::UnaryGate
andthis→unaryGate(n)
. wtk::DirectiveList<Number_T>::BINARY_CONST_GATE
-
corresponds to
wtk::BinaryConstGate<Number_T>
andthis→binaryConstGate(n)
. wtk::DirectiveList<Number_T>::INPUT
-
corresponds to
wtk::Input
andthis→input(n)
. wtk::DirectiveList<Number_T>::ASSIGN
-
corresponds to
wtk::Assign<Number_T>
andthis→assign(n)
. wtk::DirectiveList<Number_T>::ASSERT_ZERO
-
corresponds to a
wtk::Terminal
when used for the@assert_zero
gate andthis→assertZero(n)
. wtk::DirectiveList<Number_T>::DELETE_SINGLE
-
corresponds to a
wtk::Terminal
when used for a@delete
directive andthis→deleteSingle(n)
. wtk::DirectiveList<Number_T>::DELETE_RANGE
-
corresponds to an
wtk::WireRange
when used for a@delete
directive andthis→deleteRange(n)
. wtk::DirectiveList<Number_T>::FUNCTION_INVOKE
-
corresponds to
wtk::FunctionInvoke
andthis→functionInvoke(n)
. wtk::DirectiveList<Number_T>::ANON_FUNCTION
-
corresponds to
wtk::AnonFunction<Number_T>
andthis→anonFunction(n)
. wtk::DirectiveList<Number_T>::FOR_LOOP
-
corresponds to
wtk::ForLoop<Number_T>
andthis→forLoop(n)
. wtk::DirectiveList<Number_T>::SWITCH_STATEMENT
-
corresponds to
wtk::SwitchStatement<Number_T>
andthis→switchStatement(n)
.
wtk::DirectiveList<Number_T>::Type type(size_t n)
Returns the type of the nth element in this list.
n
must be in the range n >= 0 && n < this→size()
otherwise undefined behavior occurs.
virtual Type type(size_t n) = 0;
wtk::BinaryGate* binaryGate(size_t n)
Returns the nth element as a wtk::BinaryGate
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::BINARY_GATE
otherwise undefined behavior occurs.
virtual wtk::BinaryGate* binaryGate(size_t n) = 0;
wtk::UnaryGate* unaryGate(size_t n)
Returns the nth element as a wtk::UnaryGate
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::UNARY_GATE
otherwise undefined behavior occurs.
virtual wtk::UnaryGate* unaryGate(size_t n) = 0;
wtk::BinaryConstGate<Number_T>* binaryConstGate(size_t n)
Returns the nth element as a wtk::BinaryConstGate<Number_T>
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::BINARY_CONST_GATE
otherwise undefined behavior occurs.
virtual wtk::BinaryConstGate<Number_T>* binaryConstGate(size_t n) = 0;
wtk::Input* input(size_t n)
Returns the nth element as a wtk::Input
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::INPUT
otherwise undefined behavior occurs.
virtual wtk::Input* input(size_t n) = 0;
wtk::Assign<Number_T>* assign(size_t n)
Returns the nth element as a wtk::Assign<Number_T>
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::ASSIGN
otherwise undefined behavior occurs.
virtual wtk::Assign<Number_T>* assign(size_t n) = 0;
wtk::Terminal* assertZero(size_t n)
Returns the nth element as a wtk::Terminal
type for the purpose of an @assert_zero
gate.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::ASSERT_ZERO
otherwise undefined behavior occurs.
virtual wtk::Terminal* assertZero(size_t n) = 0;
wtk::Terminal* deleteSingle(size_t n)
Returns the nth element as a wtk::Terminal
type for the purpose of a @delete
gate.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::DELETE_SINGLE
otherwise undefined behavior occurs.
virtual wtk::Terminal* deleteSingle(size_t n) = 0;
wtk::WireRange* deleteRange(size_t n)
Returns the nth element as a wtk::WireRange
type for the purpose of a @delete
gate.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::DELETE_RANGE
otherwise undefined behavior occurs.
virtual wtk::WireRange* deleteRange(size_t n) = 0;
wtk::FunctionInvoke* functionInvoke(size_t n)
Returns the nth element as a wtk::FunctionInvoke
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::FUNCTION_INVOKE
otherwise undefined behavior occurs.
virtual wtk::FunctionInvoke* functionInvoke(size_t n) = 0;
wtk::AnonFunction<Number_T>* anonFunction(size_t n)
Returns the nth element as a wtk::AnonFunction<Number_T>
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::ANON_FUNCTION
otherwise undefined behavior occurs.
virtual wtk::AnonFunction<Number_T>* anonFunction(size_t n) = 0;
wtk::ForLoop<Number_T>* forLoop(size_t n)
Returns the nth element as a wtk::ForLoop<Number_T>
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::FOR_LOOP
otherwise undefined behavior occurs.
virtual wtk::ForLoop<Number_T>* forLoop(size_t n) = 0;
wtk::SwitchStatement<Number_T>* switchStatement(size_t n)
Returns the nth element as a wtk::SwitchStatement<Number_T>
type.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::DirectiveList<Number_T>::SWITCH_STATEMENT
otherwise undefined behavior occurs.
virtual wtk::SwitchStatement<Number_T>* switchStatement(size_t n) = 0;
size_t lineNum()
Returns the line number at which the DirectiveList begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct BinaryGate
This represents a binary gate in the IR. Binary refers to the two-input wires of this gate, not numeric representation.
It has methods for left and right input wires as well as the output wire.
The specific gate type is the Calculation
enum.
enum Calculation
Indicates what calculation this gate performs.
wtk::BinaryGate::AND
-
@and
wtk::BinaryGate::XOR
-
@xor
wtk::BinaryGate::ADD
-
@add
wtk::BinaryGate::MUL
-
@mul
wtk::BinaryGate::Calculation calculation()
returns which calculation the binary gate performs.
virtual Calculation calculation() = 0;
wtk::index_t outputWire()
Returns the gate’s output wire.
virtual wtk::index_t outputWire() = 0;
wtk::index_t leftWire()
Returns the gate’s left input wire.
virtual wtk::index_t leftWire() = 0;
wtk::index_t rightWire()
Returns the gate’s right input wire.
virtual wtk::index_t rightWire() = 0;
size_t lineNum()
Returns the line number at which the binary gate begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct UnaryGate
This represents a unary gate in the IR. Unary refers to the single input wire of this gate, not numeric representation.
It has methods for the input wire as well as the output wire.
The specific gate type is the Calculation
enum.
enum Calculation
Indicates what calculation this gate performs.
wtk::UnaryGate::NOT
-
$0 ← @not($1)
wtk::UnaryGate::COPY
-
$0 ← $1
wtk::UnaryGate::Calculation calculation()
returns which calculation the unary gate performs.
virtual Calculation calculation() = 0;
wtk::index_t outputWire()
Returns the gate’s output wire.
virtual wtk::index_t outputWire() = 0;
wtk::index_t rightWire()
Returns the gate’s input wire.
virtual wtk::index_t inputWire() = 0;
size_t lineNum()
Returns the line number at which the unary gate begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct BinaryConstGate
This represents a binary constant gate in the IR. Binary refers to the input wire and input constant of this gate, not numeric representation.
It has methods for left input wire and right input constant as well as the output wire.
The specific gate type is the Calculation
enum.
enum Calculation
Indicates what calculation this gate performs.
wtk::BinaryConstGate<Number_T>::ADDC
-
@addc
wtk::BinaryConstGate<Number_T>::MULC
-
@mulc
wtk::BinaryConstGate<Number_T>::Calculation calculation()
returns which calculation the binary gate performs.
virtual Calculation calculation() = 0;
wtk::index_t outputWire()
Returns the gate’s output wire.
virtual wtk::index_t outputWire() = 0;
wtk::index_t leftWire()
Returns the gate’s left input wire.
virtual wtk::index_t leftWire() = 0;
wtk::index_t rightWire()
Returns the gate’s right input constant.
virtual Number_T rightValue() = 0;
size_t lineNum()
Returns the line number at which the binary constant gate begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct Input
Represents an input directive (either @instance
or @short_witness
).
the Stream
enumeration indicates from which stream to consume.
enum Stream
An enumeration of the IR stream resources.
wtk::Input::INSTANCE
-
@instance
wtk::Input::SHORT_WITNESS
-
@short_witness
wtk::Input::Stream stream()
Returns the stream from which this directive is to consume.
virtual Stream stream() = 0;
wtk::index_t outputWire()
Returns the stream consumption’s output wire.
virtual wtk::index_t outputWire() = 0;
size_t lineNum()
Returns the line number at which the input directive begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct Assign
A directive that assigns a constant value to an output wire.
wtk::index_t outputWire()
Returns the assign directive’s output wire.
virtual wtk::index_t outputWire() = 0;
Number_T constValue()
Returns the assign directive’s constant input value.
virtual Number_T constValue() = 0;
size_t lineNum()
Returns the line number at which the assign directive begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct Terminal
Represents a directive with just a single input wire.
Its name is derived from the fact that it has no output wires, thus "terminating" some sequence of gates.
It is used by the directives @assert_zero
and @delete
(for a single input wire), although they are distinguished by wtk::DirectiveList<Number_T>::Type
rather than an enumeration within struct Terminal
.
wtk::index_t wire()
Returns the terminal’s single input wire.
virtual wtk::index_t wire() = 0;
size_t lineNum()
Returns the line number at which the terminal begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct WireRange
Represents a range of wires in the IR.
It is used for the range form of the @delete
directive, as well as a component of the wtk::WireList
.
wtk::index_t first()
Returns the first wire in the range (consider it inclusive).
virtual wtk::index_t first() = 0;
wtk::index_t last()
Returns the last wire in the range (consider it inclusive).
virtual wtk::index_t last() = 0;
size_t lineNum()
Returns the line number at which the wire range begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct WireList
The IR uses wire lists for holding parameters and returns to function-gates.
These lists are "ranged" such that the elements of the list do not correspond to the length of the list.
Some elements are individual wires, whereas other elements are ranges of wires.
Element type is given by the Type
enumeration.
size_t size()
Indicates how many elements are in the list (does not necessarily correspond to wire count).
virtual size_t size() = 0;
enum Type
Indicates if an element a single or a range element.
-
wtk::WireList::SINGLE
-
wtk::WireList::RANGE
wtk::WireList::Type type(size_t n)
Returns the type of the nth element.
n
must be in the range n >= 0 && n < this→size()
or else undefined behavior occurs.
virtual Type type(size_t n) = 0;
wtk::index_t single(size_t n)
Returns the nth element in the list as a single element.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::WireList::SINGLE
or else undefined behavior occurs.
virtual wtk::index_t single(size_t n) = 0;
wtk::WireRange* range(size_t n)
Returns the nth element in the list as a range element.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::WireList::RANGE
or else undefined behavior occurs.
virtual wtk::WireRange* range(size_t n) = 0;
size_t lineNum()
Returns the line number at which the wire list begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct FunctionDeclare
This wtk::FunctionDeclare<Number_T>
is the definition of a named function-gate.
It pairs with wtk::FunctionInvoke
for invocation.
They should be matched to eachother by a name (as a char*
).
char const* name()
Returns the name of the function.
virtual char const* name() = 0;
wtk::index_t outputCount()
Returns the number of output wires this function gate expects.
virtual wtk::index_t outputCount() = 0;
wtk::index_t inputCount()
Returns the number of input wires this function gate expects.
virtual wtk::index_t inputCount() = 0;
wtk::index_t instanceCount()
Returns the number of instance values this function gate will consume.
virtual wtk::index_t instanceCount() = 0;
wtk::index_t shortWitnessCount()
Returns the number of short witness values this function gate will consume.
virtual wtk::index_t shortWitnessCount() = 0;
wtk::DirectiveList<Number_T>* body()
returns the body of the function gate.
virtual DirectiveList<Number_T>* body() = 0;
size_t lineNum()
Returns the line number at which the function declaration begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct FunctionInvoke
The wtk:FunctionInvoke
invokes a function gate, carrying a name which should match with the name of a wtk::FunctionDeclare<Number_T>
.
char const* name()
Returns the name of the function gate.
virtual char const* name() = 0;
wtk::WireList* outputList()
Returns a wtk::WireList
for the output wires of this invocation.
virtual wtk::WireList* outputList() = 0;
wtk::WireList* inputList()
Returns a wtk::WireList
for the input wires of this invocation.
virtual wtk::WireList* inputList() = 0;
size_t lineNum()
Returns the line number at which the function invocation begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct AnonFunction
The wtk::AnonFunction<Number_T>
is the simultaneous declaration and invocation of an anonymous function-gate.
It mirrors the structure of both wtk::FunctionDeclare<Number_T>
and wtk::FunctionInvoke
, although without certain attributes such as name()
.
wtk::WireList* outputList()
Returns a wtk::WireList
for the output wires of this anonymous function.
virtual wtk::WireList* outputList() = 0;
wtk::WireList* inputList()
Returns a wtk::WireList
for the input wires of this anonymous function.
virtual wtk::WireList* inputList() = 0;
wtk::index_t instanceCount()
Returns the number of instance values this anonymous function gate will consume.
virtual wtk::index_t instanceCount() = 0;
wtk::index_t shortWitnessCount()
Returns the number of short witness values this anonymous function gate will consume.
virtual wtk::index_t shortWitnessCount() = 0;
wtk::DirectiveList<Number_T>* body()
returns the body of the anonymous function gate.
virtual DirectiveList<Number_T>* body() = 0;
size_t lineNum()
Returns the line number at which the anonymous function declaration begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct IterExpr
The wtk::IterExpr
takes the place of wtk::index_t
in input and output lists of For Loop bodies.
Instead of representing an exact wire-index, these expressions evaluate to a wire-index, allowing the loop to traverse ranges of wires.
All iterator expressions are carried out over wtk::index_t
.
The expressions are a recursive datatype, with base cases for numeric literals and loop-iterators.
Recursive cases exist for addition, subtraction, multiplication, and division by a constant.
The expression type is given by enum Type
.
enum Type
This enumerates the various types of expressions.
wtk::IterExpr::LITERAL
-
A numeric literal
wtk::IterExpr::ITERATOR
-
A reference to a loop iterator
wtk::IterExpr::ADD
-
Addition expression of two sub-expressions
wtk::IterExpr::SUB
-
Subtraction expression of two sub-expressions
wtk::IterExpr::MUL
-
Multiplition expression of two sub-expressions
wtk::IterExpr::DIV
-
Division expression of one sub-expression and a constant divisor
wtk:IterExpr::Type type()
Returns the type of this expression.
virtual Type type() = 0;
wtk::index_t literal()
Returns this literal expression or the right-hand-side of this division expression as a wtk::index_t
.
If the following precondition isn’t met, then undefined behavior occurs (see also this→type()
and enum Type
).
this->type() == wtk::IterExpr::LITERAL
|| this->type() == wtk::IterExpr::DIV
virtual wtk::index_t literal() = 0;
char const* name()
Returns this loop-iterator expression as a char*
.
If the following precondition isn’t met, then undefined behavior occurs (see also this→type()
and enum Type
).
this->type() == wtk::IterExpr::ITERATOR
virtual wtk::index_t literal() = 0;
wtk::IterExpr* lhs()
Returns the left-hand-side of this expression.
If the following precondition isn’t met, then undefined behavior occurs (see also this→type()
and enum Type
).
this->type() == wtk::IterExpr::ADD
|| this->type() == wtk::IterExpr::SUB
|| this->type() == wtk::IterExpr::MUL
|| this->type() == wtk::IterExpr::DIV
virtual IterExpr* lhs() = 0;
wtk::IterExpr* lhs()
Returns the right-hand-side of this expression.
If the following precondition isn’t met, then undefined behavior occurs (see also this→type()
and enum Type
).
this->type() == wtk::IterExpr::ADD
|| this->type() == wtk::IterExpr::SUB
|| this->type() == wtk::IterExpr::MUL
virtual IterExpr* lhs() = 0;
size_t lineNum()
Returns the line number at which the iterator expression begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct IterExprWireRange
Represents a range of iterator expressions, mirroring the form of wtk::WireRange
.
It is used as a component of the wtk::IterExprWireList
(which itself mirrors wtk::WireList
).
wtk::IterExpr* first()
Returns a wtk::IterExpr
for the first wire in the range (consider it inclusive).
virtual wtk::IterExpr* first() = 0;
wtk::IterExpr* last()
Returns a wtk::IterExpr
for the last wire in the range (consider it inclusive).
virtual wtk::IterExpr* last() = 0;
size_t lineNum()
Returns the line number at which the wire range begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct IterExprWireList
Represents a list of iterator expressions, mirroring the form of wtk::WireList
.
For for-loop bodies, the input and output wire lists use these to enable traversal based on the loop iterator.
These lists are "ranged" such that the elements of the list do not correspond to the length of the list.
Some elements are individual expressions, whereas other elements are ranges of expressions.
Element type is given by the Type
enumeration.
size_t size()
Indicates how many elements are in the list (does not necessarily correspond to wire count).
virtual size_t size() = 0;
enum Type
Indicates if an element a single or a range element.
-
wtk::IterExprWireList::SINGLE
-
wtk::IterExprWireList::RANGE
wtk::IterExprWireList::Type type(size_t n)
Returns the type of the nth element.
n
must be in the range n >= 0 && n < this→size()
or else undefined behavior occurs.
virtual Type type(size_t n) = 0;
wtk::ItereExpr* single(size_t n)
Returns the nth element in the list as a single element.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::IterExprWireList::SINGLE
or else undefined behavior occurs.
virtual wtk::IterExpr* single(size_t n) = 0;
wtk::IterExprWireRange* range(size_t n)
Returns the nth element in the list as a range element.
n
must be in the range n >= 0 && n < this→size()
and n
must have the type this→type(n) == wtk::IterExprWireList::RANGE
or else undefined behavior occurs.
virtual wtk::WireRange* range(size_t n) = 0;
size_t lineNum()
Returns the line number at which the wire list begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct IterExprFunctionInvoke
The wtk:IterExprFunctionInvoke
invokes a function gate as the body of a for-loop, carrying a name which should match with the name of a wtk::FunctionDeclare<Number_T>
.
It mirrors wtk::FunctionInvoke
replacing input and output lists with wtk::IterExprWireList
.
char const* name()
Returns the name of the function gate.
virtual char const* name() = 0;
wtk::IterExprWireList* outputList()
Returns a wtk::IterExprWireList
for the output wires of this invocation.
virtual wtk::IterExprWireList* outputList() = 0;
wtk::IterExprWireList* inputList()
Returns a wtk::IterExprWireList
for the input wires of this invocation.
virtual wtk::IterExprWireList* inputList() = 0;
size_t lineNum()
Returns the line number at which the function invocation begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct IterExprAnonFunction
The wtk::IterExprAnonFunction<Number_T>
is the simultaneous declaration and invocation of an anonymous function-gate as the body of a for-loop.
It mirrors the structure of both wtk::FunctionDeclare<Number_T>
and wtk::FunctionInvoke
, although without certain attributes such as name()
, and with the input and output lists replaced by wtk::IterExprWireList
wtk::IterExprWireList* outputList()
Returns a wtk::IterExprWireList
for the output wires of this anonymous function.
virtual wtk::IterExprWireList* outputList() = 0;
wtk::IterExprWireList* inputList()
Returns a wtk::IterExprWireList
for the input wires of this anonymous function.
virtual wtk::IterExprWireList* inputList() = 0;
wtk::index_t instanceCount()
Returns the number of instance values this anonymous function gate will consume.
virtual wtk::index_t instanceCount() = 0;
wtk::index_t shortWitnessCount()
Returns the number of short witness values this anonymous function gate will consume.
virtual wtk::index_t shortWitnessCount() = 0;
wtk::DirectiveList<Number_T>* body()
returns the body of the anonymous function gate.
virtual DirectiveList<Number_T>* body() = 0;
size_t lineNum()
Returns the line number at which the anonymous function declaration begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct ForLoop
This represents a for-loop directive in the IR.
wtk::WireList* outputList()
Returns the output list of the entire for-loop (not to be confused with the output list of one iteration of the for-loop) as a wtk::WireList
.
virtual WireList* outputList() = 0;
char const* iterName()
Returns the name of the loop’s iterator.
virtual char const* iterName() = 0;
wtk::index_t first()
Returns the first iteration of the loop (inclusive).
virtual wtk::index_t first() = 0;
wtk::index_t last()
Returns the last iteration of the loop (inclusive).
virtual wtk::index_t last() = 0;
enum BodyType
An enumeration to indicate whether the loop’s body is named or anonymous.
wtk::ForLoop<Number_T>::INVOKE
-
Named body (use
this→invokeBody()
andwtk::IterExprFunctionInvoke
). wtk::ForLoop<Number_T>::ANONYMOUS
-
Anonymous body (use
this→anonymousBody()
andwtk::IterExprAnonFunction<Number_T>
).
BodyType bodyType()
Returns the body type of this for-loop.
virtual BodyType bodyType() = 0;
wtk::IterExprFunctionInvoke* invokeBody()
Returns the body of this for-loop as an wtk::IterExprFunctionInvoke
.
If the loop’s body type is not this→bodyType() == wtk::ForLoop<Number_T>::INVOKE
, then undefined behavior occurs.
virtual IterExprFunctionInvoke* invokeBody() = 0;
wtk::IterExprAnonFunction<Number_T>* anonymousBody()
Returns the body of this for-loop as an wtk::IterExprAnonFunction<Number_T>
.
If the loop’s body type is not this→bodyType() == wtk::ForLoop<Number_T>::ANONYMOUS
, then undefined behavior occurs.
virtual IterExprAnonFunction<Number_T>* anonymousBody() = 0;
size_t lineNum()
Returns the line number at which the for-loop begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
struct CaseFunctionInvoke
The wtk:CaseFunctionInvoke
invokes a function gate as the body of a switch-case.
It mirrors the form of wtk::FunctionInvoke
, however, as the body of a switch-case, it is missing outputList()
.
char const* name()
Returns the name of the function gate.
virtual char const* name() = 0;
wtk::WireList* inputList()
Returns a wtk::WireList
for the input wires of this invocation.
virtual wtk::WireList* inputList() = 0;
size_t lineNum()
Returns the line number at which the function invocation begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct CaseAnonFunction
The wtk:CaseAnonFunction
is the simultaneous declaration and invocation of an anonymous function-gate as the body of a switch-case.
It mirrors the structure of both wtk::FunctionDeclare<Number_T>
and wtk::FunctionInvoke
, although without certain attributes such as name()
or, as the body of a switch-case, outputList()
.
wtk::WireList* inputList()
Returns a wtk::WireList
for the input wires of this anonymous function.
virtual wtk::WireList* inputList() = 0;
wtk::index_t instanceCount()
Returns the number of instance values this anonymous function gate will consume.
virtual wtk::index_t instanceCount() = 0;
wtk::index_t shortWitnessCount()
Returns the number of short witness values this anonymous function gate will consume.
virtual wtk::index_t shortWitnessCount() = 0;
wtk::DirectiveList<Number_T>* body()
returns the body of the anonymous function gate.
virtual DirectiveList<Number_T>* body() = 0;
size_t lineNum()
Returns the line number at which the anonymous function declaration begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct CaseBlock
This represents a case within a switch-statement.
Number_T match()
Returns the field-literal which is matched against the switch-statement’s condition()
wire to indicate if this case is active.
virtual Number_T match() = 0;
enum BodyType
An enumeration to indicate whether the case’s body is named or anonymous.
wtk::CaseBlock<Number_T>::INVOKE
-
Named body (use
this→invokeBody()
andwtk::CaseFunctionInvoke
). wtk::CaseBlock<Number_T>::ANONYMOUS
-
Anonymous body (use
this→anonymousBody()
andwtk::CaseAnonFunction<Number_T>
).
BodyType bodyType()
Returns the body type of this case block.
virtual BodyType bodyType() = 0;
wtk::CaseFunctionInvoke* invokeBody()
Returns the body of this case-block as a wtk::CaseFunctionInvoke
.
If the case’s body type is not this→bodyType() == wtk::CaseBlock<Number_T>::INVOKE
, then undefined behavior occurs.
virtual CaseFunctionInvoke* invokeBody() = 0;
wtk::CaseAnonFunction<Number_T>* anonymousBody()
Returns the body of this case-block as a wtk::CaseAnonFunction<Number_T>
.
If the case’s body type is not this→bodyType() == wtk::CaseBlock<Number_T>::ANONYMOUS
, then undefined behavior occurs.
virtual CaseAnonFunction<Number_T>* anonymousBody() = 0;
size_t lineNum()
Returns the line number at which the case-block begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
template<typename Number_T> struct SwitchStatement
This represents a switch-statement directive in the IR.
wtk::WireList* outputList()
Returns the switch-statement’s output list as a wtk::WireList
.
virtual wtk::WireList* outputList() = 0;
wtk::index_t condition()
Returns the condition (or "selector") wire of this switch-statement.
virtual wtk::index_t condition() = 0;
size_t size()
Returns the number of cases in the switch-statement.
virtual size_t size() = 0;
wtk::CaseBlock<Number_T>* caseBlock(size_t n)
Returns the n
th wtk::CaseBlock<Number_T>
in this switch-statement.
If n
is outside of the range n >= 0 && n < this→size()
, then undefined behavior occurs.
virtual CaseBlock<Number_T>* caseBlock(size_t n) = 0;
size_t lineNum()
Returns the line number at which the switch-statement begins.
It may be unsupported by the parser (for example line numbering is nonsensical in a binary format), in which case 0
is always returned.
virtual size_t lineNum();
IRParameters API
Include this API as
#include <wtk/Parser.h>
Members of this API live in the following namespace
namespace wtk { ...
This header defines some of the front-matter parameters of the IR. Generally these parameters will be provided by the Parser API.
enum class Resource
An enumeration of the various resources an IR file may be.
-
wtk::Resource::relation
-
wtk::Resource::instance
-
wtk::Resource::shortWitness
-
wtk::Resource::invalid
(a default or error condition)
struct GateSet
This structure defines the allowable gates of a relation.
It is split along two varieties of gates arithmetic
or boolean
, each of which may be taken as a "cannonical" set (e.g. all gates enabled) or a subset.
enum { … } gateSet
This indicates which variety of gates are used. and has the following values
-
wtk::GateSet::arithmetic
-
wtk::GateSet::boolean
-
wtk::GateSet::invalid
(a default or error conditions)
enum {
arithmetic,
boolean,
invalid
} gateSet = invalid;
SubSet definitions (union
)
This unnamed union
attribute defines sub sets of either of the gate set varieties.
As a union, only attributes of the active/correct gate set may be used.
Arithmetic Subsets (struct
)
The following flags indicate if an arithmetic gate is allowable.
-
bool enableAdd
indicates if@add
is allowed. -
bool enableAddC
indicates if@addc
is allowed. -
bool enableMul
indicates if@mul
is allowed. -
bool enableMulC
indicates if@mulc
is allowed.
Boolean Subsets (struct
)
The following flags indicate if a boolean gate is allowable.
-
bool enableXor
indicates if@xor
is allowed. -
bool enableAnd
indicates if@and
is allowed. -
bool enableNot
indicates if@not
is allowed.
bool cannonical() const
A "cannonical" gate set is one which has all gates of the variety enabled. This function indicates if this GateSet is cannonical.
bool cannonical() const;
struct FeatureToggles
The IR allows structural features to be disabled. At the extremities "IR Simple" indicates that no features are enabled, while "IR Complete" indicates that all features are enabled.
bool functionToggle
This flag indicates that named functions are enabled, and that anonymous functions are allowed (except for the bodies of other enabled features).
bool functionToggle = false;
bool forLoopToggle
This flag indicates that for loops are enabled.
bool forLoopToggle = false;
bool switchCaseToggle
This flag indicates that switch statements are enabled.
bool switchCaseToggle = false;
bool simple() const
This method checks if these feature toggles form IR-Simple (all features disabled).
bool simple() const;
bool complete() const
This method checks if these feature toggles form IR-Complete (all features enabled).
bool complete() const;
Arithmetic Stream Handler API
Include this API as
#include <wtk/ArithmeticStreamHandler.h>
Members of this API live in the following namespace
namespace wtk { ...
This API is parameterized on a Number_T
template.
Number_T
should be integer-like enough to be parsed from a string.
This API is abstract and implemented by parsers for various IR formats.
template<typename Number_T> class ArithmeticStreamHandler
The wtk::ArithmeticStreamHandler<Number_T>
is a virtual class for the user to implement with callbacks for the Parser API to use when stream parsing.
virtual void setLineNumber(size_t const line)
If the Parser supports line-numbering, it will use this callback to set the current line number before a gate’s callback is invoked.
virtual void setLineNum(size_t const line);
virtual void handleInstance(wtk::index_t const idx)
Called when an @instance
directive is reached.
virtual void handleInstance(wtk::index_t const idx);
virtual void handleShortWitness(wtk::index_t const idx)
Called when an @short_witness
directive is reached.
virtual void handleShortWitness(wtk::index_t const idx);
virtual void handleAdd(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right)
Called when an @add
directive is reached.
virtual void handleAdd(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right);
virtual void handleMul(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right)
Called when an @mul
directive is reached.
virtual void handleMul(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right);
virtual void handleAddC(wtk::index_t const out, wtk::index_t const left, Number_T const right)
Called when an @addc
directive is reached.
virtual void handleAddC(wtk::index_t const out, wtk::index_t const left, Number_T const right);
virtual void handleMulC(wtk::index_t const out, wtk::index_t const left, Number_T const right)
Called when an @mulc
directive is reached.
virtual void handleMulC(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right);
virtual void handleAssign(wtk::index_t const out, Number_T const val)
Called when a constant assignment directive is reached.
virtual void handleAssign(wtk::index_t const out, Number_T const val);
virtual void handleCopy(wtk::index_t const out, wtk::index_t const in)
Called when a copy directive is reached.
virtual void handleCopy(wtk::index_t const out, wtk::index_t const in);
virtual void handleAssertZero(wtk::index_t const in)
Called when an @assert_zero
directive is reached.
virtual void handleAssertZero(wtk::index_t const in);
virtual void handleDeleteSingle(wtk::index_t const in)
Called when a @delete
directive with a single argument is reached.
virtual void handleDeleteSingle(wtk::index_t const in);
virtual void handleDeleteRange(wtk::index_t const first, wtk::index_t const last)
Called when a @delete
directive with a range argument is reached.
The range is inclusive on both ends.
virtual void handleDeleteRange(wtk::index_t const first, wtk::index_t const last);
virtual void handleEnd()
Called after the last directive is reached.
virtual void handleEnd();
Boolean Stream Handler API
Include this API as
#include <wtk/BooleanStreamHandler.h>
Members of this API live in the following namespace
namespace wtk { ...
class BooleanStreamHandler
The wtk::BooleanStreamHandler
is a virtual class for the user to implement with callbacks for the Parser API to use when stream parsing.
virtual void setLineNumber(size_t const line)
If the Parser supports line-numbering, it will use this callback to set the current line number before a gate’s callback is invoked.
virtual void setLineNum(size_t const line);
virtual void handleInstance(wtk::index_t const idx)
Called when an @instance
directive is reached.
virtual void handleInstance(wtk::index_t const idx);
virtual void handleShortWitness(wtk::index_t const idx)
Called when an @short_witness
directive is reached.
virtual void handleShortWitness(wtk::index_t const idx);
virtual void handleXor(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right)
Called when an @xor
directive is reached.
virtual void handleXor(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right);
virtual void handleAnd(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right)
Called when an @and
directive is reached.
virtual void handleAnd(wtk::index_t const out, wtk::index_t const left, wtk::index_t const right);
virtual void handleNot(wtk::index_t const out, wtk::index_t const in)
Called when a @not
directive is reached.
virtual void handleNot(wtk::index_t const out, wtk::index_t const in);
virtual void handleAssign(wtk::index_t const out, uint8_t const val)
Called when a constant assignment directive is reached.
virtual void handleAssign(wtk::index_t const out, uint8_t const val);
virtual void handleCopy(wtk::index_t const out, wtk::index_t const in)
Called when a copy directive is reached.
virtual void handleCopy(wtk::index_t const out, wtk::index_t const in);
virtual void handleAssertZero(wtk::index_t const in)
Called when an @assert_zero
directive is reached.
virtual void handleAssertZero(wtk::index_t const in);
virtual void handleDeleteSingle(wtk::index_t const in)
Called when a @delete
directive with a single argument is reached.
virtual void handleDeleteSingle(wtk::index_t const in);
virtual void handleDeleteRange(wtk::index_t const first, wtk::index_t const last)
Called when a @delete
directive with a range argument is reached.
The range is inclusive on both ends.
virtual void handleDeleteRange(wtk::index_t const first, wtk::index_t const last);
virtual void handleEnd()
Called after the last directive is reached.
virtual void handleEnd();
ANTLR Parser API
Include this API as
#include <wtk/antlr/Parser.h>
Members of this API live in the following namespaces
namespace wtk { namespace antlr { ...
The Parser API is parameterized on a Number_T
template.
Number_T
should be integer-like enough to be parsed from a string.
This API is abstract and implemented by parsers for various IR formats.
template<typename Number_T> struct Parser
The wtk::antlr::Parser<Number_T>
implements the wtk::Parser<Number_T>
.
It parses text and produces line numbering.
However, the ANTLR runtime has been known to use large quantities of memory.
Additionally, it cannot truly stream parse either IR-Simple or input streams (instance/short witness).
While, it does implement the parseStream(…)
API, it must read the entire relation ahead of time.
Parser(std::string& f_name)
Constructor via opening the file named by f_name
.
Undefined behavior occurs if the file does not exist.
Parser(std::string& f_name);
IRRegular Parser API
Include this API as
#include <wtk/irregular/Parser.h>
Members of this API live in the following namespaces
namespace wtk { namespace irregular { ...
The Parser API is parameterized on a Number_T
template.
Number_T
should be integer-like enough to be parsed from a string.
This API is abstract and implemented by parsers for various IR formats.
template<typename Number_T> struct Parser
The wtk::irregular::Parser<Number_T>
implements the wtk::Parser<Number_T>
.
It parses text but does not produce line numbering.
It is designed to minimize memory usage while maximizing parser speed.
It is capable of stream-parsing, although it does require some buffering.
Parser(std::string& f_name)
Constructor via opening the file named by f_name
.
Undefined behavior occurs if the file does not exist.
Parser(std::string& f_name);
Parser(FILE* f)
Constructor which uses a FILE*
.
Undefined behavior occurs if f
is nullptr
or f
is not opened to an existing file (e.g. not a directory).
Parser(FILE* f);
FlatBuffer Parser API
Include this API as
#include <wtk/flatbuffer/Parser.h>
Members of this API live in the following namespaces
namespace wtk { namespace flatbuffer { ...
The Parser API is parameterized on a Number_T
template.
Number_T
should be integer-like enough to be parsed from a string.
This API is abstract and implemented by parsers for various IR formats.
template<typename Number_T> struct Parser
The wtk::flatbuffer::Parser<Number_T>
implements the wtk::Parser<Number_T>
.
It parses binary and consequently line-numbering is nonsensical.
It cannot truly stream parse either IR-Simple or input streams (instance/short witness) because FlatBuffers is designed around random access of a buffer.
While, it does implement the parseStream(…)
API, it must read the entire relation ahead of time.
It could (but does not) stream segments of a very large resource (>2GB
) because these resources are split into multiple FlatBuffers.
Parser(std::string& f_name)
Constructor via opening the file named by f_name
.
Undefined behavior occurs if the file does not exist.
Parser(std::string& f_name);
BOLT Builder API
Include this API as
#include <wtk/bolt/Builder.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> struct Builder
The wtk::bolt::Builder<Wire_T, Number_T>
conducts the first stage of BOLT invocation — building an augmented/annotated syntax tree.
It does its best to do this in O(s) time where s is the size of the syntax-tree.
This is easy to do for most of the IR, however, certain classes of for-loops require work proportional to either number of iterations ("soft" unrolling), or proportional to iterations and size of the sub-syntax tree ("hard" unrolling).
Builder(Number_T const c)
The only constructor for a wtk::bolt::Builder<Wire_T, Number_T>
requires just a single Number_T
parameter for the circuit’s characteristic (prime modulus).
Builder(Number_T const c);
Bolt<Wire_T, Number_T>* build(wtk::IRTree<Number_T>* const tree)
This method builds a wtk::bolt::Bolt<Wire_T, Number_T>
tree from the wtk::IRTree<Number_T>
parameter.
It returns nullptr
when the tree
violates IR well-formedness semantics.
In the case that tree == nullptr
then undefined behavior occurs.
Multiple invocations of build(…)
on the same object will result in undefined behavior.
wtk::bolt::Bolt<Wire_T, Number_T>* build(wtk::IRTree<Number_T>* const tree);
BOLT Evaluator API
Include this API as
#include <wtk/bolt/Evaluator.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.
class Evaluator
The wtk::bolt::Evaluator<Wire_T, Number_T>
class encapsulates the second phase of BOLT invocation.
It traverses the BOLT relation and "executes" each gate.
Thus, it is by definition O(n) where n is the total number of gates.
Evaluator(wtk::bolt::Backend<Wire_T, Number_T>* const b)
The single constructor requires just a pointer to the wtk::bolt::Backend<Wire_T, Number_T>
implementation.
Evaluator(wtk::bolt::Backend<Wire_T, Number_T>* const b);
bool evaluate(wtk::bolt::Bolt<Wire_T, Number_T>* const bolt, wtk::InputStream<Number_T>* const instance, wtk::InputStream<Number_T>* const witness)
Evaluates a wtk::bolt::Bolt<Wire_T, Number_T>
relation and returns true
on success.
The bolt relation may be constructed using the BOLT Builder.
It must also have two wtk::InputStream<Number_T>
s.
The first, instance
, must not be nullptr
, while the second, witness
, may be nullptr
for the cases where the witness is inaccessible to a verifier.
bool evaluate(Bolt<Wire_T, Number_T>* const bolt,
wtk::InputStream<Number_T>* const instance,
wtk::InputStream<Number_T>* const witness);
BOLT Backend API
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);
PLASMASnooze
Include this API as
#include <wtk/bolt/PLASMASnooze.h>
Members of this API live in the following namespace
namespace wtk { namespace bolt { ...
The PLASMASnooze Interpreter 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. PLASMASnooze requires the
Wire_T
to be default constructible/destructable as well as mutable/overwritable.
The PLASMASnooze API is an IR interpreter sharing the Backend API. It runs in a single pass and ignores certain deviations from the IR Specification, where they are deemed harmless. The name PLASMASnooze is derived from FIREALARM. PLASMA is an improvement upon FIRE (in the sense of performance and pluggability of ZK Backends), while Snooze is the opposite of ALARM (in the sense of ignoring harmless deviations from the IR Spec). The acryonym expands to Practical Local Acceleration for Single-pass with Malleable Assumtions Snooze.
template<typename Wire_T, typename Number_T> class PLASMASnooze
The wtk::bolt::PLASMASnooze<Wire_T, Number_T>
is a main driver object.
It walks an IR syntax tree and, at each gate, invokes a corresponding gate handler from the Backend API.
PLASMASnooze(wtk::bolt::Backend<Wire_T, Number_T>* const b)
The constructor requires just a pointer to a wtk::bolt::Backend<Wire_T, Number_T>
.
PLASMASnooze(wtk::bolt::Backend<Wire_T, Number_T>* const b);
wtk::bolt::PLASMASnoozeStatus evaluate(wtk::IRTree<Number_T>* const rel_tree, wtk::InputStream<Number_T>* const ins_stream, wtk::InputStream<Number_T>* const wit_stream)
Evaluates an wtk::IRTree<Number_T>
relation and returns an enum wtk::bolt::PLASMASnoozeStatus
.
It must also have two wtk::InputStream<Number_T>
s.
The first, instance
, must not be nullptr
, while the second, witness
, may be nullptr
for the cases where the witness is inaccessible to a verifier.
PLASMASnoozeStatus evaluate(
wtk::IRTree<Number_T>* const relation,
wtk::InputStream<Number_T>* const instance,
wtk::InputStream<Number_T>* const witness);
enum class PLASMASnoozeStatus
This is an enumeration of statuses that PLASMASnooze can return.
wtk::bolt::PLASMASnooze::bad_relation
-
The relation is poorly-formed.
wtk::bolt::PLASMASnooze::bad_stream
-
The instance or witness is poorly-formed.
wtk::bolt::PLASMASnooze::well_formed
-
The relation, witness, and instance are all well-formed.
Arithmetic PLASMASnooze Streaming API
Include this API as
#include <wtk/bolt/ArithmeticPLASMASnoozeHandler.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. PLASMASnooze requires it to be default constructible/destructible, and mutable/overwritable.
template<template Wire_T, typename Number_T> class ArithmeticPLASMASnoozeHandler
The wtk::bolt::ArithmeticPLASMASnoozeHandler<Wire_T, Number_T>
implements the wtk::ArithmeticStreamHandler<Number_T>
abstract class with callbacks for interpreting wire-numbers and making calls to the wtk::bolt::Backend<Wire_T, Number_T>
callback API.
ArithmeticPLASMASnoozeHandler(wtk::bolt::Backend<Wire_T, Number_T>* const b, wtk::InputStream<Number_T>* const ins, wtk::InputStream<Number_T>* const wit)
The constructor requires a wtk::bolt::Backend<Wire_T, Number_T>*
for ZK callbacks, as well as two wtk::InputStream<Number_T>*
parameters for the instance and short witness.
The second input stream (short witness) may be nullptr
, as may be necessary for verifier implementations.
ArithmeticPLASMASnoozeHandler(
wtk::bolt::Backend<Wire_T, Number_T>* const b,
wtk::InputStream<Number_T>* const ins,
wtk::InputStream<Number_T>* const wit);
wtk::Bolt::PLASMASnoozeStatus check()
After stream parsing/processing the check()
method retrieves a status (wtk::PLASMASnoozeStatus
).
wtk::bolt::PLASMASnoozeStatus check();
Boolean PLASMASnooze Streaming API
Include this API as
#include <wtk/bolt/BooleanPLASMASnoozeHandler.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. For Boolean relations, this is fixed to
uint8_t
. Wire_T
-
This template is an opaque structure supplied by the Backend for encapsulating data related to a wire. PLASMASnooze requires it to be default constructible/destructible, and mutable/overwritable.
template<template Wire_T, typename Number_T> class BooleanPLASMASnoozeHandler
The wtk::bolt::BooleanPLASMASnoozeHandler<Wire_T, Number_T>
implements the wtk::BooleanStreamHandler
abstract class with callbacks for interpreting wire-numbers and making calls to the wtk::bolt::Backend<Wire_T, uint8_t>
callback API.
BooleanPLASMASnoozeHandler(wtk::bolt::Backend<Wire_T, uint8_t>* const b, wtk::InputStream<uint8_t>* const ins, wtk::InputStream<uint8_t>* const wit)
The constructor requires a wtk::bolt::Backend<Wire_T, uint8_t>*
for ZK callbacks, as well as two wtk::InputStream<uint8_t>*
parameters for the instance and short witness.
The second input stream (short witness) may be nullptr
, as may be necessary for verifier implementations.
BooleanPLASMASnoozeHandler(
wtk::bolt::Backend<Wire_T, uint8_t>* const b,
wtk::InputStream<uint8_t>* const ins,
wtk::InputStream<uint8_t>* const wit);
wtk::Bolt::PLASMASnoozeStatus check()
After stream parsing/processing the check()
method retrieves a status (wtk::PLASMASnoozeStatus
).
wtk::bolt::PLASMASnoozeStatus check();