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)