* first (non working) version of random access gate * fixed a bug * got random access gate workgin * added parsing logic for the random access gatemain
@ -0,0 +1,140 @@ |
|||||
|
package plonky2_verifier |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
. "gnark-plonky2-verifier/field" |
||||
|
) |
||||
|
|
||||
|
type RandomAccessGate struct { |
||||
|
bits uint64 |
||||
|
numCopies uint64 |
||||
|
numExtraConstants uint64 |
||||
|
} |
||||
|
|
||||
|
func NewRandomAccessGate(bits uint64, numCopies uint64, numExtraConstants uint64) *RandomAccessGate { |
||||
|
return &RandomAccessGate{ |
||||
|
bits: bits, |
||||
|
numCopies: numCopies, |
||||
|
numExtraConstants: numExtraConstants, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) Id() string { |
||||
|
return fmt.Sprintf("RandomAccessGate { bits: %d, num_copies: %d, num_extra_constants: %d }", g.bits, g.numCopies, g.numExtraConstants) |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) vecSize() uint64 { |
||||
|
return 1 << g.bits |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) WireAccessIndex(copy uint64) uint64 { |
||||
|
if copy >= g.numCopies { |
||||
|
panic("RandomAccessGate.WireAccessIndex called with copy >= num_copies") |
||||
|
} |
||||
|
return (2 + g.vecSize()) * copy |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) WireClaimedElement(copy uint64) uint64 { |
||||
|
if copy >= g.numCopies { |
||||
|
panic("RandomAccessGate.WireClaimedElement called with copy >= num_copies") |
||||
|
} |
||||
|
|
||||
|
return (2+g.vecSize())*copy + 1 |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) WireListItem(i uint64, copy uint64) uint64 { |
||||
|
if i >= g.vecSize() { |
||||
|
panic("RandomAccessGate.WireListItem called with i >= vec_size") |
||||
|
} |
||||
|
if copy >= g.numCopies { |
||||
|
panic("RandomAccessGate.WireListItem called with copy >= num_copies") |
||||
|
} |
||||
|
|
||||
|
return (2+g.vecSize())*copy + 2 + i |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) startExtraConstants() uint64 { |
||||
|
return (2 + g.vecSize()) * g.numCopies |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) wireExtraConstant(i uint64) uint64 { |
||||
|
if i >= g.numExtraConstants { |
||||
|
panic("RandomAccessGate.wireExtraConstant called with i >= num_extra_constants") |
||||
|
} |
||||
|
|
||||
|
return g.startExtraConstants() + i |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) NumRoutedWires() uint64 { |
||||
|
return g.startExtraConstants() + g.numExtraConstants |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) WireBit(i uint64, copy uint64) uint64 { |
||||
|
if i >= g.bits { |
||||
|
panic("RandomAccessGate.WireBit called with i >= bits") |
||||
|
} |
||||
|
if copy >= g.numCopies { |
||||
|
panic("RandomAccessGate.WireBit called with copy >= num_copies") |
||||
|
} |
||||
|
|
||||
|
return g.NumRoutedWires() + copy*g.bits + i |
||||
|
} |
||||
|
|
||||
|
func (g *RandomAccessGate) EvalUnfiltered(p *PlonkChip, vars EvaluationVars) []QuadraticExtension { |
||||
|
two := QuadraticExtension{NewFieldElement(2), NewFieldElement(0)} |
||||
|
constraints := []QuadraticExtension{} |
||||
|
|
||||
|
for copy := uint64(0); copy < g.numCopies; copy++ { |
||||
|
accessIndex := vars.localWires[g.WireAccessIndex(copy)] |
||||
|
listItems := []QuadraticExtension{} |
||||
|
for i := uint64(0); i < g.vecSize(); i++ { |
||||
|
listItems = append(listItems, vars.localWires[g.WireListItem(i, copy)]) |
||||
|
} |
||||
|
claimedElement := vars.localWires[g.WireClaimedElement(copy)] |
||||
|
bits := []QuadraticExtension{} |
||||
|
for i := uint64(0); i < g.bits; i++ { |
||||
|
bits = append(bits, vars.localWires[g.WireBit(i, copy)]) |
||||
|
} |
||||
|
|
||||
|
// Assert that each bit wire value is indeed boolean.
|
||||
|
for _, b := range bits { |
||||
|
bSquared := p.qeAPI.MulExtension(b, b) |
||||
|
constraints = append(constraints, p.qeAPI.SubExtension(bSquared, b)) |
||||
|
} |
||||
|
|
||||
|
// Assert that the binary decomposition was correct.
|
||||
|
reconstructedIndex := p.qeAPI.ReduceWithPowers(bits, two) |
||||
|
constraints = append(constraints, p.qeAPI.SubExtension(reconstructedIndex, accessIndex)) |
||||
|
|
||||
|
for _, b := range bits { |
||||
|
listItemsTmp := []QuadraticExtension{} |
||||
|
for i := 0; i < len(listItems); i += 2 { |
||||
|
x := listItems[i] |
||||
|
y := listItems[i+1] |
||||
|
|
||||
|
// This is computing `if b { x } else { y }`
|
||||
|
// i.e. `bx - (by-y)`.
|
||||
|
mul1 := p.qeAPI.MulExtension(b, x) |
||||
|
sub1 := p.qeAPI.SubExtension(mul1, x) |
||||
|
|
||||
|
mul2 := p.qeAPI.MulExtension(b, y) |
||||
|
sub2 := p.qeAPI.SubExtension(mul2, sub1) |
||||
|
|
||||
|
listItemsTmp = append(listItemsTmp, sub2) |
||||
|
} |
||||
|
listItems = listItemsTmp |
||||
|
} |
||||
|
|
||||
|
if len(listItems) != 1 { |
||||
|
panic("listItems(len) != 1") |
||||
|
} |
||||
|
|
||||
|
constraints = append(constraints, p.qeAPI.SubExtension(listItems[0], claimedElement)) |
||||
|
} |
||||
|
|
||||
|
for i := uint64(0); i < g.numExtraConstants; i++ { |
||||
|
constraints = append(constraints, p.qeAPI.SubExtension(vars.localConstants[i], vars.localWires[g.wireExtraConstant(i)])) |
||||
|
} |
||||
|
|
||||
|
return constraints |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
package plonky2_verifier |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
. "gnark-plonky2-verifier/field" |
||||
|
"testing" |
||||
|
|
||||
|
"github.com/consensys/gnark/frontend" |
||||
|
"github.com/consensys/gnark/test" |
||||
|
) |
||||
|
|
||||
|
type TestRandomAccessGateCircuit struct{} |
||||
|
|
||||
|
func (circuit *TestRandomAccessGateCircuit) Define(api frontend.API) error { |
||||
|
commonCircuitData := DeserializeCommonCircuitData("./data/step/common_circuit_data.json") |
||||
|
numSelectors := len(commonCircuitData.SelectorsInfo.groups) |
||||
|
|
||||
|
fieldAPI := NewFieldAPI(api) |
||||
|
qeAPI := NewQuadraticExtensionAPI(fieldAPI, commonCircuitData.DegreeBits) |
||||
|
plonkChip := NewPlonkChip(api, qeAPI, commonCircuitData) |
||||
|
|
||||
|
randomAccessGate := RandomAccessGate{bits: 4, numCopies: 4, numExtraConstants: 2} |
||||
|
vars := EvaluationVars{localConstants: localConstants[numSelectors:], localWires: localWires, publicInputsHash: publicInputsHash} |
||||
|
|
||||
|
constraints := randomAccessGate.EvalUnfiltered(plonkChip, vars) |
||||
|
|
||||
|
if len(constraints) != len(randomAccessGateExpectedConstraints) { |
||||
|
return errors.New("constant gate constraints length mismatch") |
||||
|
} |
||||
|
|
||||
|
for i := 0; i < len(constraints); i++ { |
||||
|
qeAPI.AssertIsEqual(constraints[i], randomAccessGateExpectedConstraints[i]) |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func TestRandomAccessGate(t *testing.T) { |
||||
|
assert := test.NewAssert(t) |
||||
|
|
||||
|
testCase := func() { |
||||
|
circuit := TestRandomAccessGateCircuit{} |
||||
|
witness := TestRandomAccessGateCircuit{} |
||||
|
err := test.IsSolved(&circuit, &witness, TEST_CURVE.ScalarField()) |
||||
|
assert.NoError(err) |
||||
|
} |
||||
|
|
||||
|
testCase() |
||||
|
} |