diff --git a/pkg/core/curves/native/bls12381/g1.go b/pkg/core/curves/native/bls12381/g1.go index a35d4adf..5bd16427 100644 --- a/pkg/core/curves/native/bls12381/g1.go +++ b/pkg/core/curves/native/bls12381/g1.go @@ -781,6 +781,28 @@ func (g1 *G1) BigInt() (x, y *big.Int) { return } +// SetRaw creates a point from raw arrays x, y +// and returns the point if it is on the curve +func (g1 *G1) SetRaw(x, y *[Limbs]uint64) (*G1, error) { + var xx, yy fp + var pp G1 + pp.x = *(xx.SetRaw(x)) + pp.y = *(yy.SetRaw(y)) + + if pp.x.IsZero()&pp.y.IsZero() == 1 { + pp.Identity() + return g1.Set(&pp), nil + } + + pp.z.SetOne() + + // If not the identity point and not on the curve then invalid + if (pp.IsOnCurve()&pp.InCorrectSubgroup())|(xx.IsZero()&yy.IsZero()) == 0 { + return nil, fmt.Errorf("invalid coordinates") + } + return g1.Set(&pp), nil +} + // SetBigInt creates a point from affine x, y // and returns the point if it is on the curve func (g1 *G1) SetBigInt(x, y *big.Int) (*G1, error) { diff --git a/pkg/core/curves/native/bls12381/g2.go b/pkg/core/curves/native/bls12381/g2.go index f0b18ab8..bd622f00 100644 --- a/pkg/core/curves/native/bls12381/g2.go +++ b/pkg/core/curves/native/bls12381/g2.go @@ -762,6 +762,26 @@ func (g2 *G2) BigInt() (x, y *big.Int) { return } +// SetRaw creates a point from raw arrays x, y +// and returns the point if it is on the curve +func (g2 *G2) SetRaw(xc0, xc1, yc0, yc1 *[Limbs]uint64) (*G2, error) { + var p G2 + p.x.A.SetRaw(xc0) + p.x.B.SetRaw(xc1) + p.y.A.SetRaw(yc0) + p.y.B.SetRaw(yc1) + p.z.SetOne() + + if p.IsIdentity() == 1 { + return g2.Identity(), nil + } + + if p.IsOnCurve() == 0 { + return nil, errors.New("point is not on the curve") + } + return g2.Set(&p), nil +} + // SetBigInt creates a point from affine x, y // and returns the point if it is on the curve func (g2 *G2) SetBigInt(x, y *big.Int) (*G2, error) { diff --git a/pkg/zkp/kzg/kate.go b/pkg/zkp/kzg/kate.go new file mode 100644 index 00000000..276dd700 --- /dev/null +++ b/pkg/zkp/kzg/kate.go @@ -0,0 +1,221 @@ +package kzg + +import ( + crand "crypto/rand" + "fmt" + "math/big" + + "github.com/coinbase/kryptology/pkg/core/curves/native" + bls "github.com/coinbase/kryptology/pkg/core/curves/native/bls12381" + "github.com/pkg/errors" +) + +var ( + MAX_BIG = new(big.Int) +) + +func init() { + MAX_BIG.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(MAX_BIG, big.NewInt(1)) +} + +type KZGSetupParamaters struct { + TG1 []*bls.G1 + TG2 []*bls.G2 +} + +// NewKZGSetupParamaters returns the kzg10 setup paramaters +func NewKZGSetupParamaters(tg1 []*bls.G1, tg2 []*bls.G2) (*KZGSetupParamaters, error) { + if len(tg1) != len(tg2) { + return nil, errors.New("Number of elements in each group must match") + } + return &KZGSetupParamaters{ + TG1: tg1, + TG2: tg2, + }, nil +} + +// Setup performs an untrusted ceremony and returns the kzg10 setup paramaters +func Setup(t int) ([]*bls.G1, []*bls.G2, error) { + fmt.Printf("WARNING: This is not a secure trusted setup, do not use in a production environment\n") + + if t < 0 { + return nil, nil, fmt.Errorf("reference string degree must be positive") + } + + // α: "hidden" number no one should know + a, err := crand.Int(crand.Reader, MAX_BIG) + if err != nil { + return nil, nil, errors.Wrap(err, "unable to create system paramaters") + } + alpha := bls.Bls12381FqNew().SetBigInt(a) + + sg1 := make([]*bls.G1, t) + sg2 := make([]*bls.G2, t) + + // Evaluate point multiplication of progressive powers of alpha + // [g, g^(α^1), ..., g^(α^t)] + for i := 0; i < t; i++ { + sg1[i] = new(bls.G1).Mul(new(bls.G1).Generator(), alpha.Exp(alpha, bls.Bls12381FqNew().SetUint64(uint64(i)))) + sg2[i] = new(bls.G2).Mul(new(bls.G2).Generator(), alpha.Exp(alpha, bls.Bls12381FqNew().SetUint64(uint64(i)))) + } + + return sg1, sg2, nil +} + +// EvalPolyG1 evaluates a polynomial in g1 using the setup paramaters +func EvalPolyG1(rs *KZGSetupParamaters, p *Polynomial) (*bls.G1, error) { + + // Confirm that polynomial is valid + if p.Order() == -1 { + return nil, errors.New("Cannot evaluate polynomial, must have at least one coefficients") + } + if p.Order()+1 > len(rs.TG1) { + return nil, errors.New("Cannot evaluate polynomial of higher degree than reference string elements") + } + + // Evalute polynomial in the exponent of the reference string + // g ^ ( a_0 + a_1 * α ^ 1 + a_2 * α ^ 2 + ... + a_n * α ^ n) + result := new(bls.G1).Identity() + for i, c := range p.Coefficients { + result = result.Add(result, new(bls.G1).Mul(rs.TG1[i], bls.Bls12381FqNew().Set(c))) + } + return result, nil +} + +// EvalPolyG2 evaluates a polynomial in g2 using the setup paramaters +func EvalPolyG2(rs *KZGSetupParamaters, p *Polynomial) (*bls.G2, error) { + + // Confirm that polynomial is valid + if p.Order() == -1 { + return nil, errors.New("Cannot evaluate polynomial, must have at least one coefficients") + } + if p.Order()+1 > len(rs.TG2) { + return nil, errors.New("Cannot evaluate polynomial of higher degree than reference string elements") + } + + // Evalute polynomial in the exponent of the reference string + // g ^ ( a_0 + a_1 * α ^ 1 + a_2 * α ^ 2 + ... + a_n * α ^ n) + result := new(bls.G2).Identity() + for i, c := range p.Coefficients { + result = result.Add(result, new(bls.G2).Mul(rs.TG2[i], bls.Bls12381FqNew().Set(c))) + } + return result, nil +} + +// Commit converts a polynomial to a point in g1 by evaluation with the setup paramaters +func Commit(rs *KZGSetupParamaters, p *Polynomial) (*bls.G1, error) { + polyEvalG1, err := EvalPolyG1(rs, p) + if err != nil { + return nil, err + } + return polyEvalG1, nil +} + +// VerifyPoly returns 1 if the commitment matches an evaluated polynomial, 0 otherwise +func VerifyPoly(rs *KZGSetupParamaters, c *bls.G1, p *Polynomial) (int, error) { + pc, err := Commit(rs, p) + if err != nil { + return 0, err + } + return pc.Equal(c), nil +} + +// CreateWitness creates an evaluation proof of a point on a polynomial as a g1 element +func CreateWitness(rs *KZGSetupParamaters, p *Polynomial, x, phiX *native.Field) (*bls.G1, error) { + n := p.Sub(new(Polynomial).Set([]*native.Field{phiX})) // p - phi(x) + d := new(Polynomial).Set( // x - z + []*native.Field{ + bls.Bls12381FqNew().Neg(x), + bls.Bls12381FqNew().SetOne(), + }, + ) + + // q = ( p - phi(x) ) / (x - z) + q, r := n.Div(d) + if !r.IsZero() { + return nil, + fmt.Errorf("remainder must be 0") + } + + // proof = eval(q) + e, err := EvalPolyG1(rs, q) + if err != nil { + return nil, err + } + return e, nil +} + +// VerifyEval checks the commitment, proof, input point, and evaluation coincide via pairings +func VerifyEval(rs *KZGSetupParamaters, commitment, proof *bls.G1, x, y *native.Field) int { + + // g ^ ( α - x ) + gmx := new(bls.G2).Neg(new(bls.G2).Mul(new(bls.G2).Generator(), x)) + gamx := new(bls.G2).Add(rs.TG2[1], gmx) + + // g ^ ( f(α) - f(x) ) + gmfx := new(bls.G1).Neg(new(bls.G1).Mul(new(bls.G1).Generator(), y)) + gfamfx := new(bls.G1).Add(commitment, gmfx) + + // e(proof,g^(α-x)) == e(g^(f(α) - f(x)),g) + p1 := new(bls.Engine) + p2 := new(bls.Engine) + e1 := p1.AddPair(proof, gamx).Result() + e2 := p2.AddPair(gfamfx, new(bls.G2).Generator()).Result() + return e1.Equal(e2) +} + +// CreateWitnessBatch creates an evaluation proof of multiple points on a polynomial as a g1 element +func CreateWitnessBatch(rs *KZGSetupParamaters, p *Polynomial, x, phiX []*native.Field) (*bls.G1, error) { + + // I(x) + ix, err := CreateLagrangePolynomial(x, phiX) + if err != nil { + return nil, err + } + + // q(x) = ( p(x) - I(x) ) / z(x) + q, r := p.Sub(ix).Div(CreateZeroPolynomial(x)) + if !r.IsZero() { + return nil, + fmt.Errorf("remainder must be 0") + } + + // proof = eval(q(x)) + e, err := EvalPolyG1(rs, q) + if err != nil { + return nil, err + } + return e, nil +} + +// VerifyEvalBatch checks the commitment, proof, input points, and evaluation coincide via pairings +func VerifyEvalBatch(rs *KZGSetupParamaters, commitment, proof *bls.G1, z, y []*native.Field) (int, error) { + + // I(x) + ix, err := CreateLagrangePolynomial(z, y) + if err != nil { + return 0, err + } + + // f(I(x)) + fiG1, err := EvalPolyG1(rs, ix) + if err != nil { + return 0, err + } + + // f(z(x)) + fzG2, err := EvalPolyG2(rs, CreateZeroPolynomial(z)) + if err != nil { + return 0, err + } + + // c - f(I(x)) + cmit := new(bls.G1).Add(commitment, new(bls.G1).Neg(fiG1)) + + // e(proof, f(z(x))) == e(c - f(I(x)), g) + p1 := new(bls.Engine) + p2 := new(bls.Engine) + e1 := p1.AddPair(proof, fzG2).Result() + e2 := p2.AddPair(cmit, new(bls.G2).Generator()).Result() + return e1.Equal(e2), nil +} diff --git a/pkg/zkp/kzg/kate_test.go b/pkg/zkp/kzg/kate_test.go new file mode 100644 index 00000000..b1191319 --- /dev/null +++ b/pkg/zkp/kzg/kate_test.go @@ -0,0 +1,430 @@ +package kzg + +import ( + "testing" + + "github.com/coinbase/kryptology/pkg/core/curves/native" + bls "github.com/coinbase/kryptology/pkg/core/curves/native/bls12381" + "github.com/stretchr/testify/require" +) + +// powersOfTau outputs the first few paramaters of the Powers of Tau +// from the Zcash Sapling MPC ceremony +// +// Paramaters: +// https://archive.org/download/transcript_201804/transcript +// +// Code: +// https://github.com/ebfull/powersoftau +// +// Attestations (for transcript verification): +// https://github.com/ZcashFoundation/powersoftau-attestations/ +func powersOfTau() ([]*bls.G1, []*bls.G2, error) { + + // This is just the G1 generator, g^(α^0) = g + tau1G1, err := new(bls.G1).SetRaw( + &[bls.Limbs]uint64{ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, + }, + &[bls.Limbs]uint64{ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0xe1c8c3fad0059c0, + 0xbbc3efc5008a26a, + }) + if err != nil { + return nil, nil, err + } + + tau2G1, err := new(bls.G1).SetRaw( + &[bls.Limbs]uint64{ + 0xb1c4001f43ae605c, + 0xa3259c7580e64c19, + 0x35a375eb1c6b9758, + 0x56398c8240e84f1a, + 0xd0674229a30b6f12, + 0xc24de091cff2040, + }, + &[bls.Limbs]uint64{ + 0xfef01719660b817f, + 0x3943aa777d478029, + 0x6a21d5aadb805014, + 0x6b70779aae4bfeae, + 0xc60ae2121bd27062, + 0x11608bd86899c384, + }) + if err != nil { + return nil, nil, err + } + + tau3G1, err := new(bls.G1).SetRaw( + &[bls.Limbs]uint64{ + 0x57844fae301dad5b, + 0x6f12225a86af175f, + 0x39fa6ead21492fb7, + 0x572be66fcc7196dc, + 0x4e3ac15a95ae4189, + 0x188ae4aa9c4c4d90, + }, + &[bls.Limbs]uint64{ + 0xefd841545b412395, + 0x4342a395566f6d9, + 0x7fd75a271ae69a23, + 0xb7f6dc3b17acf301, + 0xb6f1810855b7341d, + 0x7cdbdaae73dd65c, + }) + if err != nil { + return nil, nil, err + } + + tau4G1, err := new(bls.G1).SetRaw( + &[bls.Limbs]uint64{ + 0xb64f1c70030b3b7f, + 0x17aeaa6e95d48b0e, + 0x17df2ad41819165c, + 0x2d5ec7a8faee782c, + 0x225af243cd6cf0a3, + 0x1008968d4e6b5e97, + }, + &[bls.Limbs]uint64{ + 0xfd59d9e9a1c5a43f, + 0xd60b040b6034d94b, + 0x475800d82c3416be, + 0x279ad48ff205573e, + 0xa0085fc33ba625e, + 0x17dc30c37a15b4a9, + }) + if err != nil { + return nil, nil, err + } + + tau1G2, err := new(bls.G2).SetRaw( + &[bls.Limbs]uint64{ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x58191924350bcd7, + }, + &[bls.Limbs]uint64{ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, + }, + &[bls.Limbs]uint64{ + 0x4c730af860494c4a, + 0x597cfa1f5e369c5a, + 0xe7e6856caa0a635a, + 0xbbefb5e96e0d495f, + 0x7d3a975f0ef25a2, + 0x83fd8e7e80dae5, + }, + &[bls.Limbs]uint64{ + 0xadc0fc92df64b05d, + 0x18aa270a2b1461dc, + 0x86adac6a3be4eba0, + 0x79495c4ec93da33a, + 0xe7175850a43ccaed, + 0xb2bc2a163de1bf2, + }) + if err != nil { + return nil, nil, err + } + + tau2G2, err := new(bls.G2).SetRaw( + &[bls.Limbs]uint64{ + 0xffa0385428fec3b, + 0x510e7ec541363eb2, + 0xf13a94ca2c2d416a, + 0x7fc5f5d9562339c0, + 0x7af592386dd863a0, + 0x18593bbbb413f4ae, + }, + &[bls.Limbs]uint64{ + 0x10c324d1c2da9496, + 0x1844dd0fe7a63725, + 0x6d579c341a22077e, + 0x540042d2463f25ec, + 0x7705869256c8c705, + 0x5e25cc82f956cef, + }, + &[bls.Limbs]uint64{ + 0x83ede256224bf8dc, + 0x5b1ec84cc0d00651, + 0x454e5fc13223a92a, + 0xe4b98a3823ce7167, + 0x72edd916ce4b0dea, + 0x141c08d488fa3db8, + }, + &[bls.Limbs]uint64{ + 0xfdb54d422ee52dd3, + 0xf591f71cbeffcd3b, + 0xfcf4f200ba0a3dc2, + 0x6556768a95fb0a5c, + 0x9b8119e0d7555f16, + 0xce9a4935faa25ac, + }) + if err != nil { + return nil, nil, err + } + + tau3G2, err := new(bls.G2).SetRaw( + &[bls.Limbs]uint64{ + 0xbce1a6e1b4cc4e49, + 0x5f52d58f4d9045ce, + 0x51b75755bb4840fc, + 0xb14ccbdfbfae59ac, + 0xb568776a116083f1, + 0x830e2515553f995, + }, + &[bls.Limbs]uint64{ + 0x47b3efe5c1b422b5, + 0xaf5e1a16ec5acff6, + 0x63ad7b40247c15a9, + 0x878b700492359269, + 0x6dee8a04ec81e17b, + 0xb9153833d1f902d, + }, + &[bls.Limbs]uint64{ + 0xbc37454e5592a41a, + 0x5f63d5a84099e9a1, + 0x2ef53a21150565e6, + 0x99d7334f4ffd75da, + 0xba8ddcc454795953, + 0x5aa83390eb0f93d, + }, + &[bls.Limbs]uint64{ + 0xf07e3e18aa311fba, + 0xaece70f00b4a1b52, + 0x5051ea3658aae5bb, + 0x11c54ec96ee7474f, + 0x84b337d1bf6e7a80, + 0x197670e7cde405c2, + }) + if err != nil { + return nil, nil, err + } + + tau4G2, err := new(bls.G2).SetRaw( + &[bls.Limbs]uint64{ + 0xf92483b72c69f0c3, + 0x182b5ed886dfa8b4, + 0xa9781c23b13d06f3, + 0xb65aa8447a9be1ad, + 0xb0360062d2308c75, + 0x19dde52f452a35c2, + }, + &[bls.Limbs]uint64{ + 0x5ad71d69d27ed223, + 0x34b15bae6258f2e3, + 0x58454bbf3d8bb982, + 0x317b6440cf7d36a8, + 0xaf8a321a59868131, + 0x117659178a3ad24b, + }, + &[bls.Limbs]uint64{ + 0xbb4db259f864aeb4, + 0x416d7414441e8c06, + 0x15620f806beb6138, + 0xad961f75dc5ea34d, + 0x8136272b6535ec8c, + 0x1375718ec52b4595, + }, + &[bls.Limbs]uint64{ + 0x60bab8e3dd660c, + 0x3587481b8dbb8929, + 0x93b6539d30cda8c4, + 0xb9574e9f7d280546, + 0x23a727eab19cc17e, + 0x44d63ce28227873, + }) + if err != nil { + return nil, nil, err + } + + return []*bls.G1{tau1G1, tau2G1, tau3G1, tau4G1}, []*bls.G2{tau1G2, tau2G2, tau3G2, tau4G2}, nil +} + +func TestSetupGenerator(t *testing.T) { + a, b, err := Setup(5) + require.NoError(t, err, "Unable to complete setup") + require.Equal(t, 1, new(bls.G1).Generator().Equal(a[0])) + require.Equal(t, 1, new(bls.G2).Generator().Equal(b[0])) +} + +func TestCommitVerifyPoly(t *testing.T) { + a, b, err := Setup(3) + require.NoError(t, err, "Unable to complete setup") + + pk, err := NewKZGSetupParamaters(a, b) + require.NoError(t, err, "Unable to create new KZGSetupParamaters") + + poly1 := new(Polynomial).SetUInt64([]uint64{1, 2, 3}) + poly2 := new(Polynomial).SetUInt64([]uint64{1, 2, 4}) + + c, err := Commit(pk, poly1) + require.NoError(t, err, "Unable to perform commit") + + // Ensure polynomials can be verified + v_correct, err := VerifyPoly(pk, c, poly1) + require.NoError(t, err, "Unable to perform verify") + require.Equal(t, 1, v_correct) + v_incorrect, err := VerifyPoly(pk, c, poly2) + require.NoError(t, err, "Unable to perform verify") + require.Equal(t, 0, v_incorrect) +} + +func TestBadEval(t *testing.T) { + a, b, err := Setup(5) + require.NoError(t, err, "Unable to complete setup") + + pk, err := NewKZGSetupParamaters(a, b) + require.NoError(t, err, "Unable to create new KZGSetupParamaters") + + poly1 := new(Polynomial).SetUInt64([]uint64{1, 1, 1, 1, 1, 1}) + _, err = EvalPolyG1(pk, poly1) + require.Error(t, err) +} + +func TestCommitVerifyPolyTau(t *testing.T) { + tausG1, tausG2, err := powersOfTau() + require.NoError(t, err, "Unable to complete setup") + + pk, err := NewKZGSetupParamaters(tausG1, tausG2) + require.NoError(t, err, "Unable to create new KZGSetupParamaters") + + poly1 := new(Polynomial).SetUInt64([]uint64{1, 2, 3}) + poly2 := new(Polynomial).SetUInt64([]uint64{1, 2, 4}) + + c, err := Commit(pk, poly1) + require.NoError(t, err, "Unable to perform commit") + + // Ensure polynomials can be verified + v_correct, err := VerifyPoly(pk, c, poly1) + require.NoError(t, err, "Unable to perform verify") + require.Equal(t, 1, v_correct) + v_incorrect, err := VerifyPoly(pk, c, poly2) + require.NoError(t, err, "Unable to perform verify") + require.Equal(t, 0, v_incorrect) +} + +func TestSetupFlow(t *testing.T) { + a, b, err := Setup(10) + require.NoError(t, err, "Unable to perform setup") + pk, err := NewKZGSetupParamaters(a, b) + require.NoError(t, err, "Unable to create new KZGSetupParamaters") + + poly := new(Polynomial).SetUInt64([]uint64{0, 1, 0, 1, 0, 1}) + z := bls.Bls12381FqNew().SetUint64(3) + y := bls.Bls12381FqNew().SetUint64(273) + + c, err := Commit(pk, poly) + require.NoError(t, err, "Unable to perform commit") + + proof, err := CreateWitness(pk, poly, z, y) + require.NoError(t, err, "Unable to create evaluation proof") + + v := VerifyEval(pk, c, proof, z, y) + require.Equal(t, 1, v) +} + +func TestTauFlow(t *testing.T) { + tausG1, tausG2, err := powersOfTau() + require.NoError(t, err, "Unable to init powers of tau") + pk, err := NewKZGSetupParamaters(tausG1, tausG2) + require.NoError(t, err, "Unable to create new KZGSetupParamaters") + + poly := new(Polynomial).SetUInt64([]uint64{10, 1, 1, 5}) + x := bls.Bls12381FqNew().SetUint64(3) + y := bls.Bls12381FqNew().Set(poly.Evaluate(x)) + + c, err := Commit(pk, poly) + require.NoError(t, err, "Unable to perform commit") + + ver, err := VerifyPoly(pk, c, poly) + require.NoError(t, err, "Unable to perform polynomial verification") + require.Equal(t, 1, ver) + + proof, err := CreateWitness(pk, poly, x, y) + require.NoError(t, err, "Unable to create witness of evaluation") + + v := VerifyEval(pk, c, proof, x, y) + require.Equal(t, 1, v) + + fakeProof := new(bls.G1).Mul(new(bls.G1).Generator(), bls.Bls12381FqNew().SetUint64(123)) + + v2 := VerifyEval(pk, c, fakeProof, x, y) + require.Equal(t, 0, v2) + + yWrong := bls.Bls12381FqNew().Add(y, bls.Bls12381FqNew().SetUint64(321)) + v3 := VerifyEval(pk, c, fakeProof, x, yWrong) + require.Equal(t, 0, v3) +} + +func TestTauBatchFlow(t *testing.T) { + tausG1, tausG2, err := powersOfTau() + require.NoError(t, err, "Unable to init powers of tau") + pk, err := NewKZGSetupParamaters(tausG1, tausG2) + require.NoError(t, err, "Unable to create new KZGSetupParamaters") + + poly := new(Polynomial).SetUInt64([]uint64{10, 20, 30, 40}) + x := []*native.Field{ + bls.Bls12381FqNew().SetUint64(9), + bls.Bls12381FqNew().SetUint64(8), + bls.Bls12381FqNew().SetUint64(7), + } + y := []*native.Field{} + yBad := []*native.Field{} + var y_ *native.Field + for _, val := range x { + y_ = poly.Evaluate(val) + + y = append(y, y_) + yBad = append(yBad, bls.Bls12381FqNew().Add(y_, y_)) + } + + c, err := Commit(pk, poly) + require.NoError(t, err, "Unable to perform commit") + + ver, err := VerifyPoly(pk, c, poly) + require.NoError(t, err, "Unable to perform polynomial verification") + require.Equal(t, 1, ver) + + // Verify normal setup + proof, err := CreateWitnessBatch(pk, poly, x, y) + require.NoError(t, err, "Unable to create batch witness of evaluation") + + v, err := VerifyEvalBatch(pk, c, proof, x, y) + require.NoError(t, err, "Unable to varify evaluation") + require.Equal(t, 1, v) + + // Fail verification for bad proof with good data + proofBad := new(bls.G1).Mul(new(bls.G1).Generator(), bls.Bls12381FqNew().SetUint64(99)) + require.NoError(t, err, "Unable to create batch witness of evaluation") + vwrong, err := VerifyEvalBatch(pk, c, proofBad, x, y) + require.NoError(t, err, "Unable to varify evaluation") + require.Equal(t, 0, vwrong) + + // Fail verification for incorrect polynomial evaluation + ix, err := CreateLagrangePolynomial(yBad, x) + require.NoError(t, err, "Unable to create lagrangian") + q, _ := poly.Sub(ix).Div(CreateZeroPolynomial(yBad)) + badProof, err := EvalPolyG1(pk, q) + require.NoError(t, err, "Unable to perform polynomial evaluation") + vBadY, err := VerifyEvalBatch(pk, c, badProof, x, yBad) + require.NoError(t, err, "Unable to varify evaluation") + require.Equal(t, 0, vBadY) + +} diff --git a/pkg/zkp/kzg/polynomial.go b/pkg/zkp/kzg/polynomial.go new file mode 100644 index 00000000..18a40cae --- /dev/null +++ b/pkg/zkp/kzg/polynomial.go @@ -0,0 +1,276 @@ +package kzg + +import ( + "fmt" + "math/big" + + "github.com/coinbase/kryptology/pkg/core/curves/native" + bls "github.com/coinbase/kryptology/pkg/core/curves/native/bls12381" + "github.com/pkg/errors" +) + +func higherOrderPoly(a, b *Polynomial) (*Polynomial, *Polynomial) { + if a.Order() >= b.Order() { + return a, b + } + return b, a +} + +func makeScalarArray(size int) []*native.Field { + arr := make([]*native.Field, size) + for i := 0; i < len(arr); i++ { + arr[i] = bls.Bls12381FqNew().SetZero() + } + return arr +} + +// The zero polynomial is defined as: +// z(x) = (x - z_0) * (x - z_1) * ... * (x - z_{k-1}) +func CreateZeroPolynomial(z []*native.Field) *Polynomial { + zp := new(Polynomial).Set( // x - z + []*native.Field{ + bls.Bls12381FqNew().Neg(z[0]), + bls.Bls12381FqNew().SetOne(), + }, + ) + for i := 1; i < len(z); i++ { + zp = zp.Mul( + new(Polynomial).Set( + []*native.Field{ + bls.Bls12381FqNew().Neg(z[i]), + bls.Bls12381FqNew().SetOne(), + }, + ), + ) + } + return zp +} + +// CreateLagrangePolynomial uses point data to interpolate +// a polynomial that hits all the points +func CreateLagrangePolynomial(z, y []*native.Field) (*Polynomial, error) { + if len(z) != len(y) { + return nil, errors.New("Cannot interpolate mismatched xy points") + } + + p := new(Polynomial).Zero() + lb := new(Polynomial).Zero() + + for i := 0; i < len(z); i++ { + for j := 0; j < len(z); j++ { + if i == j { + continue + } + + // Calculate components of basis polynomial + n := new(Polynomial).Set( // (x - z_j) + []*native.Field{ + bls.Bls12381FqNew().Neg(z[j]), + bls.Bls12381FqNew().SetOne(), + }, + ) + d := bls.Bls12381FqNew().Sub(z[i], z[j]) // (z_i - z_j) + bp := n.ScalarDiv(d) + + if lb.IsZero() { + lb = bp + } else { + lb = lb.Mul(bp) + } + } + + p = p.Add(lb.ScalarMul(y[i])) + lb = new(Polynomial).Zero() + } + + return p, nil +} + +type Polynomial struct { + Coefficients []*native.Field +} + +// Clean removes all higher order terms +// whose coefficient is zero +func (p *Polynomial) Clean() *Polynomial { + trailingIndex := 0 + for i := p.Order(); i >= 0; i-- { + if p.Coefficients[i].IsZero() == 0 { + trailingIndex = i + break + } + } + p.Coefficients = p.Coefficients[0 : trailingIndex+1] + return p +} + +// Order returns the order of the polynomial +func (p *Polynomial) Order() int { + return len(p.Coefficients) - 1 +} + +// Copy returns a new copy of a polynomial +func (p *Polynomial) Copy() *Polynomial { + copyCoeffs := make([]*native.Field, len(p.Coefficients)) + for i := 0; i < len(p.Coefficients); i++ { + copyCoeffs[i] = bls.Bls12381FqNew().Set(p.Coefficients[i]) + } + return new(Polynomial).Set(copyCoeffs) +} + +// Equal only returns true if both polynomial coefficients are equal +func (p *Polynomial) Equal(other *Polynomial) bool { + if p.Order() != other.Order() { + return false + } + for i, v := range p.Coefficients { + if v.Cmp(other.Coefficients[i]) != 0 { + return false + } + } + return true +} + +// Zero returns zero as a polynomial +func (p *Polynomial) Zero() *Polynomial { + return new(Polynomial).Set([]*native.Field{bls.Bls12381FqNew().SetZero()}) +} + +// IsZero returns true if the polynomial zero +func (p *Polynomial) IsZero() bool { + return p.Equal(new(Polynomial).Zero()) +} + +// Lead returns the leading coefficient +func (p *Polynomial) Lead() *native.Field { + return p.Coefficients[len(p.Coefficients)-1] +} + +// Set sets the coefficients of the polynomial from field elements +func (p *Polynomial) Set(coefficients []*native.Field) *Polynomial { + // Let empty array signify zero + if len(coefficients) == 0 { + return new(Polynomial).Zero() + } + + retP := &Polynomial{ + Coefficients: coefficients, + } + return retP.Clean() +} + +// SetBigInt sets the coefficients of the polynomial from big integers +func (p *Polynomial) SetBigInt(coefficients []*big.Int) *Polynomial { + // Let empty array signify zero + if len(coefficients) == 0 { + return new(Polynomial).Zero() + } + p.Coefficients = make([]*native.Field, len(coefficients)) + for i := 0; i < len(coefficients); i++ { + p.Coefficients[i] = bls.Bls12381FqNew().SetBigInt(coefficients[i]) + } + return p.Clean() +} + +// SetUInt64 sets the coefficients of the polynomial from unsigned integers +func (p *Polynomial) SetUInt64(coefficients []uint64) *Polynomial { + // Let empty array signify zero + if len(coefficients) == 0 { + return new(Polynomial).Zero() + } + p.Coefficients = make([]*native.Field, len(coefficients)) + for i := 0; i < len(coefficients); i++ { + p.Coefficients[i] = bls.Bls12381FqNew().SetUint64(coefficients[i]) + } + return p.Clean() +} + +// Evalute returns the evaluated polynomial at a point x +func (p *Polynomial) Evaluate(x *native.Field) *native.Field { + result := bls.Bls12381FqNew().Set(p.Coefficients[p.Order()]) + for i := p.Order() - 1; i >= 0; i-- { + result = bls.Bls12381FqNew().Add(bls.Bls12381FqNew().Mul(result, x), p.Coefficients[i]) + } + return result +} + +// Add adds two polynomials together +func (p *Polynomial) Add(other *Polynomial) *Polynomial { + a, b := higherOrderPoly(p, other) + pBig := a.Copy() + pSmall := b.Copy() + + for i := 0; i < len(pSmall.Coefficients); i++ { + pBig.Coefficients[i] = bls.Bls12381FqNew().Add(pBig.Coefficients[i], pSmall.Coefficients[i]) + } + return pBig.Clean() +} + +// Sub subtracts polynomials +func (p *Polynomial) Sub(other *Polynomial) *Polynomial { + a, b := higherOrderPoly(p, other) + pBig := a.Copy() + pSmall := b.Copy() + + for i := 0; i < len(pSmall.Coefficients); i++ { + pBig.Coefficients[i] = bls.Bls12381FqNew().Sub(pBig.Coefficients[i], pSmall.Coefficients[i]) + } + return pBig.Clean() +} + +// Mul multiplies polynomials together +func (p *Polynomial) Mul(other *Polynomial) *Polynomial { + finalCoeffs := makeScalarArray(p.Order() + other.Order() + 1) + for i := 0; i < len(p.Coefficients); i++ { + for j := 0; j < len(other.Coefficients); j++ { + inter := bls.Bls12381FqNew().Mul(p.Coefficients[i], other.Coefficients[j]) + finalCoeffs[i+j] = bls.Bls12381FqNew().Add(finalCoeffs[i+j], inter) + } + } + return new(Polynomial).Set(finalCoeffs).Clean() +} + +// ScalarMul multiplies each coefficient by scalar +func (p *Polynomial) ScalarMul(c *native.Field) *Polynomial { + pCopy := p.Copy() + for i := 0; i < len(pCopy.Coefficients); i++ { + pCopy.Coefficients[i] = bls.Bls12381FqNew().Mul(pCopy.Coefficients[i], c) + } + return pCopy +} + +// ScalarDiv divides each coefficient by scalar +func (p *Polynomial) ScalarDiv(c *native.Field) *Polynomial { + pCopy := p.Copy() + inv, _ := bls.Bls12381FqNew().Invert(c) + for i := 0; i < len(pCopy.Coefficients); i++ { + pCopy.Coefficients[i] = bls.Bls12381FqNew().Mul(pCopy.Coefficients[i], inv) + } + return pCopy +} + +// Div divides two polynomials +// ref: https://en.wikipedia.org/wiki/Polynomial_long_division +func (p *Polynomial) Div(d *Polynomial) (*Polynomial, *Polynomial) { + if d.IsZero() { + panic(fmt.Errorf("cannot divide by zero polynomial")) + } + + q := makeScalarArray(p.Order() + d.Order() + 1) + r := p + + for !r.IsZero() && (r.Order() >= d.Order()) { + + // q = q + lead(r) / lead(d) + inv, _ := bls.Bls12381FqNew().Invert(d.Lead()) + t := bls.Bls12381FqNew().Mul(r.Lead(), inv) + tIndex := len(r.Coefficients) - len(d.Coefficients) + q[tIndex] = t + + // r = r - (t * d) + td := new(Polynomial).Set(append(makeScalarArray(tIndex), t)).Mul(d) + rScalars := r.Sub(td) + r = new(Polynomial).Set(rScalars.Coefficients).Clean() + } + return new(Polynomial).Set(q).Clean(), r.Clean() +} diff --git a/pkg/zkp/kzg/polynomial_test.go b/pkg/zkp/kzg/polynomial_test.go new file mode 100644 index 00000000..e31399c3 --- /dev/null +++ b/pkg/zkp/kzg/polynomial_test.go @@ -0,0 +1,171 @@ +package kzg + +import ( + "math/big" + "testing" + + "github.com/coinbase/kryptology/pkg/core/curves/native" + bls "github.com/coinbase/kryptology/pkg/core/curves/native/bls12381" + "github.com/stretchr/testify/require" +) + +func TestNewPolyEval(t *testing.T) { + poly := new(Polynomial).SetUInt64([]uint64{5, 1}) + x := bls.Bls12381FqNew().SetBigInt(big.NewInt(1)) + + require.Equal(t, poly.Evaluate(x).BigInt(), big.NewInt(6)) +} + +func TestNewPolyLead(t *testing.T) { + p := new(Polynomial).SetUInt64([]uint64{5, 1}) + lead := p.Coefficients[len(p.Coefficients)-1] + require.Equal(t, p.Lead(), lead) +} + +func TestPolyAddSub(t *testing.T) { + poly1 := new(Polynomial).SetUInt64([]uint64{1, 0, 5}) + poly2 := new(Polynomial).SetUInt64([]uint64{3, 0, 1}) + poly3 := new(Polynomial).SetUInt64([]uint64{4, 0, 6}) + + require.True(t, poly1.Add(poly2).Equal(poly3)) + require.True(t, poly1.Add(poly2).Equal(poly2.Add(poly1))) +} + +func TestMulDiv(t *testing.T) { + a := big.NewInt(1) + b := big.NewInt(2) + c := big.NewInt(3) + + d := big.NewInt(4) + e := big.NewInt(10) + f := big.NewInt(12) + g := big.NewInt(9) + + poly1 := new(Polynomial).SetBigInt([]*big.Int{a, b, c}) + poly1sq := new(Polynomial).SetBigInt([]*big.Int{a, d, e, f, g}) + + res := poly1.Mul(poly1) + require.True(t, res.Equal(poly1sq)) + + quotient, remainder := res.Div(poly1) + require.True(t, quotient.Equal(poly1)) + require.True(t, remainder.Equal(new(Polynomial).Zero())) + + poly2 := new(Polynomial).SetBigInt([]*big.Int{c}) + + quotient2, remainder2 := res.Add(poly2).Div(poly1) + require.True(t, quotient2.Equal(poly1)) + require.True(t, remainder2.Equal(poly2)) + + quotient3, remainder3 := new(Polynomial).Zero().Div(poly1) + require.True(t, quotient3.Equal(new(Polynomial).Zero())) + require.True(t, remainder3.Equal(new(Polynomial).Zero())) + + poly3 := new(Polynomial).SetBigInt([]*big.Int{d, a, f, e, g}) + + // Test commutativity + require.True(t, (poly1.Mul(poly2)).Equal(poly2.Mul(poly1))) + require.True(t, (poly1.Mul(poly2.Mul(poly3))).Equal(poly3.Mul(poly1.Mul(poly2)))) + + // (p1 * (p2/p3)) = ((p1*p2) / p3) + qtmp, r1 := poly2.Div(poly3) + q1 := poly1.Mul(qtmp) + q2, r2 := poly1.Mul(poly2).Div(poly3) + require.True(t, q1.Equal(q2)) + require.True(t, r1.Mul(poly1).Equal(r2)) + + // Test associativity + require.True(t, (poly1.Mul(poly2.Add(poly3))).Equal(poly1.Mul(poly2).Add(poly1.Mul(poly3)))) +} + +func TestScalarMulDiv(t *testing.T) { + a := big.NewInt(1) + b := big.NewInt(2) + c := big.NewInt(3) + + d := bls.Bls12381FqNew().SetBigInt(big.NewInt(37)) + e := bls.Bls12381FqNew().SetBigInt(big.NewInt(103)) + + poly1 := new(Polynomial).SetBigInt([]*big.Int{c, b, a}) + + n := bls.Bls12381FqNew().SetBigInt(c) + + res1 := poly1.ScalarMul(n).ScalarDiv(n) + require.Equal(t, res1.Equal(poly1), true) + res2 := poly1.ScalarMul(d).ScalarMul(e).ScalarDiv(bls.Bls12381FqNew().Mul(d, e)) + require.Equal(t, res2.Equal(poly1), true) +} + +func TestLagrangeErr(t *testing.T) { + x0 := bls.Bls12381FqNew().SetUint64(1) + x1 := bls.Bls12381FqNew().SetUint64(2) + y0 := bls.Bls12381FqNew().SetUint64(3) + + x := []*native.Field{x0, x1} + y := []*native.Field{y0} + + _, err := CreateLagrangePolynomial(x, y) + require.Error(t, err) +} + +func TestCreateLagrangePolynomialLinear(t *testing.T) { + x0 := bls.Bls12381FqNew().SetUint64(1) + y0 := bls.Bls12381FqNew().SetUint64(2) + x1 := bls.Bls12381FqNew().SetUint64(3) + y1 := bls.Bls12381FqNew().SetUint64(4) + x2 := bls.Bls12381FqNew().SetUint64(5) + y2 := bls.Bls12381FqNew().SetUint64(6) + x3 := bls.Bls12381FqNew().SetUint64(7) + y3 := bls.Bls12381FqNew().SetUint64(8) + + x := []*native.Field{x0, x1, x2, x3} + y := []*native.Field{y0, y1, y2, y3} + + p, err := CreateLagrangePolynomial(x, y) + require.NoError(t, err, "Unable to create lagrange polynomial") + require.True(t, p.Equal(new(Polynomial).SetUInt64([]uint64{1, 1}))) + + for i := 0; i < len(x); i++ { + require.Equal(t, p.Evaluate(x[i]), y[i]) + } +} + +func TestZeroPolynomialDividesLagrangePolynomial(t *testing.T) { + x0 := bls.Bls12381FqNew().SetUint64(0) + x1 := bls.Bls12381FqNew().SetUint64(4) + x2 := bls.Bls12381FqNew().SetUint64(11) + x3 := bls.Bls12381FqNew().SetUint64(15) + + poly := new(Polynomial).SetUInt64([]uint64{5, 7, 3, 1, 1, 1}) + + // eval poly at points + x := []*native.Field{x0, x1, x2, x3} + y := []*native.Field{} + for _, val := range x { + y = append(y, poly.Evaluate(val)) + } + + lp, err := CreateLagrangePolynomial(x, y) + require.NoError(t, err, "Unable to create lagrange polynomial") + + _, r := poly.Sub(lp).Div(CreateZeroPolynomial(x)) + require.True(t, r.IsZero()) +} + +func TestCreateZeroPolynomial(t *testing.T) { + a := bls.Bls12381FqNew().SetUint64(1) + b := bls.Bls12381FqNew().SetUint64(2) + c := bls.Bls12381FqNew().SetUint64(3) + + nums := []*native.Field{a, b, c} + zp := CreateZeroPolynomial(nums) + zpc := new(Polynomial).SetBigInt( + []*big.Int{ + big.NewInt(-6), + big.NewInt(11), + big.NewInt(-6), + big.NewInt(1), + }, + ) + require.True(t, zp.Equal(zpc)) +}