Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new native contract to support ec curve operations and zk-SNARK verification #808

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions smartcontract/service/native/snark/ecc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright (C) 2018 The ontology Authors
* This file is part of The ontology library.
*
* The ontology is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ontology is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with The ontology. If not, see <http://www.gnu.org/licenses/>.
*/

package snark

import (
"errors"
"math/big"

"github.com/kunxian-xia/bn256"
"github.com/ontio/ontology/common"
"github.com/ontio/ontology/smartcontract/service/native"
"github.com/ontio/ontology/smartcontract/service/native/utils"
)

func ECAdd(ns *native.NativeService) ([]byte, error) {
var err error
source := common.NewZeroCopySource(ns.Input)
a := new(bn256.G1)
err = deserializeG1(a, source)
if err != nil {
return nil, err
}
b := new(bn256.G1)
err = deserializeG1(b, source)
if err != nil {
return nil, err
}

c := new(bn256.G1).Add(a, b)
return c.Marshal(), nil
}

func TwistECAdd(ns *native.NativeService) ([]byte, error) {
var err error
source := common.NewZeroCopySource(ns.Input)
a := new(bn256.G2)
err = deserializeG2(a, source)
if err != nil {
return nil, err
}
b := new(bn256.G2)
err = deserializeG2(b, source)
if err != nil {
return nil, err
}

c := new(bn256.G2).Add(a, b)
return c.Marshal(), nil
}

func ECMul(ns *native.NativeService) ([]byte, error) {
var err error
source := common.NewZeroCopySource(ns.Input)
p := new(bn256.G1)

err = deserializeG1(p, source)
if err != nil {
return nil, err
}
kBytes, eof := source.NextBytes(source.Len())
if eof {
return nil, errors.New("read k failed: eof")
}
k := new(big.Int).SetBytes(kBytes)
q := new(bn256.G1).ScalarMult(p, k)
return q.Marshal(), nil
}

func TwistECMul(ns *native.NativeService) ([]byte, error) {
var err error
source := common.NewZeroCopySource(ns.Input)
p := new(bn256.G2)

err = deserializeG2(p, source)
if err != nil {
return nil, err
}
kBytes, eof := source.NextBytes(source.Len())
if eof {
return nil, errors.New("read k failed: eof")
}
k := new(big.Int).SetBytes(kBytes)
q := new(bn256.G2).ScalarMult(p, k)
return q.Marshal(), nil
}

func PairingCheck(ns *native.NativeService) ([]byte, error) {
var err error
if len(ns.Input)%(g1Size+g2Size) != 0 {
return nil, errors.New("length of input is not a multiple of 192")
}
k := len(ns.Input) / (g1Size + g2Size)
source := common.NewZeroCopySource(ns.Input)

pointG1s := make([]*bn256.G1, k)
pointG2s := make([]*bn256.G2, k)
for i := 0; i < k; i++ {
err = deserializeG1(pointG1s[i], source)
if err != nil {
return nil, err
}
err = deserializeG2(pointG2s[i], source)
if err != nil {
return nil, err
}
}
if !bn256.PairingCheck(pointG1s, pointG2s) {
return utils.BYTE_FALSE, nil
} else {
return utils.BYTE_TRUE, nil
}
}
239 changes: 239 additions & 0 deletions smartcontract/service/native/snark/phgr13.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/*
* Copyright (C) 2018 The ontology Authors
* This file is part of The ontology library.
*
* The ontology is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ontology is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with The ontology. If not, see <http://www.gnu.org/licenses/>.
*/

package snark

import (
"errors"
"math/big"

"github.com/kunxian-xia/bn256"
"github.com/ontio/ontology/common"
"github.com/ontio/ontology/common/log"
"github.com/ontio/ontology/smartcontract/service/native"
"github.com/ontio/ontology/smartcontract/service/native/utils"
)

type phgr13VerifyingKey struct {
icLen uint64
a *bn256.G2 // alphaA*G2
b *bn256.G1 // alphaB*G1
c *bn256.G2 // alphaC*G2
gamma *bn256.G2 // gamma*G2
gammaBeta1 *bn256.G1
gammaBeta2 *bn256.G2
z *bn256.G2
ic []*bn256.G1
}

func (vk *phgr13VerifyingKey) Deserialize(source *common.ZeroCopySource) error {
var err error
var eof bool

vk.icLen, eof = source.NextUint64()
if eof {
err = errors.New("input's length is less than required")
return err
}

err = deserializeG2(vk.a, source)
if err != nil {
return err
}
err = deserializeG1(vk.b, source)
if err != nil {
return err
}
err = deserializeG2(vk.c, source)
if err != nil {
return err
}
err = deserializeG2(vk.gamma, source)
if err != nil {
return err
}
err = deserializeG1(vk.gammaBeta1, source)
if err != nil {
return err
}
err = deserializeG2(vk.gammaBeta2, source)
if err != nil {
return err
}
err = deserializeG2(vk.z, source)
if err != nil {
return err
}

if source.Len() < vk.icLen*g1Size {
return errors.New("input's length is less than required")
}
vk.ic = make([]*bn256.G1, vk.icLen)
for i := uint64(0); i < vk.icLen; i++ {
err = deserializeG1(vk.ic[i], source)
if err != nil {
return err
}
}
return nil
}

