diff --git a/primitives/bulletproofs/inner_product.go b/primitives/bulletproofs/inner_product.go new file mode 100644 index 0000000..cded9c6 --- /dev/null +++ b/primitives/bulletproofs/inner_product.go @@ -0,0 +1,125 @@ +package bulletproofs + +import ( + "cwtch.im/tapir/primitives/core" + "encoding/json" + "git.openprivacy.ca/openprivacy/libricochet-go/log" + "github.com/bwesterb/go-ristretto" + "strconv" +) + +// InnerProductProof encapsulates an inner product proof +type InnerProductProof struct { + L core.PointVector + R core.PointVector + A core.Scalar + B core.Scalar +} + +// ProveInnerProduct generates a proof for , the inner product of a and b +func ProveInnerProduct(a, b core.ScalarVector, u core.Point, P *ristretto.Point, G, H core.GeneratorVector, transcript *core.Transcript) InnerProductProof { + n := len(a) + transcript.AddToTranscript("n", []byte(strconv.Itoa(n))) + Lvec := core.PointVector{} + Rvec := core.PointVector{} + Gbytes, _ := json.Marshal(G) + transcript.AddToTranscript("G", Gbytes) + Hbytes, _ := json.Marshal(H) + transcript.AddToTranscript("H", Hbytes) + transcript.AddToTranscript("P'", P.Bytes()) + for n != 1 { + np := n / 2 + aL, aR := a[:np], a[np:] + bL, bR := b[:np], b[np:] + GL, GR := G[:np], G[np:] + HL, HR := H[:np], H[np:] + + cL := core.InnerProduct(aL, bR) + cR := core.InnerProduct(aR, bL) + + L := core.MultiExp(append(aL.Join(bR), cL), append(GR.Join(HL), u)) + R := core.MultiExp(append(aR.Join(bL), cR), append(GL.Join(HR), u)) + + transcript.AddToTranscript("L", L.Bytes()) + Lvec = append(Lvec, L) + transcript.AddToTranscript("R", R.Bytes()) + Rvec = append(Rvec, R) + + u := transcript.CommitToTranscriptScalar("u") + uinv := new(ristretto.Scalar).Inverse(u) + + for i := 0; i < len(aL); i++ { + aL_ := new(ristretto.Scalar).Mul(aL[i], u) + aL[i] = aL_.Add(aL_, new(ristretto.Scalar).Mul(aR[i], uinv)) + bL_ := new(ristretto.Scalar).Mul(bL[i], uinv) + bL[i] = bL_.Add(bL_, new(ristretto.Scalar).Mul(bR[i], u)) + + GL[i] = core.MultiExp(core.ScalarVector{uinv, u}, core.GeneratorVector{GL[i], GR[i]}) + HL[i] = core.MultiExp(core.ScalarVector{u, uinv}, core.GeneratorVector{HL[i], HR[i]}) + } + + x2 := new(ristretto.Scalar).Square(u) + P_ := new(ristretto.Point).ScalarMult(L, x2) + P_.Add(P_, P) + P_.Add(P_, new(ristretto.Point).ScalarMult(R, new(ristretto.Scalar).Inverse(x2))) + P = P_ + transcript.AddToTranscript("P'", P.Bytes()) + + a = aL + b = bL + G = GL + H = HL + Gbytes, _ := json.Marshal(G) + transcript.AddToTranscript("G", Gbytes) + Hbytes, _ := json.Marshal(H) + transcript.AddToTranscript("H", Hbytes) + n = np + } + + return InnerProductProof{Lvec, Rvec, a[0], b[0]} +} + +// Verify checks the given inner product proof +func Verify(proof InnerProductProof, n int, u, P *ristretto.Point, G, H core.GeneratorVector, transcript *core.Transcript) bool { + transcript.AddToTranscript("n", []byte(strconv.Itoa(n))) + np := n / 2 + + Gbytes, _ := json.Marshal(G) + transcript.AddToTranscript("G", Gbytes) + Hbytes, _ := json.Marshal(H) + transcript.AddToTranscript("H", Hbytes) + transcript.AddToTranscript("P'", P.Bytes()) + for i := range proof.L { + GL, GR := G[:np], G[np:] + HL, HR := H[:np], H[np:] + + transcript.AddToTranscript("L", proof.L[i].Bytes()) + transcript.AddToTranscript("R", proof.R[i].Bytes()) + x := transcript.CommitToTranscriptScalar("u") + xinv := new(ristretto.Scalar).Inverse(x) + + for j := 0; j < np; j++ { + GL[j] = core.MultiExp(core.ScalarVector{xinv, x}, core.GeneratorVector{GL[j], GR[j]}) + HL[j] = core.MultiExp(core.ScalarVector{x, xinv}, core.GeneratorVector{HL[j], HR[j]}) + } + + x2 := new(ristretto.Scalar).Square(x) + P_ := new(ristretto.Point).ScalarMult(proof.L[i], x2) + P_.Add(P_, P) + P_.Add(P_, new(ristretto.Point).ScalarMult(proof.R[i], new(ristretto.Scalar).Inverse(x2))) + P = P_ + transcript.AddToTranscript("P'", P.Bytes()) + + G = GL + H = HL + Gbytes, _ := json.Marshal(G) + transcript.AddToTranscript("G", Gbytes) + Hbytes, _ := json.Marshal(H) + transcript.AddToTranscript("H", Hbytes) + np = np / 2 + } + c := new(ristretto.Scalar).Mul(proof.A, proof.B) + P_ := core.MultiExp(core.ScalarVector{proof.A, proof.B, c}, core.GeneratorVector{G[0], H[0], u}) + log.Debugf("P:%v\nP':%v\n", P, P_) + return P.Equals(P_) +} diff --git a/primitives/bulletproofs/inner_product_test.go b/primitives/bulletproofs/inner_product_test.go new file mode 100644 index 0000000..6bcf558 --- /dev/null +++ b/primitives/bulletproofs/inner_product_test.go @@ -0,0 +1,63 @@ +package bulletproofs + +import ( + "cwtch.im/tapir/primitives/core" + "git.openprivacy.ca/openprivacy/libricochet-go/log" + "github.com/bwesterb/go-ristretto" + "testing" +) + +func assert(t *testing.T, expected *ristretto.Scalar, actual *ristretto.Scalar) { + if expected.Equals(actual) { + t.Logf("inner_product matched: %v", actual) + } else { + t.Fatalf("c should be %v instead: %v", expected, actual) + } +} + +func Test_inner_product(t *testing.T) { + one := new(ristretto.Scalar).SetOne() + zero := new(ristretto.Scalar).SetZero() + a := core.ScalarVector{one, zero, one, zero} + b := core.ScalarVector{zero, one, zero, one} + c := core.InnerProduct(a, b) + assert(t, zero, c) + + a = core.ScalarVector{one, one, one, zero} + b = core.ScalarVector{one, one, zero, one} + c = core.InnerProduct(a, b) + assert(t, new(ristretto.Scalar).Add(one, one), c) + +} + +func TestProveInnerProduct(t *testing.T) { + log.SetLevel(log.LevelDebug) + + one := new(ristretto.Scalar).SetOne() + zero := new(ristretto.Scalar).SetZero() + a := core.ScalarVector{one, zero, one, one} + b := core.ScalarVector{zero, one, one, one} + + c := core.InnerProduct(a, b) + G := make(core.GeneratorVector, 4) + H := make(core.GeneratorVector, 4) + for i := 0; i < 4; i++ { + G[i] = new(ristretto.Point).Rand() + H[i] = new(ristretto.Point).Rand() + } + + u := new(ristretto.Point).Rand() + + P_ := core.MultiExp(append(a.Join(b), c), append(G.Join(H), u)) + + proverTranscript := core.NewTranscript("test_innerproductproof") + verifierTranscript := core.NewTranscript("test_innerproductproof") + proof := ProveInnerProduct(a, b, u, new(ristretto.Point).Set(P_), core.CopyVector(G), core.CopyVector(H), proverTranscript) + + if Verify(proof, 4, u, new(ristretto.Point).Set(P_), core.CopyVector(G), core.CopyVector(H), verifierTranscript) { + t.Logf("Inner Product Proof Passed!") + } else { + t.Logf("%v\n\n%v\n", proverTranscript.OutputTranscriptToAudit(), verifierTranscript.OutputTranscriptToAudit()) + t.Fatalf("Inner Product Proof Failed!") + } +} diff --git a/primitives/bulletproofs/range_proof.go b/primitives/bulletproofs/range_proof.go new file mode 100644 index 0000000..6bfd015 --- /dev/null +++ b/primitives/bulletproofs/range_proof.go @@ -0,0 +1,224 @@ +package bulletproofs + +import ( + "cwtch.im/tapir/primitives/core" + "git.openprivacy.ca/openprivacy/libricochet-go/log" + "github.com/bwesterb/go-ristretto" + "math/big" +) + +// RangeProof encapsulates a proof that V = [0, max) where max is defined by the Setup function +type RangeProof struct { + A *ristretto.Point + S *ristretto.Point + T1 *ristretto.Point + T2 *ristretto.Point + TauX *ristretto.Scalar + InnerProduct *ristretto.Scalar + Mu *ristretto.Scalar + IPP InnerProductProof + V *ristretto.Point +} + +// CommitmentsParams encapsulates the commitment parameters for a given range proof +type CommitmentsParams struct { + G core.GeneratorVector + H core.GeneratorVector + u *ristretto.Point + g *ristretto.Point + h *ristretto.Point + max int +} + +// Setup generates multiple independent commitment parameters from the underlying transcript +func Setup(max int, transcript *core.Transcript) (c CommitmentsParams) { + c.G = transcript.CommitToGenerators("G", int(max)) + c.H = transcript.CommitToGenerators("H", int(max)) + + c.u = transcript.CommitToGenerator("u") + c.g = transcript.CommitToGenerator("g") + c.h = transcript.CommitToGenerator("h") + + c.max = max + return +} + +// GenerateRangeProof creates a valid rangeproof that value is within [0,max) under the given transcript +// It returns the rangeproof and a random scalar "gamma" that can be used to open V, the commitement to v vGgH +func GenerateRangeProof(value int32, c CommitmentsParams, transcript *core.Transcript) (RangeProof, *ristretto.Scalar) { + one := new(ristretto.Scalar).SetOne() + two := new(ristretto.Scalar).Add(one, one) + two_n := core.PowerVector(two, c.max) + + gamma := new(ristretto.Scalar).Rand() + + aL := valueToVector(value, c.max) + aR := core.VectorAddScalar(aL, new(ristretto.Scalar).Neg(one)) + alpha := new(ristretto.Scalar).Rand() + + vs := new(ristretto.Scalar).SetBigInt(big.NewInt(int64(value))) + + V := core.MultiExp(core.ScalarVector{gamma, vs}, core.GeneratorVector{c.h, c.g}) + A := core.MultiExp(append(aL.Join(aR), alpha), append(c.G.Join(c.H), c.h)) + + Sl := make(core.ScalarVector, c.max) + Sr := make(core.ScalarVector, c.max) + for i := 0; i < c.max; i++ { + Sl[i] = new(ristretto.Scalar).Rand() + Sr[i] = new(ristretto.Scalar).Rand() + } + p := new(ristretto.Scalar).Rand() + + S := core.MultiExp(append(Sl.Join(Sr), p), append(c.G.Join(c.H), c.h)) + + transcript.AddToTranscript("A", A.Bytes()) + transcript.AddToTranscript("S", S.Bytes()) + + y := transcript.CommitToTranscriptScalar("y") + z := transcript.CommitToTranscriptScalar("z") + + y_n := core.PowerVector(y, c.max) + z2 := new(ristretto.Scalar).Square(z) + + l0 := core.VectorAddScalar(aL, new(ristretto.Scalar).Neg(z)) + //l1 == Sr + r0 := core.EntrywiseSum(core.EntryWiseProduct(y_n, core.VectorAddScalar(aR, z)), core.VectorMulScalar(two_n, z2)) + r1 := core.EntryWiseProduct(Sr, y_n) + + t0 := new(ristretto.Scalar).Add(new(ristretto.Scalar).Mul(z2, vs), delta(y_n, z, c.max)) + t1 := new(ristretto.Scalar).Add(core.InnerProduct(Sl, r0), core.InnerProduct(l0, r1)) + t2 := core.InnerProduct(Sl, r1) + + tau1 := new(ristretto.Scalar).Rand() + tau2 := new(ristretto.Scalar).Rand() + + T1 := core.MultiExp(core.ScalarVector{t1, tau1}, core.GeneratorVector{c.g, c.h}) + T2 := core.MultiExp(core.ScalarVector{t2, tau2}, core.GeneratorVector{c.g, c.h}) + + transcript.AddToTranscript("T1", T1.Bytes()) + transcript.AddToTranscript("T2", T2.Bytes()) + + x := transcript.CommitToTranscriptScalar("x") + + // T(X) = t0 + t1x + t2x + TX := new(ristretto.Scalar).Set(t0) + TX.Add(TX, new(ristretto.Scalar).Mul(t1, x)) + TX.Add(TX, new(ristretto.Scalar).Mul(t2, new(ristretto.Scalar).Square(x))) + + l := core.EntrywiseSum(core.VectorAddScalar(aL, new(ristretto.Scalar).Neg(z)), core.VectorMulScalar(Sl, x)) + _r := core.EntrywiseSum(core.VectorAddScalar(aR, z), core.VectorMulScalar(Sr, x)) + r := core.EntrywiseSum(core.EntryWiseProduct(y_n, _r), core.VectorMulScalar(two_n, z2)) + + iplr := core.InnerProduct(l, r) + + log.Debugf("T(X) = %v", TX) + log.Debugf("ipp = %v", iplr) + log.Debugf("equal: %v", TX.Equals(iplr)) + + // generate h' + H_ := make(core.GeneratorVector, c.max) + H_[0] = c.H[0] + for i := 1; i < c.max; i++ { + H_[i] = new(ristretto.Point).ScalarMult(c.H[i], new(ristretto.Scalar).Inverse(y_n[i])) + } + + P := core.MultiExp(l.Join(r), c.G.Join(H_)) + log.Debugf("P: %v", P) + + uP := new(ristretto.Point).Add(P, new(ristretto.Point).ScalarMult(c.u, iplr)) + log.Debugf("uP: %v", uP) + ipp := ProveInnerProduct(l, r, c.u, new(ristretto.Point).Set(uP), core.CopyVector(c.G), core.CopyVector(H_), transcript) + + taux := new(ristretto.Scalar).Mul(tau2, new(ristretto.Scalar).Square(x)) + taux = taux.Add(taux, new(ristretto.Scalar).Mul(tau1, x)) + taux = taux.Add(taux, new(ristretto.Scalar).Mul(z2, gamma)) + + mu := new(ristretto.Scalar).Add(alpha, new(ristretto.Scalar).Mul(p, x)) + return RangeProof{A, S, T1, T2, taux, iplr, mu, ipp, V}, gamma +} + +// VerifyRangeProof returns true if the given proof passes all the checks for a given set of commitment parameters +// and the given transcript +func VerifyRangeProof(proof RangeProof, c CommitmentsParams, transcript *core.Transcript) bool { + one := new(ristretto.Scalar).SetOne() + two := new(ristretto.Scalar).Add(one, one) + two_n := core.PowerVector(two, c.max) + + transcript.AddToTranscript("A", proof.A.Bytes()) + transcript.AddToTranscript("S", proof.S.Bytes()) + y := transcript.CommitToTranscriptScalar("y") + z := transcript.CommitToTranscriptScalar("z") + transcript.AddToTranscript("T1", proof.T1.Bytes()) + transcript.AddToTranscript("T2", proof.T2.Bytes()) + x := transcript.CommitToTranscriptScalar("x") + y_n := core.PowerVector(y, c.max) + // generate h' + H_ := make(core.GeneratorVector, c.max) + H_[0] = c.H[0] + for i := 1; i < c.max; i++ { + H_[i] = new(ristretto.Point).ScalarMult(c.H[i], new(ristretto.Scalar).Inverse(y_n[i])) + } + + // check t = t(x) = t0 + t1.x + t1.x^2 + lhs := core.MultiExp(core.ScalarVector{proof.InnerProduct, proof.TauX}, core.GeneratorVector{c.g, c.h}) + rhs := core.MultiExp(core.ScalarVector{new(ristretto.Scalar).Square(z), delta(y_n, z, c.max), x, new(ristretto.Scalar).Square(x)}, core.GeneratorVector{proof.V, c.g, proof.T1, proof.T2}) + log.Debugf("lhs: %v", lhs) + log.Debugf("rhs: %v", rhs) + log.Debugf("equal: %v", lhs.Equals(rhs)) + + if lhs.Equals(rhs) { + + // compute P + + negz := new(ristretto.Scalar).Neg(z) + negzG := new(ristretto.Point).SetZero() + for _, gen := range c.G { + negzG = negzG.Add(negzG, new(ristretto.Point).ScalarMult(gen, negz)) + } + + mul := core.EntrywiseSum(core.VectorMulScalar(y_n, z), core.VectorMulScalar(two_n, new(ristretto.Scalar).Square(z))) + + Pr := new(ristretto.Point).Set(proof.A) + Pr = Pr.Add(Pr, new(ristretto.Point).ScalarMult(proof.S, x)) + Pr = Pr.Add(Pr, negzG) + Pr = Pr.Add(Pr, core.MultiExp(mul, H_)) + + Pl := new(ristretto.Point).Sub(Pr, new(ristretto.Point).ScalarMult(c.h, proof.Mu)) + // check inner product + + uP := new(ristretto.Point).Add(Pl, new(ristretto.Point).ScalarMult(c.u, proof.InnerProduct)) + + return Verify(proof.IPP, c.max, c.u, new(ristretto.Point).Set(uP), core.CopyVector(c.G), core.CopyVector(H_), transcript) + } + return false +} + +func delta(y_n core.ScalarVector, z core.Scalar, max int) core.Scalar { + one := new(ristretto.Scalar).SetOne() + // (z-z^2) + z2 := new(ristretto.Scalar).Square(z) + result := new(ristretto.Scalar).Sub(z, z2) + // (z-z^2) * <1^n,y^n> + result.Mul(result, core.InnerProduct(core.IdentityVector(max), y_n)) + two := new(ristretto.Scalar).Add(one, one) + two_n := core.PowerVector(two, max) + // (z-z^2) * <1^n,y^n> - z^3 *<1n,2n> + z3 := new(ristretto.Scalar).Mul(z2, z) + return result.Sub(result, new(ristretto.Scalar).Mul(z3, core.InnerProduct(core.IdentityVector(max), two_n))) +} + +func valueToVector(value int32, max int) core.ScalarVector { + one := new(ristretto.Scalar).SetOne() + zero := new(ristretto.Scalar).SetZero() + result := core.ScalarVector{} + for len(result) != max { + v := value & 0x0001 + if v == 1 { + result = append(result, one) + } else { + result = append(result, zero) + } + value = value >> 1 + } + return result +} diff --git a/primitives/bulletproofs/range_proof_test.go b/primitives/bulletproofs/range_proof_test.go new file mode 100644 index 0000000..d89bdf1 --- /dev/null +++ b/primitives/bulletproofs/range_proof_test.go @@ -0,0 +1,27 @@ +package bulletproofs + +import ( + "cwtch.im/tapir/primitives/core" + "encoding/json" + "git.openprivacy.ca/openprivacy/libricochet-go/log" + "testing" +) + +func TestProove(t *testing.T) { + log.SetLevel(log.LevelDebug) + + proverTranscript := core.NewTranscript("rangeproof") + verifierTranscript := core.NewTranscript("rangeproof") + + rangeproof, _ := GenerateRangeProof(10, Setup(32, proverTranscript), proverTranscript) + if VerifyRangeProof(rangeproof, Setup(32, verifierTranscript), verifierTranscript) == true { + t.Logf("Range Proof Passed!") + t.Logf("%v\n\n%v\n", proverTranscript.OutputTranscriptToAudit(), verifierTranscript.OutputTranscriptToAudit()) + jsonProof, _ := json.Marshal(rangeproof) + t.Logf("RangeProof: %s", jsonProof) + } else { + t.Logf("%v\n\n%v\n", proverTranscript.OutputTranscriptToAudit(), verifierTranscript.OutputTranscriptToAudit()) + t.Fatalf("Failed to Verify Range Proof") + } + +} diff --git a/primitives/core/operations.go b/primitives/core/operations.go new file mode 100644 index 0000000..79dfb63 --- /dev/null +++ b/primitives/core/operations.go @@ -0,0 +1,137 @@ +package core + +import ( + "fmt" + "github.com/bwesterb/go-ristretto" +) + +// Scalar is short hand for a ristretto scalar +type Scalar *ristretto.Scalar + +// Point is short hand of a ristretto point +type Point *ristretto.Point + +// ScalarVector explicit type checking +type ScalarVector []Scalar + +// PointVector explicit type checking +type PointVector []*ristretto.Point + +// GeneratorVector explicit type checking +type GeneratorVector []*ristretto.Point + +// CopyVector safely copies a vector +func CopyVector(G GeneratorVector) GeneratorVector { + H := make(GeneratorVector, len(G)) + for i, g := range G { + H[i] = new(ristretto.Point).Set(g) + } + return H +} + +// InnerProduct takes the inner product of a and b i.e. +func InnerProduct(a, b ScalarVector) *ristretto.Scalar { + if len(a) != len(b) { + panic(fmt.Sprintf("len(a) = %v ; len(b) = %v;", len(a), len(b))) + } + + result := new(ristretto.Scalar).SetZero() + for i, ai := range a { + result.Add(result, new(ristretto.Scalar).Mul(ai, b[i])) + } + return result +} + +// MultiExp takes in a vector of scalars = {a,b,c...} and a vector of generator = {A,B,C...} and outputs +// {aA,bB,cC} +func MultiExp(a ScalarVector, G GeneratorVector) *ristretto.Point { + if len(a) != len(G) { + panic(fmt.Sprintf("len(a) = %v ; len(b) = %v;", len(a), len(G))) + } + result := new(ristretto.Point).SetZero() + for i, ai := range a { + aG := new(ristretto.Point).ScalarMult(G[i], ai) + result = new(ristretto.Point).Add(result, aG) + } + return result +} + +// Join is defined for a vector of Scalars +func (a ScalarVector) Join(b ScalarVector) ScalarVector { + list := make(ScalarVector, len(a)+len(b)) + for i := 0; i < len(a); i++ { + list[i] = new(ristretto.Scalar).Set(a[i]) + } + for i := len(a); i < len(b)+len(b); i++ { + list[i] = new(ristretto.Scalar).Set(b[i-len(a)]) + } + return list +} + +// Join as defined for a vector of Generators +func (a GeneratorVector) Join(b GeneratorVector) GeneratorVector { + list := make(GeneratorVector, len(a)+len(b)) + for i := 0; i < len(a); i++ { + list[i] = new(ristretto.Point).Set(a[i]) + } + for i := len(a); i < len(b)+len(b); i++ { + list[i] = new(ristretto.Point).Set(b[i-len(a)]) + } + return list +} + +// VectorAddScalar takes in a vector v = {a,b,c..} and a scalar s and outputs {a+s,b+s,c+s....} +func VectorAddScalar(vector ScalarVector, scalar *ristretto.Scalar) ScalarVector { + result := make(ScalarVector, len(vector)) + for i := range vector { + result[i] = new(ristretto.Scalar).Add(vector[i], scalar) + } + return result +} + +// VectorMulScalar takes in a vector v = {a,b,c..} and a scalar s and outputs {as,bs,cs....} +func VectorMulScalar(vector ScalarVector, scalar *ristretto.Scalar) ScalarVector { + result := make(ScalarVector, len(vector)) + for i := range vector { + result[i] = new(ristretto.Scalar).Mul(vector[i], scalar) + } + return result +} + +// EntrywiseSum takes the entry wise sum of two vectors +func EntrywiseSum(vector ScalarVector, vector2 ScalarVector) ScalarVector { + result := make(ScalarVector, len(vector)) + for i, v := range vector { + result[i] = new(ristretto.Scalar).Add(v, vector2[i]) + } + return result +} + +// EntryWiseProduct takes the entry wise product of two vectors +func EntryWiseProduct(vector ScalarVector, vector2 ScalarVector) ScalarVector { + result := make(ScalarVector, len(vector)) + for i, v := range vector { + result[i] = new(ristretto.Scalar).Mul(v, vector2[i]) + } + return result +} + +// IdentityVector is a convenience function to generate a vector v = {1,1,1...1} +func IdentityVector(n int) ScalarVector { + result := make(ScalarVector, n) + for i := 0; i < n; i++ { + result[i] = new(ristretto.Scalar).SetOne() + } + return result +} + +// PowerVector creates a vector v = {1,x,x^2,x^3..x^n} +func PowerVector(x *ristretto.Scalar, n int) ScalarVector { + result := make(ScalarVector, n) + result[0] = new(ristretto.Scalar).SetOne() + result[1] = new(ristretto.Scalar).Set(x) + for i := 1; i < n; i++ { + result[i] = new(ristretto.Scalar).Mul(result[i-1], result[1]) + } + return result +} diff --git a/primitives/core/transcript.go b/primitives/core/transcript.go index aa49589..96471f8 100644 --- a/primitives/core/transcript.go +++ b/primitives/core/transcript.go @@ -82,6 +82,22 @@ func (t *Transcript) CommitToPRNG(label string) PRNG { return PRNG{prng: prng} } +// CommitToGenerator derives a verifiably random generator from the transcript +func (t *Transcript) CommitToGenerator(label string) *ristretto.Point { + c := t.CommitToTranscript(label) + cs := [32]byte{} + copy(cs[:], c[:]) + return new(ristretto.Point).SetElligator(&cs) +} + +// CommitToGenerators derives a set of verifiably random generators from the transcript +func (t *Transcript) CommitToGenerators(label string, n int) (generators []*ristretto.Point) { + for i := 0; i < n; i++ { + generators = append(generators, t.CommitToGenerator(fmt.Sprintf("%v-%d", label, i))) + } + return generators +} + // CommitToTranscriptScalar is a convenience method for CommitToTranscript which returns a ristretto Scalar func (t *Transcript) CommitToTranscriptScalar(label string) *ristretto.Scalar { c := t.CommitToTranscript(label)