@guildofweavers/genstark
Advanced tools
Comparing version 0.6.3 to 0.7.0
@@ -5,4 +5,4 @@ declare module '@guildofweavers/genstark' { | ||
// -------------------------------------------------------------------------------------------- | ||
import { FiniteField } from '@guildofweavers/air-script'; | ||
import { BatchMerkleProof, HashAlgorithm } from '@guildofweavers/merkle'; | ||
import { AirSchema, FiniteField } from '@guildofweavers/air-assembly'; | ||
import { Hash, HashAlgorithm, BatchMerkleProof } from '@guildofweavers/merkle'; | ||
@@ -14,30 +14,75 @@ // RE-EXPORTS | ||
// PUBLIC FUNCTIONS | ||
// -------------------------------------------------------------------------------------------- | ||
/** | ||
* Creates an instance of STARK object based on the provided AirAssembly schema. | ||
* @param schema AirAssembly schema from which the STARK object is to be built. | ||
* @param component Name of the component from which to instantiate STARK. If omitted 'default` will be used. | ||
* @param options Security and optimization options for STARK instance. | ||
* @param logger Optional logger; defaults to console logging; set to null to disable. | ||
*/ | ||
export function instantiate(schema: AirSchema, component: string, options?: Partial<StarkOptions>, logger?: Logger | null): Stark; | ||
/** | ||
* Creates an instance of STARK object from the provided AirAssembly source code. | ||
* @param source AirAssembly source code from which the STARK object is to be built. | ||
* @param component Name of the component from which to instantiate STARK. If omitted 'default` will be used. | ||
* @param options Security and optimization options for STARK instance. | ||
* @param logger Optional logger; defaults to console logging; set to null to disable. | ||
*/ | ||
export function instantiate(source: Buffer, component: string, options?: Partial<StarkOptions>, logger?: Logger | null): Stark; | ||
/** | ||
* Creates an instance of STARK object from the specified AirAssembly file. | ||
* @param path Path to a file containing AirAssembly source code from which the STARK object is to be built. | ||
* @param component Name of the component from which to instantiate STARK. If omitted 'default` will be used. | ||
* @param options Security and optimization options for STARK instance. | ||
* @param logger Optional logger; defaults to console logging; set to null to disable. | ||
*/ | ||
export function instantiate(path: string, component: string, options?: Partial<StarkOptions>, logger?: Logger | null): Stark; | ||
/** | ||
* Creates an instance of STARK object from the provided AirScript source code. | ||
* @param source AirScript source code from which the STARK object is to be built. | ||
* @param options Security and optimization options for STARK instance. | ||
* @param logger Optional logger; defaults to console logging; set to null to disable. | ||
*/ | ||
export function instantiateScript(source: Buffer, options?: Partial<StarkOptions>, logger?: Logger): Stark; | ||
/** | ||
* Creates an instance of STARK object from the specified AirAssembly file. | ||
* @param path Path to a file containing AirScript source code from which the STARK object is to be built. | ||
* @param component Name of the component from which to instantiate STARK. If omitted 'default` will be used. | ||
* @param logger Optional logger; defaults to console logging; set to null to disable. | ||
*/ | ||
export function instantiateScript(path: string, options?: Partial<StarkOptions>, logger?: Logger): Stark; | ||
// STARK | ||
// -------------------------------------------------------------------------------------------- | ||
export interface StarkOptions extends SecurityOptions { | ||
/** A flag indicating whether to use WebAssembly optimizations; defaults to true */ | ||
readonly wasm: boolean; | ||
} | ||
export interface SecurityOptions { | ||
/** | ||
* Execution trace extension factor; defaults to the smallest power of 2 greater than 2x | ||
* of the highest constraint degree | ||
*/ | ||
readonly extensionFactor: number; | ||
/** Execution trace extension factor; defaults to the smallest power of 2 greater than 2x of max constraint degree */ | ||
extensionFactor: number; | ||
/** Number of queries for the execution trace; defaults to 80 */ | ||
exeQueryCount: number; | ||
readonly exeQueryCount: number; | ||
/** Number of queries for low degree proof; defaults to 40 */ | ||
friQueryCount: number; | ||
readonly friQueryCount: number; | ||
/** Hash algorithm for Merkle trees; defaults to sha256 */ | ||
hashAlgorithm: HashAlgorithm; | ||
readonly hashAlgorithm: HashAlgorithm; | ||
} | ||
export interface OptimizationOptions { | ||
export interface Stark { | ||
/** Initial number of bytes to allocate for WASM optimization */ | ||
initialMemory: number; | ||
/** Maximum number of bytes to allocate for WASM optimization */ | ||
maximumMemory: number; | ||
} | ||
export class Stark { | ||
/** Estimated security level of the STARK (experimental) */ | ||
@@ -47,36 +92,17 @@ readonly securityLevel: number; | ||
/** | ||
* Creates a STARK instance based on the provided parameters | ||
* @param source AirScript source for the STARK | ||
* @param security Security options for the STARK instance | ||
* @param optimization A flag indicating whether WebAssembly-based optimization should be enabled | ||
* @param logger Optional logger; defaults to console logging; set to null to disable | ||
* Generate a proof of computation for this STARK. | ||
* @param assertions Boundary constraints for the computation. | ||
* @param inputs Values for initializing all declared input. | ||
* @param seed Seed values for initializing execution trace. | ||
*/ | ||
constructor(source: string, security?: Partial<SecurityOptions>, optimization?: boolean, logger?: Logger); | ||
prove(assertions: Assertion[], inputs?: any[], seed?: bigint[]): StarkProof; | ||
/** | ||
* Creates a STARK instance based on the provided parameters | ||
* @param source AirScript source for the STARK | ||
* @param security Security options for the STARK instance | ||
* @param optimization WebAssembly optimization options | ||
* @param logger Optional logger; defaults to console logging; set to null to disable | ||
* Verifies a proof of computation for this STARK. | ||
* @param assertions Boundary constraints for the computation. | ||
* @param proof Proof of the computation. | ||
* @param publicInputs Values for initializing declared public inputs. | ||
*/ | ||
constructor(source: string, security?: Partial<SecurityOptions>, optimization?: Partial<OptimizationOptions>, logger?: Logger); | ||
verify(assertions: Assertion[], proof: StarkProof, publicInputs?: any[]): boolean; | ||
/** | ||
* Generate a proof of computation for this STARK | ||
* @param assertions Boundary constraints for the computation | ||
* @param inputs TODO | ||
* @param auxPublicInputs TODO | ||
* @param auxSecretInputs TODO | ||
*/ | ||
prove(assertions: Assertion[], inputs: any[], auxPublicInputs?: bigint[][], auxSecretInputs?: bigint[][]): StarkProof; | ||
/** | ||
* Verifies a proof of computation for this STARK | ||
* @param assertions Boundary constraints for the computation | ||
* @param proof Proof of the computation | ||
* @param auxPublicInputs TODO | ||
*/ | ||
verify(assertions: Assertion[], proof: StarkProof, auxPublicInputs?: bigint[][]): boolean; | ||
/** Returns the size in bytes for the provided proof */ | ||
@@ -93,6 +119,6 @@ sizeOf(proof: StarkProof): number; | ||
export interface StarkProof { | ||
evRoot : Buffer; | ||
evProof : BatchMerkleProof; | ||
ldProof : LowDegreeProof; | ||
traceShape : number[]; | ||
evRoot : Buffer; | ||
evProof : BatchMerkleProof; | ||
ldProof : LowDegreeProof; | ||
iShapes : number[][]; | ||
} | ||
@@ -99,0 +125,0 @@ |
41
index.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const air_assembly_1 = require("@guildofweavers/air-assembly"); | ||
const air_script_1 = require("@guildofweavers/air-script"); | ||
const Stark_1 = require("./lib/Stark"); | ||
const utils_1 = require("./lib/utils"); | ||
// RE-EXPORTS | ||
// ================================================================================================ | ||
var Stark_1 = require("./lib/Stark"); | ||
exports.Stark = Stark_1.Stark; | ||
var utils_1 = require("./lib/utils"); | ||
exports.inline = utils_1.inline; | ||
var Stark_2 = require("./lib/Stark"); | ||
exports.Stark = Stark_2.Stark; | ||
var utils_2 = require("./lib/utils"); | ||
exports.inline = utils_2.inline; | ||
var merkle_1 = require("@guildofweavers/merkle"); | ||
@@ -14,2 +18,31 @@ exports.MerkleTree = merkle_1.MerkleTree; | ||
exports.createPrimeField = galois_1.createPrimeField; | ||
// PUBLIC FUNCTIONS | ||
// ================================================================================================ | ||
function instantiate(source, component, options, logger) { | ||
if (logger === null) { | ||
logger = utils_1.noopLogger; | ||
} | ||
else if (logger === undefined) { | ||
logger = new utils_1.Logger(); | ||
} | ||
if (source instanceof air_assembly_1.AirSchema) { | ||
return new Stark_1.Stark(source, component, options, logger); | ||
} | ||
else { | ||
const schema = air_assembly_1.compile(source); | ||
return new Stark_1.Stark(schema, component, options, logger); | ||
} | ||
} | ||
exports.instantiate = instantiate; | ||
function instantiateScript(source, options, logger) { | ||
if (logger === null) { | ||
logger = utils_1.noopLogger; | ||
} | ||
else if (logger === undefined) { | ||
logger = new utils_1.Logger(); | ||
} | ||
const schema = air_script_1.compile(source); | ||
return instantiate(schema, 'default', options, logger); | ||
} | ||
exports.instantiateScript = instantiateScript; | ||
//# sourceMappingURL=index.js.map |
@@ -11,3 +11,3 @@ "use strict"; | ||
// -------------------------------------------------------------------------------------------- | ||
constructor(constraints, assertions, seed, context, logger) { | ||
constructor(assertions, seed, context, logger) { | ||
this.field = context.field; | ||
@@ -18,9 +18,9 @@ this.bPoly = new BoundaryConstraints_1.BoundaryConstraints(assertions, context); | ||
// degree of trace polynomial combination | ||
this.combinationDegree = getCombinationDegree(constraints, context.traceLength); | ||
this.combinationDegree = getCombinationDegree(context.constraints, context.traceLength); | ||
// degree of composition polynomial is deg(C(x)) = deg(Q(x)) - deg(Z(x)) | ||
this.compositionDegree = Math.max(this.combinationDegree - context.traceLength, context.traceLength); | ||
// group transition constraints together by their degree | ||
this.constraintGroups = groupTransitionConstraints(constraints, context.traceLength); | ||
this.constraintGroups = groupTransitionConstraints(context.constraints, context.traceLength); | ||
// create coefficients needed for linear combination | ||
let dCoefficientCount = constraints.length; | ||
let dCoefficientCount = context.constraints.length; | ||
for (let { degree, indexes } of this.constraintGroups) { | ||
@@ -51,3 +51,3 @@ if (degree < this.combinationDegree) { | ||
try { | ||
qEvaluations = context.evaluateTracePolynomials(pPolys); | ||
qEvaluations = context.evaluateTransitionConstraints(pPolys); | ||
} | ||
@@ -54,0 +54,0 @@ catch (error) { |
@@ -12,5 +12,4 @@ "use strict"; | ||
this.fieldElementSize = config.field.elementSize; | ||
this.stateWidth = config.stateWidth; | ||
this.iRegisterCount = config.iRegisterCount; | ||
this.sRegisterCount = config.sRegisterCount; | ||
this.tRegisterCount = config.traceRegisterCount; | ||
this.sRegisterCount = config.secretInputCount; | ||
this.hashDigestSize = hashDigestSize; | ||
@@ -46,6 +45,9 @@ } | ||
} | ||
// trace shape | ||
offset = buffer.writeUInt8(proof.traceShape.length, offset); | ||
for (let level of proof.traceShape) { | ||
offset = buffer.writeUInt32LE(level, offset); | ||
// input shapes | ||
offset = buffer.writeUInt8(proof.iShapes.length, offset); | ||
for (let shape of proof.iShapes) { | ||
offset = buffer.writeUInt8(shape.length, offset); | ||
for (let level of shape) { | ||
offset = buffer.writeUInt32LE(level, offset); | ||
} | ||
} | ||
@@ -90,9 +92,14 @@ // return the buffer | ||
} | ||
// trace shape | ||
const traceDepth = buffer.readUInt8(offset); | ||
// input shapes | ||
const inputCount = buffer.readUInt8(offset); | ||
offset += 1; | ||
const traceShape = new Array(traceDepth); | ||
for (let i = 0; i < traceDepth; i++) { | ||
traceShape[i] = buffer.readUInt32LE(offset); | ||
offset += 4; | ||
const inputShapes = new Array(inputCount); | ||
for (let i = 0; i < inputCount; i++) { | ||
let rank = buffer.readUInt8(offset); | ||
offset += 1; | ||
inputShapes[i] = new Array(); | ||
for (let j = 0; j < rank; j++) { | ||
inputShapes[i][j] = buffer.readUInt32LE(offset); | ||
offset += 4; | ||
} | ||
} | ||
@@ -109,3 +116,3 @@ // build and return the proof | ||
}, | ||
traceShape: traceShape | ||
iShapes: inputShapes | ||
}; | ||
@@ -116,3 +123,3 @@ } | ||
getValueCount() { | ||
return this.stateWidth + this.sRegisterCount + this.iRegisterCount; | ||
return this.tRegisterCount + this.sRegisterCount; | ||
} | ||
@@ -119,0 +126,0 @@ } |
133
lib/Stark.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const merkle_1 = require("@guildofweavers/merkle"); | ||
const air_script_1 = require("@guildofweavers/air-script"); | ||
const air_assembly_1 = require("@guildofweavers/air-assembly"); | ||
const components_1 = require("./components"); | ||
@@ -15,5 +15,2 @@ const utils_1 = require("./utils"); | ||
const MAX_FRI_QUERY_COUNT = 64; | ||
const WASM_PAGE_SIZE = 65536; // 64 KB | ||
const DEFAULT_INITIAL_MEMORY = 32 * 2 ** 20; // 32 MB | ||
const DEFAULT_MAXIMUM_MEMORY = 2 * 2 ** 30 - WASM_PAGE_SIZE; // 2 GB less one page | ||
const HASH_ALGORITHMS = ['sha256', 'blake2s256']; | ||
@@ -26,33 +23,20 @@ const DEFAULT_HASH_ALGORITHM = 'sha256'; | ||
// -------------------------------------------------------------------------------------------- | ||
constructor(source, security, optimization, logger) { | ||
if (typeof source !== 'string') | ||
throw new TypeError('Source script must be a string'); | ||
if (!source.trim()) | ||
throw new TypeError('Source script cannot be an empty string'); | ||
let extensionFactor = security ? security.extensionFactor : undefined; | ||
let sOptions; | ||
if (optimization) { | ||
const wasmOptions = buildWasmOptions(optimization); | ||
// instantiate AIR module | ||
this.air = air_script_1.parseScript(source, { wasmOptions, extensionFactor }); | ||
if (!this.air.field.isOptimized) { | ||
console.warn(`WARNING: WebAssembly optimization is not available for the specified field`); | ||
} | ||
// instantiate Hash object | ||
sOptions = validateSecurityOptions(security, this.air.extensionFactor); | ||
const wasmOptions2 = buildWasmOptions(optimization); // TODO: use the same options as for AIR | ||
this.hash = merkle_1.createHash(sOptions.hashAlgorithm, wasmOptions2); | ||
if (!this.hash.isOptimized) { | ||
console.warn(`WARNING: WebAssembly optimization is not available for ${sOptions.hashAlgorithm} hash algorithm`); | ||
} | ||
constructor(schema, component, options = {}, logger) { | ||
const wasmOptions = buildWasmOptions(options.wasm); | ||
// instantiate AIR module | ||
this.air = air_assembly_1.instantiate(schema, component, { extensionFactor: options.extensionFactor, wasmOptions }); | ||
if (wasmOptions && !this.air.field.isOptimized) { | ||
console.warn(`WARNING: WebAssembly optimization is not available for the specified field`); | ||
} | ||
else { | ||
this.air = air_script_1.parseScript(source, { extensionFactor }); | ||
sOptions = validateSecurityOptions(security, this.air.extensionFactor); | ||
this.hash = merkle_1.createHash(sOptions.hashAlgorithm, false); | ||
// build security options | ||
const sOptions = buildSecurityOptions(options, this.air.extensionFactor); | ||
// instantiate Hash object | ||
this.hash = merkle_1.createHash(sOptions.hashAlgorithm, this.air.field.isOptimized); | ||
if (!this.hash.isOptimized) { | ||
console.warn(`WARNING: WebAssembly optimization is not available for ${sOptions.hashAlgorithm} hash algorithm`); | ||
} | ||
this.extensionFactor = sOptions.extensionFactor; | ||
; | ||
this.indexGenerator = new components_1.QueryIndexGenerator(sOptions); | ||
this.serializer = new Serializer_1.Serializer(this.air, this.hash.digestSize); | ||
this.logger = logger || new utils_1.Logger(); | ||
this.logger = logger; | ||
} | ||
@@ -62,3 +46,3 @@ // ACCESSORS | ||
get securityLevel() { | ||
const extensionFactor = this.extensionFactor; | ||
const extensionFactor = this.air.extensionFactor; | ||
// execution trace security | ||
@@ -76,3 +60,3 @@ const exeQueryCount = this.indexGenerator.exeQueryCount; | ||
// -------------------------------------------------------------------------------------------- | ||
prove(assertions, inputs, auxPublicInputs, auxSecretInputs) { | ||
prove(assertions, inputs, seed) { | ||
const log = this.logger.start('Starting STARK computation'); | ||
@@ -84,7 +68,4 @@ // 0 ----- validate parameters | ||
throw new TypeError('At least one assertion must be provided'); | ||
if (!Array.isArray(inputs)) | ||
throw new TypeError('Initialization values parameter must be an array'); | ||
// 1 ----- set up evaluation context | ||
const field = this.air.field; | ||
const context = this.air.initProof(inputs, auxPublicInputs || [], auxSecretInputs || []); | ||
const context = this.air.initProvingContext(inputs, seed); | ||
const evaluationDomainSize = context.evaluationDomain.length; | ||
@@ -103,9 +84,9 @@ log('Set up evaluation context'); | ||
// 3 ----- compute P(x) polynomials and low-degree extend them | ||
const pPolys = field.interpolateRoots(context.executionDomain, executionTrace); | ||
const pPolys = context.field.interpolateRoots(context.executionDomain, executionTrace); | ||
log('Computed execution trace polynomials P(x)'); | ||
const pEvaluations = field.evalPolysAtRoots(pPolys, context.evaluationDomain); | ||
const pEvaluations = context.field.evalPolysAtRoots(pPolys, context.evaluationDomain); | ||
log('Low-degree extended P(x) polynomials over evaluation domain'); | ||
// 4 ----- build merkle tree for evaluations of P(x) and S(x) | ||
const hEvaluations = context.hiddenRegisterTraces; | ||
const eVectors = [...field.matrixRowsToVectors(pEvaluations), ...hEvaluations]; | ||
const sEvaluations = context.secretRegisterTraces; | ||
const eVectors = [...context.field.matrixRowsToVectors(pEvaluations), ...sEvaluations]; | ||
const hashedEvaluations = this.hash.mergeVectorRows(eVectors); | ||
@@ -117,3 +98,3 @@ log('Serialized evaluations of P(x) and S(x) polynomials'); | ||
const cLogger = this.logger.sub('Computing composition polynomial'); | ||
const cPoly = new components_1.CompositionPolynomial(this.air.constraints, assertions, eTree.root, context, cLogger); | ||
const cPoly = new components_1.CompositionPolynomial(assertions, eTree.root, context, cLogger); | ||
const cEvaluations = cPoly.evaluateAll(pPolys, pEvaluations, context); | ||
@@ -124,3 +105,3 @@ this.logger.done(cLogger); | ||
const lCombination = new components_1.LinearCombination(eTree.root, cPoly.compositionDegree, cPoly.coefficientCount, context); | ||
const lEvaluations = lCombination.computeMany(cEvaluations, pEvaluations, hEvaluations); | ||
const lEvaluations = lCombination.computeMany(cEvaluations, pEvaluations, sEvaluations); | ||
log('Combined P(x) and S(x) evaluations with C(x) evaluations'); | ||
@@ -152,3 +133,3 @@ // 7 ----- Compute low-degree proof | ||
ldProof: ldProof, | ||
traceShape: context.traceShape | ||
iShapes: context.inputShapes | ||
}; | ||
@@ -158,3 +139,3 @@ } | ||
// -------------------------------------------------------------------------------------------- | ||
verify(assertions, proof, auxPublicInputs) { | ||
verify(assertions, proof, publicInputs) { | ||
const log = this.logger.start('Starting STARK verification'); | ||
@@ -165,8 +146,7 @@ // 0 ----- validate parameters | ||
// 1 ----- set up evaluation context | ||
const field = this.air.field; | ||
const eRoot = proof.evRoot; | ||
const extensionFactor = this.extensionFactor; | ||
const context = this.air.initVerification(proof.traceShape, auxPublicInputs || []); | ||
const extensionFactor = this.air.extensionFactor; | ||
const context = this.air.initVerificationContext(proof.iShapes, publicInputs); | ||
const evaluationDomainSize = context.traceLength * extensionFactor; | ||
const cPoly = new components_1.CompositionPolynomial(this.air.constraints, assertions, eRoot, context, utils_1.noop); | ||
const cPoly = new components_1.CompositionPolynomial(assertions, eRoot, context, utils_1.noop); | ||
const lCombination = new components_1.LinearCombination(eRoot, cPoly.compositionDegree, cPoly.coefficientCount, context); | ||
@@ -180,9 +160,9 @@ log('Set up evaluation context'); | ||
const pEvaluations = new Map(); | ||
const hEvaluations = new Map(); | ||
const sEvaluations = new Map(); | ||
for (let i = 0; i < proof.evProof.values.length; i++) { | ||
let mergedEvaluations = proof.evProof.values[i]; | ||
let position = augmentedPositions[i]; | ||
let [p, h] = this.parseValues(mergedEvaluations); | ||
let [p, s] = this.parseValues(mergedEvaluations); | ||
pEvaluations.set(position, p); | ||
hEvaluations.set(position, h); | ||
sEvaluations.set(position, s); | ||
} | ||
@@ -208,10 +188,10 @@ log(`Decoded evaluation spot checks`); | ||
let step = positions[i]; | ||
let x = field.exp(context.rootOfUnity, BigInt(step)); | ||
let x = context.field.exp(context.rootOfUnity, BigInt(step)); | ||
let pValues = pEvaluations.get(step); | ||
let nValues = pEvaluations.get((step + extensionFactor) % evaluationDomainSize); | ||
let hValues = hEvaluations.get(step); | ||
let sValues = sEvaluations.get(step); | ||
// evaluate composition polynomial at x | ||
let cValue = cPoly.evaluateAt(x, pValues, nValues, hValues, context); | ||
let cValue = cPoly.evaluateAt(x, pValues, nValues, sValues, context); | ||
// combine composition polynomial evaluation with values of P(x) and S(x) | ||
lcValues[i] = lCombination.computeOne(x, cValue, pValues, hValues); | ||
lcValues[i] = lCombination.computeOne(x, cValue, pValues, sValues); | ||
} | ||
@@ -246,3 +226,3 @@ log(`Verified transition and boundary constraints`); | ||
getAugmentedPositions(positions, evaluationDomainSize) { | ||
const skip = this.extensionFactor; | ||
const skip = this.air.extensionFactor; | ||
const augmentedPositionSet = new Set(); | ||
@@ -269,15 +249,12 @@ for (let i = 0; i < positions.length; i++) { | ||
const elementSize = this.air.field.elementSize; | ||
const stateWidth = this.air.stateWidth; | ||
const sRegisterCount = this.air.sRegisterCount; | ||
const iRegisterCount = this.air.iRegisterCount; | ||
let offset = 0; | ||
const pValues = new Array(stateWidth); | ||
const pValues = new Array(this.air.traceRegisterCount); | ||
for (let i = 0; i < pValues.length; i++, offset += elementSize) { | ||
pValues[i] = utils_1.readBigInt(buffer, offset, elementSize); | ||
} | ||
const hValues = new Array(sRegisterCount + iRegisterCount); | ||
for (let i = 0; i < hValues.length; i++, offset += elementSize) { | ||
hValues[i] = utils_1.readBigInt(buffer, offset, elementSize); | ||
const sValues = new Array(this.air.secretInputCount); | ||
for (let i = 0; i < sValues.length; i++, offset += elementSize) { | ||
sValues[i] = utils_1.readBigInt(buffer, offset, elementSize); | ||
} | ||
return [pValues, hValues]; | ||
return [pValues, sValues]; | ||
} | ||
@@ -288,3 +265,3 @@ } | ||
// ================================================================================================ | ||
function validateSecurityOptions(options, extensionFactor) { | ||
function buildSecurityOptions(options, extensionFactor) { | ||
// execution trace spot checks | ||
@@ -311,17 +288,11 @@ const exeQueryCount = (options ? options.exeQueryCount : undefined) || DEFAULT_EXE_QUERY_COUNT; | ||
} | ||
function buildWasmOptions(options) { | ||
if (typeof options === 'boolean') { | ||
return { | ||
memory: new WebAssembly.Memory({ | ||
initial: Math.ceil(DEFAULT_INITIAL_MEMORY / WASM_PAGE_SIZE), | ||
maximum: Math.ceil(DEFAULT_MAXIMUM_MEMORY / WASM_PAGE_SIZE) | ||
}) | ||
}; | ||
} | ||
else { | ||
const initialMemory = Math.ceil((options.initialMemory || DEFAULT_INITIAL_MEMORY) / WASM_PAGE_SIZE); | ||
const maximumMemory = Math.ceil((options.maximumMemory || DEFAULT_MAXIMUM_MEMORY) / WASM_PAGE_SIZE); | ||
const memory = new WebAssembly.Memory({ initial: initialMemory, maximum: maximumMemory }); | ||
return { memory }; | ||
} | ||
function buildWasmOptions(useWasm) { | ||
if (useWasm === false) | ||
return undefined; | ||
return { | ||
memory: new WebAssembly.Memory({ | ||
initial: 512, | ||
maximum: 32768 // 2 GB | ||
}) | ||
}; | ||
} | ||
@@ -328,0 +299,0 @@ function validateAssertions(trace, assertions) { |
@@ -14,2 +14,3 @@ "use strict"; | ||
exports.Logger = Logger_1.Logger; | ||
exports.noopLogger = Logger_1.noopLogger; | ||
exports.inline = inliners; | ||
@@ -16,0 +17,0 @@ // MATH |
@@ -59,2 +59,10 @@ "use strict"; | ||
exports.Logger = Logger; | ||
// NOOP LOGGER | ||
// ================================================================================================ | ||
const noopLog = (message) => undefined; | ||
exports.noopLogger = { | ||
start: (message, prefix) => noopLog, | ||
sub: (message) => noopLog, | ||
done: (log, message) => undefined | ||
}; | ||
//# sourceMappingURL=Logger.js.map |
@@ -32,7 +32,10 @@ "use strict"; | ||
size += ldProof; | ||
// trace shape | ||
let traceShape = 1; // trace depth | ||
traceShape += proof.traceShape.length * 4; | ||
size += traceShape; | ||
return { evProof, ldProof: { lcProof, levels: ldLevels, total: ldProof }, traceShape, total: size }; | ||
// input shapes | ||
let inputShapes = 1; // input count | ||
for (let i = 0; i < proof.iShapes.length; i++) { | ||
inputShapes += 1; // rank | ||
inputShapes += proof.iShapes[i].length * 4; | ||
} | ||
size += inputShapes; | ||
return { evProof, ldProof: { lcProof, levels: ldLevels, total: ldProof }, inputShapes, total: size }; | ||
} | ||
@@ -39,0 +42,0 @@ exports.sizeOf = sizeOf; |
{ | ||
"name": "@guildofweavers/genstark", | ||
"version": "0.6.3", | ||
"version": "0.7.0", | ||
"description": "zk-STARK generation library", | ||
@@ -24,9 +24,17 @@ "main": "index.js", | ||
}, | ||
"scripts": { | ||
"clean": "rimraf bin", | ||
"compile": "tsc -p .", | ||
"copyfiles": "copyfiles ./package*.json ./*.d.ts ./*.md ./.npmignore bin", | ||
"build": "npm run clean && npm run copyfiles && npm run compile", | ||
"publish": "npm publish bin --access=public" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "12.7.x", | ||
"del": "5.0.x", | ||
"gulp": "4.0.x" | ||
"copyfiles": "2.1.x", | ||
"rimraf": "3.0.x" | ||
}, | ||
"dependencies": { | ||
"@guildofweavers/air-script": "0.5.x", | ||
"@guildofweavers/air-assembly": "0.3.x", | ||
"@guildofweavers/air-script": "0.6.x", | ||
"@guildofweavers/galois": "0.4.x", | ||
@@ -33,0 +41,0 @@ "@guildofweavers/merkle": "0.3.x" |
145
README.md
@@ -19,13 +19,15 @@ # genSTARK | ||
```TypeScript | ||
import { Stark } from '@guildofweavers/genstark'; | ||
import { instantiateScript } from '@guildofweavers/genstark'; | ||
// define a STARK for this computation | ||
const fooStark = new Stark(` | ||
const fooStark = instantiateScript(Buffer.from(` | ||
define Foo over prime field (2^32 - 3 * 2^25 + 1) { | ||
secret input startValue: element[1]; | ||
// define transition function | ||
transition 1 register { | ||
for each ($i0) { | ||
init { $i0 } | ||
for steps [1..63] { $r0 + 2 } | ||
for each (startValue) { | ||
init { yield startValue; } | ||
for steps [1..63] { yield $r0 + 2; } | ||
} | ||
@@ -36,5 +38,5 @@ } | ||
enforce 1 constraint { | ||
for all steps { transition($r) = $n } | ||
for all steps { enforce transition($r) = $n; } | ||
} | ||
}`); | ||
}`)); | ||
@@ -96,90 +98,84 @@ // create a proof that if we start computation at 1, we end up at 127 after 64 steps | ||
To create a STARK for a computation you need to create a `Stark` object like so: | ||
The simplest way to create a STARK for a computation is to instantiate it from [AirScript](https://github.com/GuildOfWeavers/AirScript) source like so: | ||
```TypeScript | ||
const myStark = new Stark(source, security, optimization, logger); | ||
const myStark = new instantiateScript(source, options, logger); | ||
``` | ||
where: | ||
* `source` is the AirScript source code. If this parameter is a `Buffer`, it is expected to contain AirScript code in UTF8 format. If the parameter is a string, it is expected to be a path to a file containing AirScript code. | ||
* `options` is an optional parameter with additional [STARK options](#Stark-options). | ||
* `logger` is an optional logger. The default logger prints output to the console, but it can be replaced with anything that complies with the Logger interface. | ||
The meaning of the constructor parameters is as follows: | ||
### STARK options | ||
When provided, STARK options parameter should have the following form: | ||
| Parameter | Description | | ||
| ------------------ | ----------- | | ||
| source | [AirScript](https://github.com/GuildOfWeavers/AirScript) source defining transition function, transition constraints, and other properties of the STARK. | | ||
| security? | An optional property specifying [security parameters](#Security-options) for the STARK. | | ||
| optimization? | An optional property specifying [WASM optimization parameters](#Optimization-options) for the STARK. You can also set this to `true` to turn on WASM optimization with default parameters. | | ||
| logger? | An optional logger. The default logger prints output to the console, but it can be replaced with anything that complies with the Logger interface. | | ||
**Note:** WASM-optimization is available for certain [finite fields](https://github.com/GuildOfWeavers/galois#wasm-optimization) and [hash functions](https://github.com/GuildOfWeavers/merkle#hash). If the field or the hash function you are using does not support WASM-optimization, a warning will be printed and its JavaScript equivalents will be used. In general, WASM optimization can speed up STARK proof time by 2x - 5x. | ||
### Security options | ||
Security options parameter should have the following form: | ||
| Property | Description | | ||
| ------------------ | ----------- | | ||
| extensionFactor? | Number by which the execution trace is "stretched." Must be a power of 2 at least 2x of the constraint degree, but cannot exceed 32. This property is optional, the default is smallest power of 2 that is greater than 2 * constraint degree. | | ||
| exeQueryCount? | Number of queries of the execution trace to include into the proof. This property is optional; the default is 80; the max is 128. | | ||
| exeQueryCount? | Number of queries of the execution trace to include into the proof. This property is optional; the default is 80; the max is 128. | | ||
| friQueryCount? | Number of queries of the columns of low degree proof to include into the proof. This property is optional; the default is 40; the max is 64. | | ||
| hashAlgorithm? | Hash algorithm to use when building Merkle trees for the proof. Currently, can be one of the following values: `sha256`, `blake2s256`. This property is optional; the default is `sha256`. | | ||
| wasm | A flag indicating whether to use WebAssembly optimizations. This proper is optional, the default is `true`. | | ||
### Optimization options | ||
Optimization options parameter should have the following form: | ||
**Note:** WASM-optimization is available for certain [finite fields](https://github.com/GuildOfWeavers/galois#wasm-optimization) and [hash functions](https://github.com/GuildOfWeavers/merkle#hash). If the field or the hash function you are using does not support WASM-optimization, a warning will be printed and its JavaScript equivalents will be used. In general, WASM optimization can speed up STARK proof time by 2x - 5x. | ||
| Property | Description | | ||
| ------------------ | ----------- | | ||
| initialMemory? | Initial number of bytes to allocate for WASM optimization; the default is 32 MB. | | ||
| maximumMemory? | Maximum number of bytes to allocate for WASM optimization; the default is 2 GB. | | ||
You can also instantiate a STARK from [AirAssembly](https://github.com/GuildOfWeavers/AirAssembly) source, or even from an [AirSchema](https://github.com/GuildOfWeavers/AirAssembly#air-schema) object: | ||
```TypeScript | ||
const myStark = new instantiate(source, component, options, logger); | ||
``` | ||
where: | ||
* `source` is either `Buffer` with AirAssembly source code, a string that is a path to a file with AirAssembly source code, or an `AirSchema` object. | ||
* `component` is a name of the component within AirAssembly source from which the STARK is to be instantiated. If this parameter is omitted the `default` component will be instantiated. | ||
* `options` is an optional parameter with additional [STARK options](#Stark-options). | ||
* `logger` is an optional logger. The default logger prints output to the console, but it can be replaced with anything that complies with the Logger interface. | ||
## Generating proofs | ||
Once you have a `Stark` object, you can start generating proofs using `Stark.prove()` method like so: | ||
```TypeScript | ||
const proof = myStark.prove(assertions, initValues, publicInputs?, secretInputs?); | ||
const proof = myStark.prove(assertions, inputs?, seed?); | ||
``` | ||
The meaning of the parameters is as follows: | ||
where: | ||
* `assertions` is an array of [Assertion](#Assertions) objects (also called boundary constraints). These assertions specify register values at specific steps of a valid computation. At least 1 assertion must be provided. | ||
* `inputs` is an array of values initializing all declared inputs. This parameter must be provided only if the STARK requires inputs. | ||
* `seed` is an array of seed values for initializing execution trace. This parameter must be provided only if the STARK requires initialization from seed values. | ||
| Parameter | Description | | ||
| ------------- | ----------- | | ||
| assertions | An array of [Assertion](#Assertions) objects (also called boundary constraints). These assertions specify register values at specific steps of a valid computation. At least 1 assertion must be provided. | | ||
| inputs | An array containing initialization values for all `$i` registers. Must contain at least one set of values. | | ||
| auxPublicInputs? | An array containing initialization values for all `$p` registers. This parameter is optional and can be skipped if no public auxiliary inputs have been defined. | | ||
| auxSecretInputs? | An array containing initialization values for all `$s` registers. This parameter is optional and can be skipped if no secret auxiliary inputs have been defined. | | ||
### Inputs | ||
Handling of inputs deserves a bit more explanation. As described above, there are 3 ways to supply inputs to `STARK.prove()` method: | ||
Handling of inputs deserves a bit more explanation. | ||
* `inputs` parameter is always required. It is used to initialize the execution trace for different instances of the computation. The structure of `inputs` must match the structure of input loops defined in transition function. For more information, see [Input loops](https://github.com/GuildOfWeavers/AirScript#input-loops) section of AirScript documentation. | ||
* The other two parameters provide values for the input registers defined in the STARK. To learn more about these, refer to [Readonly registers](https://github.com/GuildOfWeavers/AirScript#readonly-registers) section of AirScript documentation. These parameters are required only if STARK's definition includes auxiliary input registers. | ||
If you've instantiated a STARK from AirScrip source, you don't need to worry about the `seed` parameter as AirScript does not support explicit trace initialization. However, STARKs instantiated from AirScript source always require `inputs` parameter. The shape of this parameter will depend on how you've defined your inputs (see [AirScript](https://github.com/GuildOfWeavers/AirScript#inputs) documentation for more info). | ||
For example, the fragment below specifies that a STARK must have a single `$i` register of depth 0, and 3 auxiliary input registers. Moreover, prefixes `$p` and `$s` specify that 2 of the auxiliary registers are *public* (the values will be known to the prover **and** the verified), and 1 of the registers is *secret* (the values will be known **only** to the prover). | ||
For example, if your input declaration looks like this: | ||
``` | ||
transition 1 register { | ||
for each ($i0) { | ||
init { $i0 } | ||
for steps [1..63] { $r0 + 2 } | ||
} | ||
} | ||
public input foo: element[1]; | ||
``` | ||
your `inputs` array will need to contain a single element which will be an array of values for input `foo`. For example: | ||
```TypeScript | ||
[[1n, 2n]] | ||
``` | ||
using 3 readonly registers { | ||
$p0: repeat [...]; | ||
$p1: spread [...]; | ||
$s0: spread [...]; | ||
} | ||
If you've declared more than one input, like so: | ||
``` | ||
Based on this definition, the parameters for `STARK.prove()` method should be supplied like so: | ||
public input foo: element[1]; | ||
public input bar: element[1]; | ||
``` | ||
your `inputs` array will need to contain an array of values for each declared input. For example: | ||
```TypeScript | ||
// let's say we want to run the computation for 2 sets of inputs | ||
let inputs = [[1n], [2n]]; | ||
[[1n, 2n], [3n, 4n]] | ||
``` | ||
Here, the `[1n, 2n]` values are assigned to input `foo` and `[3n, 4n]` values are assigned to input `bar`. | ||
// define values for public auxiliary inputs | ||
let pValues1 = [1n, 2n, 3n, 4n]; | ||
let pValues2 = [1n, 2n, 3n, 4n, 5n, 6n, 7n, 7n]; | ||
// define values for secret auxiliary inputs | ||
let sValues = [10n, 11n, 12n, 13n]; | ||
// generate the proof | ||
let proof = fooStark.prove(assertions, inputs, [pValues1, pValues2], [sValues]); | ||
If you've declared [nested inputs](https://github.com/GuildOfWeavers/AirScript#nested-input-loops) like so: | ||
``` | ||
When the proof is generated, the provided values will "appear" in registers `$i0`, `$p0`, `$p1`, and `$s0` to be used in transition function and transition constraints. The rules for how this happens are also described in the [Input loops](https://github.com/GuildOfWeavers/AirScript#input-loops) and [Readonly registers](https://github.com/GuildOfWeavers/AirScript#readonly-registers) sections of AirScript documentation. | ||
public input foo: element[1]; | ||
public input bar: element[1][1]; | ||
``` | ||
your `inputs` object may look like so: | ||
```TypeScript | ||
[[1n, 2n], [[3n, 4n], [5n, 6n]]] | ||
``` | ||
Here, for each value of `foo`, we need to provide a list of value for `bar`. | ||
#### AirAssembly inputs | ||
If you've instantiated a STARK from AirAssembly source, you may need to provide values for both `inputs` and `seed` parameters based on what is required by AirAssembly declaration. The `inputs` parameter requirements are defined via [input registers](https://github.com/GuildOfWeavers/AirAssembly/tree/master/specs#input-registers), while `seed` parameter requirements are defined via [trace initializer](https://github.com/GuildOfWeavers/AirAssembly/tree/master/specs#trace-initializer). | ||
@@ -190,12 +186,9 @@ ## Verifying proofs | ||
```TypeScript | ||
const result = myStark.verify(assertions, proof, auxPublicInputs?); | ||
const result = myStark.verify(assertions, proof, publicInputs?); | ||
``` | ||
The meaning of the parameters is as follows: | ||
where: | ||
* `assertions` is the same array of [Assertion](#Assertions) objects that was passed to the `prove()` method. | ||
* `proof` is the proof object that was generated by the `prove()` method. | ||
* `publicInputs` is an array of values for initializing all declared public inputs. | ||
| Parameter | Description | | ||
| ------------- | ----------- | | ||
| assertions | The same array of [Assertion](#Assertions) objects that was passed to the `prove()` method. | | ||
| proof | The proof object that was generated by the `prove()` method. | | ||
| auxPublicInputs? | An array containing initialization values for all `$p` registers. This parameter is optional and can be skipped if no public auxiliary inputs have been defined. | | ||
Verifying a proof basically attests to something like this: | ||
@@ -202,0 +195,0 @@ |
95621
1617
4
248
+ Added@guildofweavers/air-assembly@0.3.6(transitive)
+ Added@guildofweavers/air-script@0.6.4(transitive)
- Removed@guildofweavers/air-script@0.5.8(transitive)