type phgr13Proof struct {
a *bn256.G1
aPrime *bn256.G1
b *bn256.G2
bPrime *bn256.G1
c *bn256.G1
cPrime *bn256.G1
k *bn256.G1
h *bn256.G1
}

func (proof *phgr13Proof) Deserialize(source *common.ZeroCopySource) error {
var err error

err = deserializeG1(proof.a, source)
if err != nil {
return err
}
err = deserializeG1(proof.aPrime, source)
if err != nil {
return err
}
err = deserializeG2(proof.b, source)
if err != nil {
return err
}
err = deserializeG1(proof.bPrime, source)
if err != nil {
return err
}
err = deserializeG1(proof.c, source)
if err != nil {
return err
}
err = deserializeG1(proof.cPrime, source)
if err != nil {
return err
}
err = deserializeG1(proof.k, source)
if err != nil {
return err
}
err = deserializeG1(proof.h, source)
if err != nil {
return err
}
return nil
}

func verify(vk *phgr13VerifyingKey, proof *phgr13Proof, input []*big.Int) (bool, error) {
if len(input)+1 != len(vk.ic) {
return false, errors.New("len(input) + 1 != len(vk.ic)")
}
vkX := vk.ic[0]
for i := 0; i < len(input); i++ {
vkX.Add(vkX, new(bn256.G1).ScalarMult(vk.ic[i+1], input[i]))
}

// p1 := new(bn256.G1).ScalarBaseMult(big.NewInt(1))
p2 := new(bn256.G2).ScalarBaseMult(big.NewInt(1))

if !bn256.PairingCheck([]*bn256.G1{proof.a, new(bn256.G1).Neg(proof.aPrime)},
[]*bn256.G2{vk.a, p2}) {
log.Error("knowledge commitments condition a failed")
return false, nil
}
if !bn256.PairingCheck([]*bn256.G1{vk.b, new(bn256.G1).Neg(proof.bPrime)},
[]*bn256.G2{proof.b, p2}) {
log.Error("knowledge commitments condition b failed")
return false, nil
}
if !bn256.PairingCheck([]*bn256.G1{proof.c, new(bn256.G1).Neg(proof.cPrime)},
[]*bn256.G2{vk.c, p2}) {
log.Error("knowledge commitments condition c failed")
return false, nil
}

vkxPlusAPlusC := new(bn256.G1).Add(vkX, proof.a)
vkxPlusAPlusC.Add(vkxPlusAPlusC, proof.c)

if !bn256.PairingCheck([]*bn256.G1{proof.k, new(bn256.G1).Neg(vkxPlusAPlusC),
new(bn256.G1).Neg(vk.gammaBeta1)}, []*bn256.G2{vk.gamma, vk.gammaBeta2, proof.b}) {
log.Error("same coefficient condition failed")
return false, nil
}

if !bn256.PairingCheck([]*bn256.G1{new(bn256.G1).Add(vkX, proof.a),
new(bn256.G1).Neg(proof.h), new(bn256.G1).Neg(proof.c)},
[]*bn256.G2{proof.b, vk.z, p2}) {
log.Error("qap divisibility condition failed")
return false, nil
}
return true, nil
}

// PHGR13Verify ...
func PHGR13Verify(ns *native.NativeService) ([]byte, error) {
var err error
// inputs consists of (vk, proof, public input)
source := common.NewZeroCopySource(ns.Input)

// deserialize vk
vk := new(phgr13VerifyingKey)
err = vk.Deserialize(source)
if err != nil {
return nil, err
}

// deserialize proof
proof := new(phgr13Proof)
err = proof.Deserialize(source)
if err != nil {
return nil, err
}

// deserialize public input
// public input is a vector of field elements
fieldsNum, eof := source.NextUint64()
if eof {
return nil, errors.New("eof when deserialize number of field elements in public input")
}
if source.Len() < fieldsNum*fieldSize {
return nil, errors.New("input's length is less than required")
}
input := make([]*big.Int, fieldsNum)
for i := uint64(0); i < fieldsNum; i++ {
bytes, eof := source.NextBytes(fieldSize)
if eof {
return nil, errors.New("encounter eof when deserialize field element")
}
input[i] = new(big.Int).SetBytes(bytes)
}

// do the actual zk-SNARKs verification
valid, err := verify(vk, proof, input)
if err != nil {
return utils.BYTE_FALSE, err
}
if !valid {
return utils.BYTE_FALSE, nil
} else {
return utils.BYTE_TRUE, nil
}
}
Loading