
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
@guildofweavers/air-assembly
Advanced tools
A low-level language for encoding Algebraic Intermediate Representation of computations
This library contains specifications and JavaScript runtime for AirAssembly - a language for encoding Algebraic Intermediate Representation (AIR) of computations. AIR is a representation used in zk-STARKs to construct succinct proofs of computational integrity.
AirAssembly is a low-level language and is intended to be a compilation target for other, higher-level, languages (e.g. AirScript). It uses a simple s-expression-based syntax to specify:
Full specifications for AirAssembly can be found here.
This library is not intended for standalone use, but is rather meant to be a component used in STARK provers/verifiers (e.g. genSTARK). Nevertheless, you can install it separately like so:
$ npm install @guildofweavers/air-assembly --save
Once installed, you can use the library to compile AirAssembly source code into AirModules, and then use these modules to generate execution trace tables and constraint evaluation tables for computations.
The code below illustrates how to do this on the example of a MiMC computation. Other examples can be found here.
import { compile, instantiate } from '@guildofweavers/air-assembly';
const source = `
(module
(field prime 4194304001)
(const $alpha scalar 3)
(function $mimcRound
(result vector 1)
(param $state vector 1) (param $roundKey scalar)
(add
(exp (load.param $state) (load.const $alpha))
(load.param $roundKey)))
(export mimc
(registers 1) (constraints 1) (steps 32)
(static
(cycle (prng sha256 0x4d694d43 32)))
(init
(param $seed vector 1)
(load.param $seed))
(transition
(call $mimcRound (load.trace 0) (get (load.static 0) 0)))
(evaluation
(sub
(load.trace 1)
(call $mimcRound (load.trace 0) (get (load.static 0) 0))))))`;
// instantiate AirModule object
const schema = compile(Buffer.from(source));
const air = instantiate(schema, 'mimc');
// generate trace table
const context = air.initProvingContext([], [3n]);
const trace = context.generateExecutionTrace();
// generate constraint evaluation table
const tracePolys = air.field.interpolateRoots(context.executionDomain, trace);
const constraintEvaluations = context.evaluateTransitionConstraints(tracePolys);
Complete API definitions can be found in air-assembly.d.ts. Here is a quick overview of the provided functionality.
The library exposes a small set of functions that can be used to compile AirAssembly source code, instantiate AirModules, and perform basic analysis of the underlying AIR. These functions are:
compile(source: Buffer
| string
, limits?: StarkLimits
): AirSchema
Parses and compiles AirAssembly source code into an AirSchema object. If source
parameter is a Buffer
, it is expected to contain AirAssembly code. If source
is a string
, it is expected to be a path to a file containing AirAssembly code. If limits
parameter is provided, generated AirSchema
will be validated against these limits.
instantiate(schema: AirSchema
, component: string
, options?: ModuleOptions
): AirModule
Creates an AirModule object for the specified component
within the provided schema
. The AirModule
can then be used to generate execution trace tables and evaluate transition constraints. The optional options
parameter can be used to control instantiation of the AirModule
.
instantiate(schema: AirSchema
, options?: ModuleOptions
): AirModule
An overloaded version of the instantiate()
function with component
name assumed to equal "default". If the provided schema
does not have a default component export, an error will be thrown.
analyze(schema: AirSchema
, component: name
): SchemaAnalysisResult
Performs basic analysis of the component
within the provided schema
to infer such things as degree of transition constraints, number of additions and multiplications needed to evaluate transition function etc.
When instantiating an AirModule
object, an AirModuleOptions
object can be provided to specify any of the following parameters for the module:
Property | Description |
---|---|
limits | Limits to be imposed on the instantiated module. If not provided, default limit values will be used. |
wasmOptions | Options for finite fields which can take advantage of WebAssembly optimization. This property can also be set to a boolean value to turn the optimization on or off. |
extensionFactor | Number by which the execution trace is to be "stretched." Must be a power of 2 at least 2x of the highest constraint degree. This property is optional, the default is the smallest power of 2 that is greater than 2x of the highest constraint degree. |
StarkLimits
object defines limits against which AirSchema
and AirModule
objects are validated. StarkLimits
objects can contain any of the following properties:
Property | Description |
---|---|
maxTraceLength | Maximum number of steps in an execution trace; defaults to 220 |
maxTraceRegisters | Maximum number of state registers; defaults to 64 |
maxStaticRegisters | Maximum number of static registers; defaults to 64 |
maxConstraintCount | Maximum number of transition constraints; defaults to 1024 |
maxConstraintDegree | Highest allowed degree of transition constraints; defaults to 16 |
To generate an execution trace for a computation defined by AirAssembly source code, the following steps should be executed:
compile()
function.AirSchema
to the top-level instantiate()
function to create an AirModule. You'll also need to specify the name of the AirComponent exported from the AirSchema
because AirModules
are instantiated for a specific component.AirModule.initProvingContext()
method. If the computation contains input registers, then input values for these registers must be passed to the initProvingContext()
method. This instantiates the ProvingContext
for a specific set of inputs.ProvingContext.generateExecutionTrace()
method.The code block bellow illustrates these steps:
const schema = compile(Buffer.from(source));
const air = instantiate(schema, `mimc`, { extensionFactor: 16 });
const context = air.initProvingContext([], [3n]);
const trace = context.generateExecutionTrace();
In the above:
source
is a string variable containing AirAssembly source code similar to the one shown in the usage section.AirModule
is instantiated for the exported mimc
component using default limits but setting extension factor to 16
.initProvingContext()
method since the computation shown in the usage section does not define any input registers.3
is passed as the second parameter to the initProvingContext()
method since component initializer expects a vector parameter to initialize the first row of the execution trace.trace
variable will be a matrix with 1 row and 32 columns (corresponding to 1 register and 32 steps).The execution trace for the single register will look like so:
[
3, 1539309651, 3863242857, 3506640509, 1371547896, 215222094, 220283781, 2120321425,
2290167095, 3044083866, 3673976270, 2694057310, 995327947, 2470701222, 798926004, 2416031839,
4124930959, 680273881, 115120944, 2405022753, 963841868, 327198005, 34356700, 1065113318,
2951801258, 791752781, 1878966595, 2503692690, 1792666246, 3884924604, 3800788053, 2681237718
]
Transition constraints described in AirAssembly source code can be evaluated either as a prover or as a verifier. Both methods are described below.
When generating a STARK proof, transition constraints need to be evaluated at all points of the evaluation domain. This can be done efficiently by invoking ProvingContext.evaluateTransitionConstraints()
method. To evaluate the constraints, the following steps should be executed:
ProvingContext.evaluateTransitionConstraints()
method and passing trace polynomials to it.The code block bellow illustrates steps 2 and 3:
const tracePolys = air.field.interpolateRoots(context.executionDomain, trace);
const constraintEvaluations = context.evaluateTransitionConstraints(tracePolys);
In the above:
field
object is used to interpolate the execution trace. The interpolateRoots() method takes a vector of x
values as the first parameter, and a matrix of y
values as the second parameter (the matrix is expected to have a distinct set of y
values in each row). The output is a matrix where each row contains an interpolated polynomial.evaluateTransitionConstraints()
method returns a matrix where each row corresponds to a transition constraint evaluated over the composition domain.For example, evaluating constraints for AirAssembly code from the usage section, will produce a matrix with a single row containing the following values:
[
0, 1888826267, 934997684, 522697873, 0, 3636300716, 301925789, 369141145,
0, 767283131, 270628806, 1668446351, 0, 1739694248, 3247199818, 2569615536,
0, 44729160, 4039819553, 3564072931, 0, 1616917451, 1151293301, 3209868277,
0, 3410907990, 4004509077, 4190379432, 0, 3101507817, 3553581961, 2793433224,
0, 330772896, 4060647779, 2512435701, 0, 3403188821, 235591542, 3772363484,
0, 2256420389, 2357121513, 61957993, 0, 3272390069, 197242509, 2878395132,
0, 155740407, 298885317, 3310802262, 0, 19161130, 691333255, 1102311751,
0, 1751005830, 2349558192, 3473961491, 0, 4006336837, 565227775, 4021023132,
0, 3315940573, 989407555, 2088778801, 0, 898450568, 3610287112, 3576441219,
0, 326707597, 2532917782, 3330991749, 0, 4162556873, 1554019377, 4171366685,
0, 984976271, 2011763604, 728626530, 0, 3611841258, 2245193661, 2605704194,
0, 2583926003, 3992303847, 2748879594, 0, 2379703446, 430289311, 3052280185,
0, 179547660, 1215051408, 2628504587, 0, 2862551083, 2740849758, 925951430,
0, 4000243259, 913649599, 1118200600, 0, 1484209861, 1897468182, 190582872,
0, 4135707956, 1007284323, 2027805646, 0, 1310083809, 2946378676, 350300836,
0, 3019962854, 1468795609, 1874742277, 803208359, 4116321517, 3116095172, 77399359
]
Note: The constraints are evaluated over the composition domain (not evaluation domain). The evaluations can be extended to the full evaluation domain by interpolating them over the composition domain, and then evaluating the resulting polynomials over the evaluation domain. But in practice, to improve performance, the evaluations are first merged into a single vector via random linear combination, and then this single vector is extended to the full evaluation domain.
When verifying a STARK proof, transition constraints need to be evaluated at a small subset of points. This can be done by invoking VerificationContext.evaluateConstraintsAt()
method which evaluates constraints at a single point of evaluation domain. To evaluate constraints at a single point, the following steps should be executed:
compile()
function.AirSchema
together with exported component name to the top-level instantiate()
function to create an AirModule.AirModule.initVerificationContext()
method. If the computation contains input registers, input shapes for these registers must be passed to the initVerificationContext()
method. If any of the input registers are public, input values for these registers must also be passed to the method.VerificationContext.evaluateConstraintsAt()
method and passing to it an x-coordinate of the desired point from the evaluation domain, as well as corresponding values of register traces.The code block bellow illustrates these steps:
const schema = compile(Buffer.from(source));
const air = instantiate(schema, 'mimc', { extensionFactor: 16 });
const context = air.initVerificationContext();
const x = air.field.exp(context.rootOfUnity, 16n);
const rValues = [1539309651n], nValues = [3863242857n];
const evaluations = context.evaluateConstraintsAt(x, rValues, nValues, []);
In the above:
source
is a string variable containing AirAssembly source code similar to the one shown in the usage section.AirModule
for mimc
component is instantiated using default limits but setting extension factor to 16
.initVerificationContext()
method since the computation shown in the usage section does not define any input registers.x
is set to the 17th value in of the execution domain, while rValues
and nValues
contain register traces for 2nd and 3rd steps of the computation. This is because when the extension factor is 16
, the 2nd value of the execution trace aligns with the 17th value of the evaluation domain.evaluations
variable will be an array with a single value 0
. This is because applying transition function to value 1539309651
(current state of the execution trace) results in 3863242857
, which is the next state of the execution trace.An AirSchema
object contains a semantic representation of AirAssembly source code. This representation makes it easy to analyze the source code, and serves as the basis for generating AirModules. An AirSchema
object can be created by compiling AirAssembly code with the top-level compile()
function.
AirSchema
has the following properties:
Property | Description |
---|---|
field | A finite field object instantiated for the field specified for the computations contained withing schema. |
constants | An array of Constant objects describing module constants defined for the computations. |
functions | an array of AirFunction objects describing module functions defined for the computations. |
components | A map of component export, where the key is the name of the component, and the value is an AirComponent object. |
Note: definitions for Constant
and AirFunction
objects mentioned above can be found in air-assembly.d.ts file.
An AirComponent
object is a semantic representation of a specific computation contained within AirSchema. That is, a single AirSchema
object can contain many AirComponents
each describing a distinct computation. This allows packaging AIR of many computations into a single physical file.
AirComponent
has the following properties:
Property | Description |
---|---|
name | String value containing name of the exported component. |
staticRegisters | An array of StaticRegister objects describing static registers defined for the computation. |
secretInputCount | An integer value specifying number of secret input registers defined for the computation. |
traceInitializer | An AirProcedure object describing execution trace initializer expression defined for the computation. |
transitionFunction | An AirProcedure object describing transition function expression defined for the computation. |
constraintEvaluator | An AirProcedure object describing transition constraint evaluator expression defined for the computation. |
constraints | An array of ConstraintDescriptor objects containing metadata for each of the defined transition constraints (e.g. constraint degree). |
maxConstraintDegree | An integer value specifying the highest degree of transition constraints defined for the computation. |
Note: definitions for StaticRegister
, AirProcedure
, and ConstraintDescriptor
objects mentioned above can be found in air-assembly.d.ts file.
An AirModule
object contains JavaScript code needed to create ProvingContext and VerificationContext objects. These objects can then be used to generate execution trace and evaluate transition constraints for a computation. An AirModule
can be instantiated for a specific component of an AirSchema
by using the top-level instantiate()
function.
AirModule
has the following properties:
Property | Description |
---|---|
field | A finite field object used for all arithmetic operations of the computation. |
traceRegisterCount | Number of state registers in the execution trace. |
staticRegisterCount | Number of static registers in the execution trace. |
inputDescriptors | An array of input descriptor objects describing inputs required by the computation. |
secretInputCount | An integer value specifying number of secret input registers defined for the computation. |
constraints | An array of ConstraintDescriptor objects containing metadata for each of the defined transition constraints (e.g. constraint degree). |
maxConstraintDegree | An integer value specifying the highest degree of transition constraints defined for the computation. |
extensionFactor | An integer value specifying how much the execution trace is to be "stretched." |
AirModule
exposes the following methods:
initProvingContext(inputs?: any[]
, seed?: bigint[]
): ProvingContext
Instantiates a ProvingContext object for a specific instance of the computation. This context can then be used to generate execution trace table and constraint evaluation table for the computation.
inputs
parameter must be provided only if the computation contains input registers. In such a case, the shape of input objects must be in line with the shapes specified by the computation's input descriptors.seed
parameter must be provided only if the trace initializer for the computation expects a vector parameter.initVerificationContext(inputShapes?: InputShape[]
, publicInputs?: any[]
): VerificationContext
Instantiates a VerificationContext object for a specific instance of the computation. This context can then be used to evaluate transition constraints at a given point of the evaluation domain of the computation. If the computation contains input registers, inputShapes
parameter must be provided to specify the shapes of consumed inputs. If any of the input registers are public, publicInputs
parameter must also be provided to specify the actual values of all public inputs consumed by the computation.
A ProvingContext
object contains properties and methods needed to help generate a proof for a specific instance of a computation. Specifically, a ProvingContext
can be used to generate an execution trace for a specific set of inputs, and to evaluate transition constraints derived form this trace. To create a ProvingContext
, use initProvingContext()
method of AirModule object.
ProvingContext
has the following properties:
Property | Description |
---|---|
field | Reference to the finite field object of the AirModule which describes the computation. |
rootOfUnit | Primitive root of unity of the evaluation domain for the instance of the computation. |
traceLength | Length of the execution trace for the instance of the computation. |
extensionFactor | Extension factor of the execution trace. |
constraints | An array of constraint descriptors with metadata for the defined transition constraints. |
inputShapes | Shapes of all input registers for the instance of the computation. |
executionDomain | A vector defining domain of the execution trace. |
evaluationDomain | A vector defining domain of the low-degree extended execution trace. The length of the evaluation domain is equal to traceLength * extensionFactor . |
compositionDomain | A vector defining domain of the low-degree extended composition polynomial. The length of the composition domain is equal to traceLength * compositionFactor , where compositionFactor is set to be the smallest power of 2 greater than or equal to the highest constraint degree of the computation. For example, if highest constraint degree is 3 , the compositionFactor will be set to 2 . |
secretRegisterTraces | Values of secret input registers evaluated over the evaluation domain. |
ProvingContext
exposes the following methods:
generateExecutionTrace(): Matrix
Generates an execution trace for a computation. The return value is a matrix where each row corresponds to a dynamic register, and every column corresponds to a step in a computation (i.e. the number of columns will be equal to the length of the execution trace).
evaluateTransitionConstraints(tracePolys: Matrix
): Matrix
Evaluates transition constraints for a computation. The tracePolys
parameter is a matrix where each row represents a polynomial interpolated from a corresponding register of the execution trace. The return value is a matrix where each row represents a transition constraint evaluated over the composition domain.
A VerificationContext
object contains properties and methods needed to help verify a proof of an instance of a computation (i.e. instance of a computation for a specific set of inputs). Specifically, a VerificationContext
can be used to evaluate transition constraints at a specific point of an evaluation domain. To create a VerificationContext
, use initVerificationContext()
method of AirModule object.
VerificationContext
has the following properties:
Property | Description |
---|---|
field | Reference to the finite field object of the AirModule which describes the computation. |
rootOfUnit | Primitive root of unity of the evaluation domain for the instance of the computation. |
traceLength | Length of the execution trace for the instance of the computation. |
extensionFactor | Extension factor of the execution trace. |
constraints | An array of constraint descriptors with metadata for the defined transition constraints. |
inputShapes | Shapes of all input registers for the instance of the computation. |
VerificationContext
exposes the following methods:
bigint
, rValues: bigint[]
, nValues: bigint[]
, sValues: bigint[]
): bigint[]
x
. For example, if the computation is defined by a single transition constraint, an array with one value will be returned. The meaning of the parameters is as follows:
x
is the point of the evaluation domain corresponding to the current step of the computation.rValues
is an array of dynamic register values at the current step of the computation.nValues
is an array of dynamic register values at the next step of the computation.sValues
is an array of secret register values at the current step of the computation.An InputDescriptor
object contains information about an input register defined for the computation.
InputDescriptor
has the following properties:
Property | Description |
---|---|
rank | An integer value indicating the position of the register in the input dependency tree. For example, rank of a register without parents is 1 , rank of a register with a single ancestor is 2 , rank of register with 2 ancestors is 3 etc. |
secret | A boolean value indicating wither the inputs for the register are public or secret. |
binary | A boolean value indicating whether the register can accept only binary values (ones and zeros). |
offset | A signed integer value specifying the number of steps by which an input value is to be shifted in the execution trace. |
parent | An integer value specifying an index of the parent input register. If the register has no parents, this property will be undefined . |
steps | An integer value specifying the number of steps by which a register trace is to be expanded for each input value. For non-leaf registers, this property will be undefined . |
MIT © 2019 Guild of Weavers
FAQs
A low-level language for encoding Algebraic Intermediate Representation of computations
The npm package @guildofweavers/air-assembly receives a total of 1 weekly downloads. As such, @guildofweavers/air-assembly popularity was classified as not popular.
We found that @guildofweavers/air-assembly demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.