Compiler APIs
This guide provides a comprehensive overview of the core APIs offered by the compiler.
Root Package
To integrate the compiler into your Go project, include the following import statement in your code:
import "github.com/PolyhedraZK/ExpanderCompilerCollection/ecgo"
Compile Function
The ecgo.Compile
function serves as the primary interface to the compiler. It accepts a frontend.Circuit
as input and returns a CompileResult
.
CompileResult Structure
The ecgo.CompileResult
structure encapsulates the compilation process outputs, including both the layered circuit and the intermediate representation (IR).
The CompileResult
offers three methods to access the data:
GetCircuitIr()
: Retrieves the IR.GetLayeredCircuit()
: Fetches the layered circuit.GetInputSolver()
: Obtains the compiled witness solver of the circuit.
Builder API
The ecgo.API
serves as the interface for the builder API. It extends the frontend.API
interface from gnark, providing additional features such as support for sub-circuits and utility functions.
Sub-Circuit API
Sub-circuit functionality is enabled through the following methods:
type SubCircuitSimpleFunc func(api frontend.API, input []frontend.Variable) []frontend.Variable
type SubCircuitFunc interface{}
type SubCircuitAPI interface {
MemorizedSimpleCall(SubCircuitSimpleFunc, []frontend.Variable) []frontend.Variable
MemorizedCall(SubCircuitFunc, ...interface{}) interface{}
}
SubCircuitFunc
is designed to accommodate any function that takes simple types and frontend.Variable
as inputs and returns frontend.Variable
as output.
For example, a function with the signature func(frontend.API, int, uint8, [][]frontend.Variable, string, ...[]frontend.Variable) [][][]frontend.Variable
would be considered a valid SubCircuitFunc
.
A key requirement for SubCircuitFunc
is determinism: for any given set of identical simple-type inputs, the output must always be the same. This is crucial because the compiler memorizes the wiring of sub-circuit calls and reuses these memorized results for identical inputs, enhancing efficiency.
Additional APIs
type API interface {
Output(frontend.Variable)
GetRandomValue() frontend.Variable
CustomGate(gateType uint64, inputs ...frontend.Variable) frontend.Variable
}
Output
: Appends a variable to the circuit's output. Typically used to designate certain variables as public outputs of the circuit.GetRandomValue
: Retrieves a random value directly, offering a more efficient approach than generating pseudo-random numbers using a hash function. This direct access to random numbers is facilitated by the Libra proving process.CustomGate
: Similar to Gnark'sNewHint
, this method essentially calls a hint function to compute a result. In the resulting layered circuit, it will be compiled into a custom gate of the specified gate type. UnlikeNewHint
, it requires pre-registering the hint function and other parameters. For specific details, refer to this example.
Several other APIs exist for compatibility with the old pure Golang Expander Compiler, but they are currently no-op.
Builder Extensions
Memorized Function Wrappers
func MemorizedSimpleFunc(f SubCircuitSimpleFunc) SubCircuitSimpleFunc
func MemorizedVoidFunc(f SubCircuitFunc) func(frontend.API, ...interface{})
func Memorized0DFunc(f SubCircuitFunc) func(frontend.API, ...interface{}) frontend.Variable
func Memorized1DFunc(f SubCircuitFunc) func(frontend.API, ...interface{}) []frontend.Variable
func Memorized2DFunc(f SubCircuitFunc) func(frontend.API, ...interface{}) [][]frontend.Variable
func Memorized3DFunc(f SubCircuitFunc) func(frontend.API, ...interface{}) [][][]frontend.Variable
These function wrappers serve as syntactic conveniences for MemorizedCall
, streamlining its usage. For instance, the following invocations are functionally equivalent:
output := api.MemorizedSimpleCall(f, input)
memorizedF := builder.MemorizedSimpleFunc(f)
output := memorizedF(api, input)