Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
siq1 committed Aug 12, 2024
1 parent a799c99 commit a188643
Show file tree
Hide file tree
Showing 43 changed files with 3,759 additions and 181 deletions.
298 changes: 298 additions & 0 deletions ecgo/builder/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
// Some content of this file is copied from gnark/frontend/cs/r1cs/api.go

package builder

import (
"github.com/PolyhedraZK/ExpanderCompilerCollection/ecgo/irsource"
"github.com/consensys/gnark/constraint"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/math/bits"
)

// API defines a set of methods for interacting with the circuit builder.
type API interface {
// ToSingleVariable converts an expression to a single base variable.
ToSingleVariable(frontend.Variable) frontend.Variable
// Output adds a variable to the circuit's output.
Output(frontend.Variable)
// LayerOf returns an approximation of the layer in which a variable will be placed after compilation.
LayerOf(frontend.Variable) int // For debug usage.
// ToFirstLayer uses a hint to pull a variable back to the first layer.
ToFirstLayer(frontend.Variable) frontend.Variable
// GetRandomValue returns a random value for use within the circuit.
GetRandomValue() frontend.Variable
}

// ---------------------------------------------------------------------------------------------
// Arithmetic

// Add computes the sum i1+i2+...in and returns the result.
func (builder *builder) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable {
// extract frontend.Variables from input
vars := builder.toVariables(append([]frontend.Variable{i1, i2}, in...)...)
return builder.add(vars, false)
}

func (builder *builder) MulAcc(a, b, c frontend.Variable) frontend.Variable {
return builder.Add(builder.Mul(b, c), a)
}

// Sub computes the difference between the given variables.
func (builder *builder) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable {
vars := builder.toVariables(append([]frontend.Variable{i1, i2}, in...)...)
return builder.add(vars, true)
}

// returns res = Σ(vars) or res = vars[0] - Σ(vars[1:]) if sub == true.
func (builder *builder) add(vars []variable, sub bool) variable {
coef := make([]constraint.Element, len(vars))
coef[0] = builder.tOne
if sub {
for i := 1; i < len(vars); i++ {
coef[i] = builder.field.Neg(builder.tOne)
}
} else {
for i := 1; i < len(vars); i++ {
coef[i] = builder.tOne
}
}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.LinComb,
Inputs: unwrapVariables(vars),
LinCombCoef: coef,
})
return builder.addVar()
}

// Neg returns the negation of the given variable.
func (builder *builder) Neg(i frontend.Variable) frontend.Variable {
v := builder.toVariable(i)
coef := []constraint.Element{builder.field.Neg(builder.tOne)}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.LinComb,
Inputs: []int{v.id},
LinCombCoef: coef,
})
return builder.addVar()
}

// Mul computes the product of the given variables.
func (builder *builder) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable {
vars := builder.toVariables(append([]frontend.Variable{i1, i2}, in...)...)
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.Mul,
Inputs: unwrapVariables(vars),
})
return builder.addVar()
}

// DivUnchecked returns i1 divided by i2 and returns 0 if both i1 and i2 are zero.
func (builder *builder) DivUnchecked(i1, i2 frontend.Variable) frontend.Variable {
vars := builder.toVariables(i1, i2)
v1 := vars[0]
v2 := vars[1]
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.Div,
X: v1.id,
Y: v2.id,
ExtraId: 1,
})
return builder.addVar()
}

// Div returns the result of i1 divided by i2.
func (builder *builder) Div(i1, i2 frontend.Variable) frontend.Variable {
vars := builder.toVariables(i1, i2)
v1 := vars[0]
v2 := vars[1]
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.Div,
X: v1.id,
Y: v2.id,
ExtraId: 0,
})
return builder.addVar()
}

// Inverse returns the multiplicative inverse of the given variable.
func (builder *builder) Inverse(i1 frontend.Variable) frontend.Variable {
return builder.Div(1, i1)
}

// ---------------------------------------------------------------------------------------------
// Bit operations

// ToBinary unpacks a frontend.Variable in binary,
// n is the number of bits to select (starting from lsb)
// n default value is fr.Bits the number of bits needed to represent a field element
//
// The result in in little endian (first bit= lsb)
func (builder *builder) ToBinary(i1 frontend.Variable, n ...int) []frontend.Variable {
panic("unimplemented")
}

// FromBinary packs the given variables, seen as a fr.Element in little endian, into a single variable.
func (builder *builder) FromBinary(_b ...frontend.Variable) frontend.Variable {
return bits.FromBinary(builder, _b)
}

// Xor computes the logical XOR between two frontend.Variables.
func (builder *builder) Xor(_a, _b frontend.Variable) frontend.Variable {
vars := builder.toVariables(_a, _b)
a := vars[0]
b := vars[1]
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.BoolBinOp,
X: a.id,
Y: b.id,
ExtraId: 1,
})
return builder.addVar()
}

// Or computes the logical OR between two frontend.Variables.
func (builder *builder) Or(_a, _b frontend.Variable) frontend.Variable {
vars := builder.toVariables(_a, _b)
a := vars[0]
b := vars[1]
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.BoolBinOp,
X: a.id,
Y: b.id,
ExtraId: 2,
})
return builder.addVar()
}

// And computes the logical AND between two frontend.Variables.
func (builder *builder) And(_a, _b frontend.Variable) frontend.Variable {
vars := builder.toVariables(_a, _b)
a := vars[0]
b := vars[1]
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.BoolBinOp,
X: a.id,
Y: b.id,
ExtraId: 3,
})
return builder.addVar()
}

// ---------------------------------------------------------------------------------------------
// Conditionals

// Select yields the second variable if the first is true, otherwise yields the third variable.
func (builder *builder) Select(i0, i1, i2 frontend.Variable) frontend.Variable {
vars := builder.toVariables(i0, i1, i2)
cond := vars[0]

// ensures that cond is boolean
builder.AssertIsBoolean(cond)

v := builder.Sub(vars[1], vars[2]) // no constraint is recorded
w := builder.Mul(cond, v)
return builder.Add(w, vars[2])
}

// Lookup2 performs a 2-bit lookup based on the given bits and values.
func (builder *builder) Lookup2(b0, b1 frontend.Variable, i0, i1, i2, i3 frontend.Variable) frontend.Variable {
vars := builder.toVariables(b0, b1, i0, i1, i2, i3)
s0, s1 := vars[0], vars[1]
in0, in1, in2, in3 := vars[2], vars[3], vars[4], vars[5]

// ensure that bits are actually bits. Adds no constraints if the variables
// are already constrained.
builder.AssertIsBoolean(s0)
builder.AssertIsBoolean(s1)

// two-bit lookup for the general case can be done with three constraints as
// following:
// (1) (in3 - in2 - in1 + in0) * s1 = tmp1 - in1 + in0
// (2) tmp1 * s0 = tmp2
// (3) (in2 - in0) * s1 = RES - tmp2 - in0
// the variables tmp1 and tmp2 are new internal variables and the variables
// RES will be the returned result

tmp1 := builder.Add(in3, in0)
tmp1 = builder.Sub(tmp1, in2, in1)
tmp1 = builder.Mul(tmp1, s1)
tmp1 = builder.Add(tmp1, in1)
tmp1 = builder.Sub(tmp1, in0) // (1) tmp1 = s1 * (in3 - in2 - in1 + in0) + in1 - in0
tmp2 := builder.Mul(tmp1, s0) // (2) tmp2 = tmp1 * s0
res := builder.Sub(in2, in0)
res = builder.Mul(res, s1)
res = builder.Add(res, tmp2, in0) // (3) res = (v2 - v0) * s1 + tmp2 + in0
return res
}

// IsZero returns 1 if the given variable is zero, otherwise returns 0.
func (builder *builder) IsZero(i1 frontend.Variable) frontend.Variable {
a := builder.toVariable(i1)
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.IsZero,
X: a.id,
})
return builder.addVar()
}

// Cmp compares i1 and i2 and returns 1 if i1>i2, 0 if i1=i2, -1 if i1<i2.
func (builder *builder) Cmp(i1, i2 frontend.Variable) frontend.Variable {
nbBits := builder.field.FieldBitLen()
// in AssertIsLessOrEq we omitted comparison against modulus for the left
// side as if `a+r<b` implies `a<b`, then here we compute the inequality
// directly.
bi1 := bits.ToBinary(builder, i1, bits.WithNbDigits(nbBits))
bi2 := bits.ToBinary(builder, i2, bits.WithNbDigits(nbBits))

res := builder.toVariable(0)

for i := builder.field.FieldBitLen() - 1; i >= 0; i-- {

iszeroi1 := builder.IsZero(bi1[i])
iszeroi2 := builder.IsZero(bi2[i])

i1i2 := builder.And(bi1[i], iszeroi2)
i2i1 := builder.And(bi2[i], iszeroi1)

n := builder.Select(i2i1, -1, 0)
m := builder.Select(i1i2, 1, n)

res = builder.Select(builder.IsZero(res), m, res).(variable)

}
return res
}

// Println is not implemented and will panic if called.
func (builder *builder) Println(a ...frontend.Variable) {
panic("unimplemented")
}

// Compiler returns itself as it implements the frontend.Compiler interface.
func (builder *builder) Compiler() frontend.Compiler {
return builder
}

// Commit is faulty in its current implementation as it merely returns a compile-time random number.
func (builder *builder) Commit(v ...frontend.Variable) (frontend.Variable, error) {
vars := builder.toVariables(v...)
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.Commit,
Inputs: unwrapVariables(vars),
})
return builder.addVar(), nil
}

// SetGkrInfo is not implemented and will panic if called.
func (builder *builder) SetGkrInfo(info constraint.GkrInfo) error {
panic("unimplemented")
}

// Output adds the given variable to the circuit's output.
func (builder *builder) Output(x_ frontend.Variable) {
if builder.root.builder != builder {
panic("Output can only be called on root circuit")
}
x := builder.toVariable(x_)
builder.output = append(builder.output, x)
}
Loading

0 comments on commit a188643

Please sign in to comment.