From ae10d7090cea81f7137ef52779172cddcb726e23 Mon Sep 17 00:00:00 2001 From: Wanwiset Peerapatanapokin Date: Thu, 7 Mar 2024 10:40:55 +0400 Subject: [PATCH] Rollback privacy (#477) Rollback the previous test removal or privacy package on devnet. This reverts commit 4f79e535b37dc85144e2d38c2f2570b0ce4305b2. This reverts commit 5a2722c268ec4f03f3117b1af2dee2411af4b13a. --- core/vm/contracts.go | 42 + core/vm/privacy/bulletproof.go | 1400 +++++++++++++++++++++++++++ core/vm/privacy/bulletproof.json | 1 + core/vm/privacy/bulletproof_test.go | 527 ++++++++++ core/vm/privacy/ringct.go | 632 ++++++++++++ core/vm/privacy/ringct_test.go | 324 +++++++ 6 files changed, 2926 insertions(+) create mode 100644 core/vm/privacy/bulletproof.go create mode 100644 core/vm/privacy/bulletproof.json create mode 100644 core/vm/privacy/bulletproof_test.go create mode 100644 core/vm/privacy/ringct.go create mode 100644 core/vm/privacy/ringct_test.go diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 1f03559e2a87..067bc7018ab4 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -22,6 +22,8 @@ import ( "errors" "math/big" + "github.com/XinFinOrg/XDPoSChain/core/vm/privacy" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -61,6 +63,8 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{6}): &bn256AddByzantium{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{}, common.BytesToAddress([]byte{8}): &bn256PairingByzantium{}, + common.BytesToAddress([]byte{30}): &ringSignatureVerifier{}, + common.BytesToAddress([]byte{40}): &bulletproofVerifier{}, common.BytesToAddress([]byte{41}): &XDCxLastPrice{}, common.BytesToAddress([]byte{42}): &XDCxEpochPrice{}, } @@ -77,6 +81,8 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{30}): &ringSignatureVerifier{}, + common.BytesToAddress([]byte{40}): &bulletproofVerifier{}, common.BytesToAddress([]byte{41}): &XDCxLastPrice{}, common.BytesToAddress([]byte{42}): &XDCxEpochPrice{}, } @@ -420,6 +426,42 @@ func runBn256Pairing(input []byte) ([]byte, error) { return false32Byte, nil } +type ringSignatureVerifier struct{} +type bulletproofVerifier struct{} + +func (c *bulletproofVerifier) RequiredGas(input []byte) uint64 { + //the gas should depends on the ringsize + return 100000 +} + +func (c *ringSignatureVerifier) RequiredGas(input []byte) uint64 { + //the gas should depends on the ringsize + return 100000 +} + +func (c *ringSignatureVerifier) Run(proof []byte) ([]byte, error) { + der, err := privacy.Deserialize(proof) + if err != nil { + return []byte{}, errors.New("Fail to deserialize proof") + } + if !privacy.Verify(der, false) { + return []byte{}, errors.New("Fail to verify ring signature") + } + return []byte{}, nil +} + +func (c *bulletproofVerifier) Run(proof []byte) ([]byte, error) { + mrp := new(privacy.MultiRangeProof) + if mrp.Deserialize(proof) != nil { + return []byte{}, errors.New("failed to deserialize bulletproofs") + } + + if !privacy.MRPVerify(mrp) { + return []byte{}, errors.New("failed to verify bulletproof") + } + return []byte{}, nil +} + // bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve // conforming to Istanbul consensus rules. type bn256PairingIstanbul struct{} diff --git a/core/vm/privacy/bulletproof.go b/core/vm/privacy/bulletproof.go new file mode 100644 index 000000000000..f620d230b2ca --- /dev/null +++ b/core/vm/privacy/bulletproof.go @@ -0,0 +1,1400 @@ +package privacy + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "errors" + "fmt" + "math" + "math/big" + "strconv" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/log" + + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/btcsuite/btcd/btcec" +) + +type Bulletproof struct { + proofData []byte +} + +var EC CryptoParams +var VecLength = 512 // support maximum 8 spending value, each 64 bit (gwei is unit) +var curve elliptic.Curve = crypto.S256() + +/* +Implementation of BulletProofs +*/ +type ECPoint struct { + X, Y *big.Int +} + +func (p *ECPoint) toECPubKey() *ecdsa.PublicKey { + return &ecdsa.PublicKey{curve, p.X, p.Y} +} + +func toECPoint(key *ecdsa.PublicKey) *ECPoint { + return &ECPoint{key.X, key.Y} +} + +// Equal returns true if points p (self) and p2 (arg) are the same. +func (p ECPoint) Equal(p2 ECPoint) bool { + if p.X.Cmp(p2.X) == 0 && p2.Y.Cmp(p2.Y) == 0 { + return true + } + return false +} + +// Mult multiplies point p by scalar s and returns the resulting point +func (p ECPoint) Mult(s *big.Int) ECPoint { + modS := new(big.Int).Mod(s, EC.N) + X, Y := EC.C.ScalarMult(p.X, p.Y, modS.Bytes()) + return ECPoint{X, Y} +} + +// Add adds points p and p2 and returns the resulting point +func (p ECPoint) Add(p2 ECPoint) ECPoint { + X, Y := EC.C.Add(p.X, p.Y, p2.X, p2.Y) + return ECPoint{X, Y} +} + +// Neg returns the additive inverse of point p +func (p ECPoint) Neg() ECPoint { + negY := new(big.Int).Neg(p.Y) + modValue := negY.Mod(negY, EC.C.Params().P) // mod P is fine here because we're describing a curve point + return ECPoint{p.X, modValue} +} + +type CryptoParams struct { + C elliptic.Curve // curve + KC *btcec.KoblitzCurve // curve + BPG []ECPoint // slice of gen 1 for BP + BPH []ECPoint // slice of gen 2 for BP + N *big.Int // scalar prime + U ECPoint // a point that is a fixed group element with an unknown discrete-log relative to g,h + V int // Vector length + G ECPoint // G value for commitments of a single value + H ECPoint // H value for commitments of a single value +} + +func (c CryptoParams) Zero() ECPoint { + return ECPoint{big.NewInt(0), big.NewInt(0)} +} + +func check(e error) { + if e != nil { + panic(e) + } +} + +/* +Vector Pedersen Commitment +Given an array of values, we commit the array with different generators +for each element and for each randomness. +*/ +func VectorPCommit(value []*big.Int) (ECPoint, []*big.Int) { + R := make([]*big.Int, EC.V) + + commitment := EC.Zero() + + for i := 0; i < EC.V; i++ { + r, err := rand.Int(rand.Reader, EC.N) + check(err) + + R[i] = r + + modValue := new(big.Int).Mod(value[i], EC.N) + + // mG, rH + lhsX, lhsY := EC.C.ScalarMult(EC.BPG[i].X, EC.BPG[i].Y, modValue.Bytes()) + rhsX, rhsY := EC.C.ScalarMult(EC.BPH[i].X, EC.BPH[i].Y, r.Bytes()) + + commitment = commitment.Add(ECPoint{lhsX, lhsY}).Add(ECPoint{rhsX, rhsY}) + } + + return commitment, R +} + +/* +Two Vector P Commit +Given an array of values, we commit the array with different generators +for each element and for each randomness. +*/ +func TwoVectorPCommit(a []*big.Int, b []*big.Int) ECPoint { + if len(a) != len(b) { + log.Debug("TwoVectorPCommit: Arrays not of the same length", "len(a)", len(a), "len(b)", len(b)) + } + + commitment := EC.Zero() + + for i := 0; i < EC.V; i++ { + commitment = commitment.Add(EC.BPG[i].Mult(a[i])).Add(EC.BPH[i].Mult(b[i])) + } + + return commitment +} + +/* +Vector Pedersen Commitment with Gens +Given an array of values, we commit the array with different generators +for each element and for each randomness. +We also pass in the Generators we want to use +*/ +func TwoVectorPCommitWithGens(G, H []ECPoint, a, b []*big.Int) ECPoint { + if len(G) != len(H) || len(G) != len(a) || len(a) != len(b) { + log.Debug("TwoVectorPCommitWithGens: Arrays not of the same length", "len(G)", len(G), "len(H)", len(H), "len(a)", len(a), "len(b)", len(b)) + } + + commitment := EC.Zero() + + for i := 0; i < len(G); i++ { + modA := new(big.Int).Mod(a[i], EC.N) + modB := new(big.Int).Mod(b[i], EC.N) + + commitment = commitment.Add(G[i].Mult(modA)).Add(H[i].Mult(modB)) + } + + return commitment +} + +// The length here always has to be a power of two +func InnerProduct(a []*big.Int, b []*big.Int) *big.Int { + if len(a) != len(b) { + log.Debug("InnerProduct: Arrays not of the same length", "len(a)", len(a), "len(b", len(b)) + } + + c := big.NewInt(0) + + for i := range a { + tmp1 := new(big.Int).Mul(a[i], b[i]) + c = new(big.Int).Add(c, new(big.Int).Mod(tmp1, EC.N)) + } + + return new(big.Int).Mod(c, EC.N) +} + +func VectorAdd(v []*big.Int, w []*big.Int) []*big.Int { + if len(v) != len(w) { + log.Debug("VectorAdd: Arrays not of the same length", "len(v)", len(v), "len(w)", len(w)) + } + result := make([]*big.Int, len(v)) + + for i := range v { + result[i] = new(big.Int).Mod(new(big.Int).Add(v[i], w[i]), EC.N) + } + + return result +} + +func VectorHadamard(v, w []*big.Int) []*big.Int { + if len(v) != len(w) { + log.Debug("VectorHadamard: Arrays not of the same length", "len(v)", len(v), "len(w)", len(w)) + } + + result := make([]*big.Int, len(v)) + + for i := range v { + result[i] = new(big.Int).Mod(new(big.Int).Mul(v[i], w[i]), EC.N) + } + + return result +} + +func VectorAddScalar(v []*big.Int, s *big.Int) []*big.Int { + result := make([]*big.Int, len(v)) + + for i := range v { + result[i] = new(big.Int).Mod(new(big.Int).Add(v[i], s), EC.N) + } + + return result +} + +func ScalarVectorMul(v []*big.Int, s *big.Int) []*big.Int { + result := make([]*big.Int, len(v)) + + for i := range v { + result[i] = new(big.Int).Mod(new(big.Int).Mul(v[i], s), EC.N) + } + + return result +} + +func HashPointsToBytes(points []ECPoint) []byte { + input := []byte{} + + for index := 0; index < len(points); index++ { + pointInByte := append(PadTo32Bytes(points[index].X.Bytes()), PadTo32Bytes(points[index].Y.Bytes())...) + input = append(input, pointInByte...) + } + + return crypto.Keccak256(input) +} + +/* +InnerProd Proof +This stores the argument values +*/ +type InnerProdArg struct { + L []ECPoint + R []ECPoint + A *big.Int + B *big.Int + + Challenges []*big.Int +} + +func GenerateNewParams(G, H []ECPoint, x *big.Int, L, R, P ECPoint) ([]ECPoint, []ECPoint, ECPoint) { + nprime := len(G) / 2 + + Gprime := make([]ECPoint, nprime) + Hprime := make([]ECPoint, nprime) + + xinv := new(big.Int).ModInverse(x, EC.N) + + // Gprime = xinv * G[:nprime] + x*G[nprime:] + // Hprime = x * H[:nprime] + xinv*H[nprime:] + + for i := range Gprime { + //fmt.Printf("i: %d && i+nprime: %d\n", i, i+nprime) + Gprime[i] = G[i].Mult(xinv).Add(G[i+nprime].Mult(x)) + Hprime[i] = H[i].Mult(x).Add(H[i+nprime].Mult(xinv)) + } + + x2 := new(big.Int).Mod(new(big.Int).Mul(x, x), EC.N) + xinv2 := new(big.Int).ModInverse(x2, EC.N) + + Pprime := L.Mult(x2).Add(P).Add(R.Mult(xinv2)) // x^2 * L + P + xinv^2 * R + + return Gprime, Hprime, Pprime +} + +/* + Inner Product Argument + +Proves that =c +This is a building block for BulletProofs +*/ +func InnerProductProveSub(proof InnerProdArg, G, H []ECPoint, a []*big.Int, b []*big.Int, u ECPoint, P ECPoint) InnerProdArg { + if len(a) == 1 { + // Prover sends a & b + proof.A = a[0] + proof.B = b[0] + return proof + } + + curIt := int(math.Log2(float64(len(a)))) - 1 + + nprime := len(a) / 2 + cl := InnerProduct(a[:nprime], b[nprime:]) // either this line + cr := InnerProduct(a[nprime:], b[:nprime]) // or this line + L := TwoVectorPCommitWithGens(G[nprime:], H[:nprime], a[:nprime], b[nprime:]).Add(u.Mult(cl)) + R := TwoVectorPCommitWithGens(G[:nprime], H[nprime:], a[nprime:], b[:nprime]).Add(u.Mult(cr)) + + proof.L[curIt] = L + proof.R[curIt] = R + + // prover sends L & R and gets a challenge + // LvalInBytes := append(PadTo32Bytes(L.X.Bytes()), PadTo32Bytes(L.Y.Bytes())...) + // RvalInBytes := append(PadTo32Bytes(R.X.Bytes()), PadTo32Bytes(R.Y.Bytes())...) + // input := append(LvalInBytes, RvalInBytes...) + // s256 := crypto.Keccak256(input) + s256 := HashPointsToBytes([]ECPoint{L, R}) + + x := new(big.Int).SetBytes(s256[:]) + + proof.Challenges[curIt] = x + + Gprime, Hprime, Pprime := GenerateNewParams(G, H, x, L, R, P) + + xinv := new(big.Int).ModInverse(x, EC.N) + + // or these two lines + aprime := VectorAdd( + ScalarVectorMul(a[:nprime], x), + ScalarVectorMul(a[nprime:], xinv)) + bprime := VectorAdd( + ScalarVectorMul(b[:nprime], xinv), + ScalarVectorMul(b[nprime:], x)) + + return InnerProductProveSub(proof, Gprime, Hprime, aprime, bprime, u, Pprime) +} + +// rpresult.IPP = InnerProductProve(left, right, that, P, EC.U, EC.BPG, HPrime) +func InnerProductProve(a []*big.Int, b []*big.Int, c *big.Int, P, U ECPoint, G, H []ECPoint) InnerProdArg { + loglen := int(math.Log2(float64(len(a)))) + + challenges := make([]*big.Int, loglen+1) + Lvals := make([]ECPoint, loglen) + Rvals := make([]ECPoint, loglen) + + runningProof := InnerProdArg{ + Lvals, + Rvals, + big.NewInt(0), + big.NewInt(0), + challenges} + + // randomly generate an x value from public data + // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) + // x := crypto.Keccak256(input) + x := HashPointsToBytes([]ECPoint{P}) + + runningProof.Challenges[loglen] = new(big.Int).SetBytes(x[:]) + + Pprime := P.Add(U.Mult(new(big.Int).Mul(new(big.Int).SetBytes(x[:]), c))) + + ux := U.Mult(new(big.Int).SetBytes(x[:])) + //fmt.Printf("Prover Pprime value to run sub off of: %s\n", Pprime) + + return InnerProductProveSub(runningProof, G, H, a, b, ux, Pprime) +} + +/* + Inner Product Verify + +Given a inner product proof, verifies the correctness of the proof +Since we're using the Fiat-Shamir transform, we need to verify all x hash computations, +all g' and h' computations +P : the Pedersen commitment we are verifying is a commitment to the innner product +ipp : the proof +*/ +func InnerProductVerify(c *big.Int, P, U ECPoint, G, H []ECPoint, ipp InnerProdArg) bool { + //fmt.Println("Verifying Inner Product Argument") + //fmt.Printf("Commitment Value: %s \n", P) + // s1 := sha256.Sum256([]byte(P.X.String() + P.Y.String())) + + // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) + // s1 := crypto.Keccak256(input) + s1 := HashPointsToBytes([]ECPoint{P}) + + chal1 := new(big.Int).SetBytes(s1[:]) + ux := U.Mult(chal1) + curIt := len(ipp.Challenges) - 1 + + if ipp.Challenges[curIt].Cmp(chal1) != 0 { + log.Info("Initial Challenge Failed") + return false + } + + curIt -= 1 + + Gprime := G + Hprime := H + Pprime := P.Add(ux.Mult(c)) // line 6 from protocol 1 + //fmt.Printf("New Commitment value with u^cx: %s \n", Pprime) + + for curIt >= 0 { + Lval := ipp.L[curIt] + Rval := ipp.R[curIt] + + // prover sends L & R and gets a challenge + // s256 := sha256.Sum256([]byte( + // Lval.X.String() + Lval.Y.String() + + // Rval.X.String() + Rval.Y.String())) + // LvalInBytes := append(PadTo32Bytes(Lval.X.Bytes()), PadTo32Bytes(Lval.Y.Bytes())...) + // RvalInBytes := append(PadTo32Bytes(Rval.X.Bytes()), PadTo32Bytes(Rval.Y.Bytes())...) + // input := append(LvalInBytes, RvalInBytes...) + // s256 := crypto.Keccak256(input) + s256 := HashPointsToBytes([]ECPoint{Lval, Rval}) + + chal2 := new(big.Int).SetBytes(s256[:]) + + if ipp.Challenges[curIt].Cmp(chal2) != 0 { + log.Info("Challenge verification failed", "index", strconv.Itoa(curIt)) + return false + } + + Gprime, Hprime, Pprime = GenerateNewParams(Gprime, Hprime, chal2, Lval, Rval, Pprime) + curIt -= 1 + } + ccalc := new(big.Int).Mod(new(big.Int).Mul(ipp.A, ipp.B), EC.N) + + Pcalc1 := Gprime[0].Mult(ipp.A) + Pcalc2 := Hprime[0].Mult(ipp.B) + Pcalc3 := ux.Mult(ccalc) + Pcalc := Pcalc1.Add(Pcalc2).Add(Pcalc3) + + return Pprime.Equal(Pcalc) +} + +/* Inner Product Verify Fast +Given a inner product proof, verifies the correctness of the proof. Does the same as above except +we replace n separate exponentiations with a single multi-exponentiation. +*/ + +func InnerProductVerifyFast(c *big.Int, P, U ECPoint, G, H []ECPoint, ipp InnerProdArg) bool { + // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) + // s1 := crypto.Keccak256(input) + s1 := HashPointsToBytes([]ECPoint{P}) + + chal1 := new(big.Int).SetBytes(s1[:]) + ux := U.Mult(chal1) + curIt := len(ipp.Challenges) - 1 + + // check all challenges + if ipp.Challenges[curIt].Cmp(chal1) != 0 { + log.Debug("Initial Challenge Failed") + return false + } + + for j := curIt - 1; j >= 0; j-- { + Lval := ipp.L[j] + Rval := ipp.R[j] + + // prover sends L & R and gets a challenge + // LvalInBytes := append(PadTo32Bytes(Lval.X.Bytes()), PadTo32Bytes(Lval.Y.Bytes())...) + // RvalInBytes := append(PadTo32Bytes(Rval.X.Bytes()), PadTo32Bytes(Rval.Y.Bytes())...) + // input := append(LvalInBytes, RvalInBytes...) + // s256 := crypto.Keccak256(input) + s256 := HashPointsToBytes([]ECPoint{Lval, Rval}) + + chal2 := new(big.Int).SetBytes(s256[:]) + + if ipp.Challenges[j].Cmp(chal2) != 0 { + log.Debug("Challenge verification failed", "index", strconv.Itoa(j)) + return false + } + } + // begin computing + + curIt -= 1 + Pprime := P.Add(ux.Mult(c)) // line 6 from protocol 1 + + tmp1 := EC.Zero() + for j := curIt; j >= 0; j-- { + x2 := new(big.Int).Exp(ipp.Challenges[j], big.NewInt(2), EC.N) + x2i := new(big.Int).ModInverse(x2, EC.N) + //fmt.Println(tmp1) + tmp1 = ipp.L[j].Mult(x2).Add(ipp.R[j].Mult(x2i)).Add(tmp1) + //fmt.Println(tmp1) + } + rhs := Pprime.Add(tmp1) + //rhs=P+c*ux + L*xw+R*x2i 公式29 + + sScalars := make([]*big.Int, EC.V) + invsScalars := make([]*big.Int, EC.V) + + for i := 0; i < EC.V; i++ { + si := big.NewInt(1) + for j := curIt; j >= 0; j-- { + // original challenge if the jth bit of i is 1, inverse challenge otherwise + chal := ipp.Challenges[j] + if big.NewInt(int64(i)).Bit(j) == 0 { + chal = new(big.Int).ModInverse(chal, EC.N) + } + // fmt.Printf("Challenge raised to value: %d\n", chal) + si = new(big.Int).Mod(new(big.Int).Mul(si, chal), EC.N) + } + //fmt.Printf("Si value: %d\n", si) + sScalars[i] = si + invsScalars[i] = new(big.Int).ModInverse(si, EC.N) + } + + ccalc := new(big.Int).Mod(new(big.Int).Mul(ipp.A, ipp.B), EC.N) + lhs := TwoVectorPCommitWithGens(G, H, ScalarVectorMul(sScalars, ipp.A), ScalarVectorMul(invsScalars, ipp.B)).Add(ux.Mult(ccalc)) + + if !rhs.Equal(lhs) { + log.Info("IPVerify - Final Commitment checking failed") + return false + } + + return true +} + +// from here: https://play.golang.org/p/zciRZvD0Gr with a fix +func PadLeft(str, pad string, l int) string { + strCopy := str + for len(strCopy) < l { + strCopy = pad + strCopy + } + + return strCopy +} + +func STRNot(str string) string { + result := "" + + for _, i := range str { + if i == '0' { + result += "1" + } else { + result += "0" + } + } + return result +} + +func StrToBigIntArray(str string) []*big.Int { + result := make([]*big.Int, len(str)) + + for i := range str { + t, success := new(big.Int).SetString(string(str[i]), 10) + if success { + result[i] = t + } + } + + return result +} + +func reverse(l []*big.Int) []*big.Int { + result := make([]*big.Int, len(l)) + + for i := range l { + result[i] = l[len(l)-i-1] + } + + return result +} + +func PowerVector(l int, base *big.Int) []*big.Int { + result := make([]*big.Int, l) + + for i := 0; i < l; i++ { + result[i] = new(big.Int).Exp(base, big.NewInt(int64(i)), EC.N) + } + + return result +} + +func RandVector(l int) []*big.Int { + result := make([]*big.Int, l) + + for i := 0; i < l; i++ { + x, err := rand.Int(rand.Reader, EC.N) + check(err) + result[i] = x + } + + return result +} + +func VectorSum(y []*big.Int) *big.Int { + result := big.NewInt(0) + + for _, j := range y { + result = new(big.Int).Mod(new(big.Int).Add(result, j), EC.N) + } + + return result +} + +type RangeProof struct { + Comm ECPoint + A ECPoint + S ECPoint + T1 ECPoint + T2 ECPoint + Tau *big.Int + Th *big.Int + Mu *big.Int + IPP InnerProdArg + + // challenges + Cy *big.Int + Cz *big.Int + Cx *big.Int +} + +/* +Delta is a helper function that is used in the range proof +\delta(y, z) = (z-z^2)<1^n, y^n> - z^3<1^n, 2^n> +*/ + +func Delta(y []*big.Int, z *big.Int) *big.Int { + result := big.NewInt(0) + + // (z-z^2)<1^n, y^n> + z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N) + t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N) + t2 := new(big.Int).Mod(new(big.Int).Mul(t1, VectorSum(y)), EC.N) + + // z^3<1^n, 2^n> + z3 := new(big.Int).Mod(new(big.Int).Mul(z2, z), EC.N) + po2sum := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(EC.V)), EC.N), big.NewInt(1)) + t3 := new(big.Int).Mod(new(big.Int).Mul(z3, po2sum), EC.N) + + result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) + + return result +} + +// Calculates (aL - z*1^n) + sL*x +func CalculateL(aL, sL []*big.Int, z, x *big.Int) []*big.Int { + result := make([]*big.Int, len(aL)) + + tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z)) + tmp2 := ScalarVectorMul(sL, x) + + result = VectorAdd(tmp1, tmp2) + + return result +} + +func CalculateR(aR, sR, y, po2 []*big.Int, z, x *big.Int) []*big.Int { + if len(aR) != len(sR) || len(aR) != len(y) || len(y) != len(po2) { + log.Info("CalculateR: Arrays not of the same length") + } + + result := make([]*big.Int, len(aR)) + + z2 := new(big.Int).Exp(z, big.NewInt(2), EC.N) + tmp11 := VectorAddScalar(aR, z) + tmp12 := ScalarVectorMul(sR, x) + tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12)) + tmp2 := ScalarVectorMul(po2, z2) + + result = VectorAdd(tmp1, tmp2) + + return result +} + +// Calculates (aL - z*1^n) + sL*x +func CalculateLMRP(aL, sL []*big.Int, z, x *big.Int) []*big.Int { + result := make([]*big.Int, len(aL)) + + tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z)) + tmp2 := ScalarVectorMul(sL, x) + + result = VectorAdd(tmp1, tmp2) + + return result +} + +func CalculateRMRP(aR, sR, y, zTimesTwo []*big.Int, z, x *big.Int) []*big.Int { + if len(aR) != len(sR) || len(aR) != len(y) || len(y) != len(zTimesTwo) { + log.Info("CalculateRMRP: Arrays not of the same length") + } + + result := make([]*big.Int, len(aR)) + + tmp11 := VectorAddScalar(aR, z) + tmp12 := ScalarVectorMul(sR, x) + tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12)) + + result = VectorAdd(tmp1, zTimesTwo) + + return result +} + +/* +DeltaMRP is a helper function that is used in the multi range proof +\delta(y, z) = (z-z^2)<1^n, y^n> - \sum_j z^3+j<1^n, 2^n> +*/ + +func DeltaMRP(y []*big.Int, z *big.Int, m int) *big.Int { + result := big.NewInt(0) + + // (z-z^2)<1^n, y^n> + z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N) + t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N) + t2 := new(big.Int).Mod(new(big.Int).Mul(t1, VectorSum(y)), EC.N) + + // \sum_j z^3+j<1^n, 2^n> + // <1^n, 2^n> = 2^n - 1 + po2sum := new(big.Int).Sub( + new(big.Int).Exp( + big.NewInt(2), big.NewInt(int64(EC.V/m)), EC.N), big.NewInt(1)) + t3 := big.NewInt(0) + + for j := 0; j < m; j++ { + zp := new(big.Int).Exp(z, big.NewInt(3+int64(j)), EC.N) + tmp1 := new(big.Int).Mod(new(big.Int).Mul(zp, po2sum), EC.N) + t3 = new(big.Int).Mod(new(big.Int).Add(t3, tmp1), EC.N) + } + + result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) + + return result +} + +type MultiRangeProof struct { + Comms []ECPoint + A ECPoint + S ECPoint + T1 ECPoint + T2 ECPoint + Tau *big.Int + Th *big.Int + Mu *big.Int + IPP InnerProdArg + + // challenges + Cy *big.Int + Cz *big.Int + Cx *big.Int +} + +func serializePointArray(pa []ECPoint, serializeSize bool) []byte { + ret := []byte{} + + if serializeSize { + size := make([]byte, 4) + binary.BigEndian.PutUint32(size, uint32(len(pa))) + ret = append(ret, size[:]...) + } + + for i := 0; i < len(pa); i++ { + sp := SerializeCompressed(pa[i].toECPubKey()) + ret = append(ret, sp[:]...) + } + return ret +} + +func deserializePointArray(input []byte, numPoint uint32) ([]ECPoint, error) { + if len(input) <= 4 { + return []ECPoint{}, errors.New("input data invalid") + } + numPointSize := uint32(0) + if numPoint == 0 { + numPointSize = 4 + numPoint = binary.BigEndian.Uint32(input[0:4]) + } + if uint32(len(input)) < (numPointSize + 33*numPoint) { + return []ECPoint{}, errors.New("input data too short") + } + ret := make([]ECPoint, numPoint) + offset := numPointSize + for i := 0; i < int(numPoint); i++ { + compressed := DeserializeCompressed(curve, input[offset:offset+33]) + if compressed == nil { + return ret, errors.New("invalid input data") + } + ret[i] = *toECPoint(compressed) + offset += 33 + } + return ret, nil +} + +func (ipp *InnerProdArg) Serialize() []byte { + proof := []byte{} + + spa := serializePointArray(ipp.L, false) + proof = append(proof, spa[:]...) + + spa = serializePointArray(ipp.R, false) + proof = append(proof, spa[:]...) + + if ipp.A.Cmp(big.NewInt(0)) < 0 { + ipp.A.Mod(ipp.A, EC.N) + } + sp := PadTo32Bytes(ipp.A.Bytes()) + proof = append(proof, sp[:]...) + + sp = PadTo32Bytes(ipp.B.Bytes()) + proof = append(proof, sp[:]...) + + for i := 0; i < len(ipp.Challenges); i++ { + sp = PadTo32Bytes(ipp.Challenges[i].Bytes()) + proof = append(proof, sp[:]...) + } + + return proof +} + +func (ipp *InnerProdArg) Deserialize(proof []byte, numChallenges int) error { + if len(proof) <= 12 { + return errors.New("proof data too short") + } + offset := 0 + L, err := deserializePointArray(proof[:], uint32(numChallenges)-1) + if err != nil { + return err + } + ipp.L = append(ipp.L, L[:]...) + offset += len(L) * 33 + + R, err := deserializePointArray(proof[offset:], uint32(numChallenges)-1) + if err != nil { + return err + } + + ipp.R = append(ipp.R, R[:]...) + offset += len(R) * 33 + + if len(proof) <= offset+64+4 { + return errors.New("proof data too short") + } + + ipp.A = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + ipp.B = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + if len(proof) <= (offset + 32*numChallenges) { + return errors.New("input data too short") + } + for i := 0; i < numChallenges; i++ { + ipp.Challenges = append(ipp.Challenges, new(big.Int).SetBytes(proof[offset:offset+32])) + offset += 32 + } + return nil +} + +func (mrp *MultiRangeProof) Serialize() []byte { + proof := []byte{} + + serializedPA := serializePointArray(mrp.Comms, true) + proof = append(proof, serializedPA[:]...) + + sp := SerializeCompressed(mrp.A.toECPubKey()) + proof = append(proof, sp[:]...) + + sp = SerializeCompressed(mrp.S.toECPubKey()) + proof = append(proof, sp[:]...) + + sp = SerializeCompressed(mrp.T1.toECPubKey()) + proof = append(proof, sp[:]...) + + sp = SerializeCompressed(mrp.T2.toECPubKey()) + proof = append(proof, sp[:]...) + + //Tau, Th, Mu + sp = PadTo32Bytes(mrp.Tau.Bytes()) + proof = append(proof, sp[:]...) + + if mrp.Th.Cmp(big.NewInt(0)) < 0 { + mrp.Th.Mod(mrp.Th, EC.N) + } + sp = PadTo32Bytes(mrp.Th.Bytes()) + proof = append(proof, sp[:]...) + + sp = PadTo32Bytes(mrp.Mu.Bytes()) + proof = append(proof, sp[:]...) + + //challenges + sp = mrp.IPP.Serialize() + proof = append(proof, sp[:]...) + + //challenges + sp = PadTo32Bytes(mrp.Cy.Bytes()) + proof = append(proof, sp[:]...) + + sp = PadTo32Bytes(mrp.Cz.Bytes()) + proof = append(proof, sp[:]...) + + sp = PadTo32Bytes(mrp.Cx.Bytes()) + proof = append(proof, sp[:]...) + + return proof +} + +func (mrp *MultiRangeProof) Deserialize(proof []byte) error { + Cs, err := deserializePointArray(proof[:], 0) + if err != nil { + return err + } + mrp.Comms = append(mrp.Comms, Cs[:]...) + + offset := 4 + len(Cs)*33 + + if len(proof) <= offset+4+4*33+6*32 { + return errors.New("invalid input data") + } + compressed := DeserializeCompressed(curve, proof[offset:offset+33]) + if compressed == nil { + return errors.New("failed to decode A") + } + offset += 33 + mrp.A = *toECPoint(compressed) + + compressed = DeserializeCompressed(curve, proof[offset:offset+33]) + if compressed == nil { + return errors.New("failed to decode S") + } + offset += 33 + mrp.S = *toECPoint(compressed) + + compressed = DeserializeCompressed(curve, proof[offset:offset+33]) + if compressed == nil { + return errors.New("failed to decode T2") + } + offset += 33 + mrp.T1 = *toECPoint(compressed) + + compressed = DeserializeCompressed(curve, proof[offset:offset+33]) + if compressed == nil { + return errors.New("failed to decode T2") + } + offset += 33 + mrp.T2 = *toECPoint(compressed) + + mrp.Tau = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + mrp.Th = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + mrp.Mu = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + numChallenges := int(math.Log2(float64(len(mrp.Comms)*bitsPerValue))) + 1 + mrp.IPP.Deserialize(proof[offset:], numChallenges) + offset += len(mrp.IPP.L)*33 + len(mrp.IPP.R)*33 + len(mrp.IPP.Challenges)*32 + 2*32 + + mrp.Cy = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + mrp.Cz = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + mrp.Cx = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + return nil +} + +func pedersenCommitment(gamma *big.Int, value *big.Int) ECPoint { + return EC.G.Mult(value).Add(EC.H.Mult(gamma)) +} + +var MAX_64_BITS = new(big.Int).SetUint64(0xFFFFFFFFFFFFFFFF) + +/* +MultiRangeProof Prove +Takes in a list of values and provides an aggregate +range proof for all the values. +changes: + + all values are concatenated + r(x) is computed differently + tau_x calculation is different + delta calculation is different + +{(g, h \in G, \textbf{V} \in G^m ; \textbf{v, \gamma} \in Z_p^m) : + + V_j = h^{\gamma_j}g^{v_j} \wedge v_j \in [0, 2^n - 1] \forall j \in [1, m]} +*/ +var bitsPerValue = 64 + +func MRPProve(values []*big.Int) (MultiRangeProof, error) { + var acceptedInputNumber bool + + MRPResult := MultiRangeProof{} + + m := len(values) + + if m == 1 || m == 2 || m == 4 || m == 8 { + acceptedInputNumber = true + } + + if !acceptedInputNumber { + return MultiRangeProof{}, errors.New("Value number is not supported - just 1, 2, 4, 8") + } + + EC = genECPrimeGroupKey(m * bitsPerValue) + + // we concatenate the binary representation of the values + + PowerOfTwos := PowerVector(bitsPerValue, big.NewInt(2)) + + Comms := make([]ECPoint, m) + gammas := make([]*big.Int, m) + aLConcat := make([]*big.Int, EC.V) + aRConcat := make([]*big.Int, EC.V) + + for j := range values { + v := values[j] + if v.Cmp(big.NewInt(0)) == -1 { + return MultiRangeProof{}, errors.New("Value is below range! Not proving") + } + + if v.Cmp(MAX_64_BITS) == 1 { + return MultiRangeProof{}, errors.New("Value is above range! Not proving") + } + + if v.Cmp(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bitsPerValue)), EC.N)) == 1 { + return MultiRangeProof{}, errors.New("Value is above range! Not proving") + } + + gamma, err := rand.Int(rand.Reader, EC.N) + + check(err) + Comms[j] = pedersenCommitment(gamma, v) + gammas[j] = gamma + + // break up v into its bitwise representation + aL := reverse(StrToBigIntArray(PadLeft(fmt.Sprintf("%b", v), "0", bitsPerValue))) + aR := VectorAddScalar(aL, big.NewInt(-1)) + + for i := range aR { + aLConcat[bitsPerValue*j+i] = aL[i] + aRConcat[bitsPerValue*j+i] = aR[i] + } + } + + //compare aL, aR + MRPResult.Comms = Comms + + alpha, err := rand.Int(rand.Reader, EC.N) + check(err) + + A := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, aLConcat, aRConcat).Add(EC.H.Mult(alpha)) + MRPResult.A = A + + // fmt.Println("Ec.V %+v", EC.V) + sL := RandVector(EC.V) + sR := RandVector(EC.V) + + rho, err := rand.Int(rand.Reader, EC.N) + check(err) + + S := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, sL, sR).Add(EC.H.Mult(rho)) + MRPResult.S = S + + // input := append(PadTo32Bytes(A.X.Bytes()), PadTo32Bytes(A.Y.Bytes())...) + // chal1s256 := crypto.Keccak256(input) + chal1s256 := HashPointsToBytes(append(Comms, A)) + + // chal1s256 := sha256.Sum256([]byte(A.X.String() + A.Y.String())) + cy := new(big.Int).SetBytes(chal1s256[:]) + MRPResult.Cy = cy + + // input = append(PadTo32Bytes(S.X.Bytes()), PadTo32Bytes(S.Y.Bytes())...) + // chal2s256 := crypto.Keccak256(input) + chal2s256 := HashPointsToBytes(append(Comms, A, S)) + + // chal2s256 := sha256.Sum256([]byte(S.X.String() + S.Y.String())) + cz := new(big.Int).SetBytes(chal2s256[:]) + MRPResult.Cz = cz + + zPowersTimesTwoVec := make([]*big.Int, EC.V) + for j := 0; j < m; j++ { + zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) + for i := 0; i < bitsPerValue; i++ { + zPowersTimesTwoVec[j*bitsPerValue+i] = new(big.Int).Mod(new(big.Int).Mul(PowerOfTwos[i], zp), EC.N) + } + } + + PowerOfCY := PowerVector(EC.V, cy) + // fmt.Println(PowerOfCY) + l0 := VectorAddScalar(aLConcat, new(big.Int).Neg(cz)) + l1 := sL + r0 := VectorAdd( + VectorHadamard( + PowerOfCY, + VectorAddScalar(aRConcat, cz)), + zPowersTimesTwoVec) + r1 := VectorHadamard(sR, PowerOfCY) + + //calculate t0 + vz2 := big.NewInt(0) + z2 := new(big.Int).Mod(new(big.Int).Mul(cz, cz), EC.N) + PowerOfCZ := PowerVector(m, cz) + for j := 0; j < m; j++ { + vz2 = new(big.Int).Add(vz2, + new(big.Int).Mul( + PowerOfCZ[j], + new(big.Int).Mul(values[j], z2))) + vz2 = new(big.Int).Mod(vz2, EC.N) + } + + t0 := new(big.Int).Mod(new(big.Int).Add(vz2, DeltaMRP(PowerOfCY, cz, m)), EC.N) + + t1 := new(big.Int).Mod(new(big.Int).Add(InnerProduct(l1, r0), InnerProduct(l0, r1)), EC.N) + t2 := InnerProduct(l1, r1) + + // given the t_i values, we can generate commitments to them + tau1, err := rand.Int(rand.Reader, EC.N) + check(err) + tau2, err := rand.Int(rand.Reader, EC.N) + check(err) + + T1 := pedersenCommitment(tau1, t1) // EC.G.Mult(t1).Add(EC.H.Mult(tau1)) //commitment to t1 + T2 := pedersenCommitment(tau2, t2) //EC.G.Mult(t2).Add(EC.H.Mult(tau2)) //commitment to t2 + + MRPResult.T1 = T1 + MRPResult.T2 = T2 + + // t1Byte := append(PadTo32Bytes(T1.X.Bytes()), PadTo32Bytes(T1.Y.Bytes())...) + // t2Byte := append(PadTo32Bytes(T2.X.Bytes()), PadTo32Bytes(T2.Y.Bytes())...) + // input = append(t1Byte, t2Byte...) + // chal3s256 := crypto.Keccak256(input) + chal3s256 := HashPointsToBytes(append(Comms, A, S, T1, T2)) + + // chal3s256 := sha256.Sum256([]byte(T1.X.String() + T1.Y.String() + T2.X.String() + T2.Y.String())) + cx := new(big.Int).SetBytes(chal3s256[:]) + + MRPResult.Cx = cx + + left := CalculateLMRP(aLConcat, sL, cz, cx) + right := CalculateRMRP(aRConcat, sR, PowerOfCY, zPowersTimesTwoVec, cz, cx) + + thatPrime := new(big.Int).Mod( // t0 + t1*x + t2*x^2 + new(big.Int).Add(t0, new(big.Int).Add(new(big.Int).Mul(t1, cx), new(big.Int).Mul(new(big.Int).Mul(cx, cx), t2))), EC.N) + + that := InnerProduct(left, right) // NOTE: BP Java implementation calculates this from the t_i + + // thatPrime and that should be equal + if thatPrime.Cmp(that) != 0 { + fmt.Println("Proving -- Uh oh! Two diff ways to compute same value not working") + fmt.Printf("\tthatPrime = %s\n", thatPrime.String()) + fmt.Printf("\tthat = %s \n", that.String()) + } + + MRPResult.Th = that + + vecRandomnessTotal := big.NewInt(0) + for j := 0; j < m; j++ { + zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) + tmp1 := new(big.Int).Mul(gammas[j], zp) + vecRandomnessTotal = new(big.Int).Mod(new(big.Int).Add(vecRandomnessTotal, tmp1), EC.N) + } + //fmt.Println(vecRandomnessTotal) + taux1 := new(big.Int).Mod(new(big.Int).Mul(tau2, new(big.Int).Mul(cx, cx)), EC.N) + taux2 := new(big.Int).Mod(new(big.Int).Mul(tau1, cx), EC.N) + taux := new(big.Int).Mod(new(big.Int).Add(taux1, new(big.Int).Add(taux2, vecRandomnessTotal)), EC.N) + + MRPResult.Tau = taux + + mu := new(big.Int).Mod(new(big.Int).Add(alpha, new(big.Int).Mul(rho, cx)), EC.N) + MRPResult.Mu = mu + + HPrime := make([]ECPoint, len(EC.BPH)) + + for i := range HPrime { + HPrime[i] = EC.BPH[i].Mult(new(big.Int).ModInverse(PowerOfCY[i], EC.N)) + } + + P := TwoVectorPCommitWithGens(EC.BPG, HPrime, left, right) + //fmt.Println(P) + + MRPResult.IPP = InnerProductProve(left, right, that, P, EC.U, EC.BPG, HPrime) + + return MRPResult, nil +} + +/* +MultiRangeProof Verify +Takes in a MultiRangeProof and verifies its correctness +*/ +func MRPVerify(mrp *MultiRangeProof) bool { + m := len(mrp.Comms) + EC = genECPrimeGroupKey(m * bitsPerValue) + + //changes: + // check 1 changes since it includes all commitments + // check 2 commitment generation is also different + + // verify the challenges + // input := append(PadTo32Bytes(mrp.A.X.Bytes()), PadTo32Bytes(mrp.A.Y.Bytes())...) + // chal1s256 := crypto.Keccak256(input) + chal1s256 := HashPointsToBytes(append(mrp.Comms, mrp.A)) + + cy := new(big.Int).SetBytes(chal1s256[:]) + + if cy.Cmp(mrp.Cy) != 0 { + log.Debug("MRPVerify challenge failed!", "Cy", common.Bytes2Hex(mrp.Cy.Bytes())) + return false + } + + // input = append(PadTo32Bytes(mrp.S.X.Bytes()), PadTo32Bytes(mrp.S.Y.Bytes())...) + // chal2s256 := crypto.Keccak256(input) + chal2s256 := HashPointsToBytes(append(mrp.Comms, mrp.A, mrp.S)) + + // chal2s256 := sha256.Sum256([]byte(mrp.S.X.String() + mrp.S.Y.String())) + cz := new(big.Int).SetBytes(chal2s256[:]) + if cz.Cmp(mrp.Cz) != 0 { + log.Debug("MRPVerify challenge failed!", "Cz", common.Bytes2Hex(mrp.Cz.Bytes())) + return false + } + + // t1Byte := append(PadTo32Bytes(mrp.T1.X.Bytes()), PadTo32Bytes(mrp.T1.Y.Bytes())...) + // t2Byte := append(PadTo32Bytes(mrp.T2.X.Bytes()), PadTo32Bytes(mrp.T2.Y.Bytes())...) + // input = append(t1Byte, t2Byte...) + // chal3s256 := crypto.Keccak256(input) + chal3s256 := HashPointsToBytes(append(mrp.Comms, mrp.A, mrp.S, mrp.T1, mrp.T2)) + + // chal3s256 := sha256.Sum256([]byte(T1.X.String() + T1.Y.String() + T2.X.String() + T2.Y.String())) + // cx := new(big.Int).SetBytes(chal3s256[:]) + //chal3s256 := sha256.Sum256([]byte(mrp.T1.X.String() + mrp.T1.Y.String() + mrp.T2.X.String() + mrp.T2.Y.String())) + + cx := new(big.Int).SetBytes(chal3s256[:]) + + if cx.Cmp(mrp.Cx) != 0 { + log.Debug("MRPVerify challenge failed!", "Cx", common.Bytes2Hex(mrp.Cx.Bytes())) + return false + } + + // given challenges are correct, very range proof + PowersOfY := PowerVector(EC.V, cy) + + // t_hat * G + tau * H + lhs := pedersenCommitment(mrp.Tau, mrp.Th) //EC.G.Mult(mrp.Th).Add(EC.H.Mult(mrp.Tau)) + + // z^2 * \bold{z}^m \bold{V} + delta(y,z) * G + x * T1 + x^2 * T2 + CommPowers := EC.Zero() + PowersOfZ := PowerVector(m, cz) + z2 := new(big.Int).Mod(new(big.Int).Mul(cz, cz), EC.N) + + for j := 0; j < m; j++ { + CommPowers = CommPowers.Add(mrp.Comms[j].Mult(new(big.Int).Mul(z2, PowersOfZ[j]))) + } + + // TODO i need to change how to calculate the commitment here also ? + // need to compare and double check with privacy-client lib + rhs := EC.G.Mult(DeltaMRP(PowersOfY, cz, m)).Add( + mrp.T1.Mult(cx)).Add( + mrp.T2.Mult(new(big.Int).Mul(cx, cx))).Add(CommPowers) + + if !lhs.Equal(rhs) { + log.Debug("Rangeproof failed") + return false + } + + tmp1 := EC.Zero() + zneg := new(big.Int).Mod(new(big.Int).Neg(cz), EC.N) + for i := range EC.BPG { + tmp1 = tmp1.Add(EC.BPG[i].Mult(zneg)) + } + + PowerOfTwos := PowerVector(bitsPerValue, big.NewInt(2)) + tmp2 := EC.Zero() + // generate h' + HPrime := make([]ECPoint, len(EC.BPH)) + + for i := range HPrime { + mi := new(big.Int).ModInverse(PowersOfY[i], EC.N) + HPrime[i] = EC.BPH[i].Mult(mi) + } + + for j := 0; j < m; j++ { + for i := 0; i < bitsPerValue; i++ { + val1 := new(big.Int).Mul(cz, PowersOfY[j*bitsPerValue+i]) + zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) + val2 := new(big.Int).Mod(new(big.Int).Mul(zp, PowerOfTwos[i]), EC.N) + tmp2 = tmp2.Add(HPrime[j*bitsPerValue+i].Mult(new(big.Int).Add(val1, val2))) + } + } + + // without subtracting this value should equal muCH + l[i]G[i] + r[i]H'[i] + // we want to make sure that the innerproduct checks out, so we subtract it + P := mrp.A.Add(mrp.S.Mult(cx)).Add(tmp1).Add(tmp2).Add(EC.H.Mult(mrp.Mu).Neg()) + //fmt.Println(P) + + if !InnerProductVerifyFast(mrp.Th, P, EC.U, EC.BPG, HPrime, mrp.IPP) { + log.Debug("Range proof failed!") + return false + } + + return true +} + +// NewECPrimeGroupKey returns the curve (field), +// Generator 1 x&y, Generator 2 x&y, order of the generators +func NewECPrimeGroupKey(n int) CryptoParams { + curValue := btcec.S256().Gx + s256 := sha256.New() + gen1Vals := make([]ECPoint, n) + gen2Vals := make([]ECPoint, n) + u := ECPoint{big.NewInt(0), big.NewInt(0)} + cg := ECPoint{} + ch := ECPoint{} + + j := 0 + confirmed := 0 + for confirmed < (2*n + 3) { + s256.Write(new(big.Int).Add(curValue, big.NewInt(int64(j))).Bytes()) + + potentialXValue := make([]byte, 33) + binary.LittleEndian.PutUint32(potentialXValue, 2) + for i, elem := range s256.Sum(nil) { + potentialXValue[i+1] = elem + } + + gen2, err := btcec.ParsePubKey(potentialXValue, btcec.S256()) + if err == nil { + if confirmed == 2*n { // once we've generated all g and h values then assign this to u + u = ECPoint{gen2.X, gen2.Y} + //fmt.Println("Got that U value") + } else if confirmed == 2*n+1 { + cg = ECPoint{gen2.X, gen2.Y} + + } else if confirmed == 2*n+2 { + ch = ECPoint{gen2.X, gen2.Y} + } else { + if confirmed%2 == 0 { + gen1Vals[confirmed/2] = ECPoint{gen2.X, gen2.Y} + //fmt.Println("new G Value") + } else { + gen2Vals[confirmed/2] = ECPoint{gen2.X, gen2.Y} + //fmt.Println("new H value") + } + } + confirmed += 1 + } + j += 1 + } + + return CryptoParams{ + btcec.S256(), + btcec.S256(), + gen1Vals, + gen2Vals, + btcec.S256().N, + u, + n, + cg, + ch} +} + +func genECPrimeGroupKey(n int) CryptoParams { + // curValue := btcec.S256().Gx + // s256 := sha256.New() + gen1Vals := make([]ECPoint, n) + gen2Vals := make([]ECPoint, n) + // u := ECPoint{big.NewInt(0), big.NewInt(0)} + hx, _ := new(big.Int).SetString("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0", 16) + hy, _ := new(big.Int).SetString("31d3c6863973926e049e637cb1b5f40a36dac28af1766968c30c2313f3a38904", 16) + ch := ECPoint{hx, hy} + + gx, _ := new(big.Int).SetString("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16) + gy, _ := new(big.Int).SetString("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16) + cg := ECPoint{gx, gy} + + i := 0 + for i < n { + gen2Vals[i] = ch.Mult( + big.NewInt(int64(i*2 + 1)), + ) + gen1Vals[i] = cg.Mult( + big.NewInt(int64(i*2 + 2)), + ) + i++ + } + + u := cg.Mult( + big.NewInt(int64(n + 3)), + ) + + return CryptoParams{ + btcec.S256(), + btcec.S256(), + gen1Vals, + gen2Vals, + btcec.S256().N, + u, + n, + ch, + cg} +} + +func init() { + // just need the base parameter N, P, G, H for this init, ignore everything else + EC = CryptoParams{ + btcec.S256(), + btcec.S256(), + make([]ECPoint, VecLength), + make([]ECPoint, VecLength), + btcec.S256().N, + ECPoint{big.NewInt(0), big.NewInt(0)}, + VecLength, + ECPoint{big.NewInt(0), big.NewInt(0)}, + ECPoint{big.NewInt(0), big.NewInt(0)}} +} diff --git a/core/vm/privacy/bulletproof.json b/core/vm/privacy/bulletproof.json new file mode 100644 index 000000000000..263dd7927099 --- /dev/null +++ b/core/vm/privacy/bulletproof.json @@ -0,0 +1 @@ +{"Ipp":{"L":[{"x":"7be22788517d8c7e149727c5397cf7b2d6713546dee4f974f3239a1f53e64f44","y":"b18c08695e40b801ace64f00bf7010245dcf972cad7ce1e97e441fa68575bb72"},{"x":"29956e85820c1ede4f6ec987b3c20cbf7c0fc0aa6a8cd823e73105fc681c1880","y":"228e434578a40d247a2b5f1c2a7a5154716fb06e98d8087b3dce9cc85429b72c"},{"x":"b39703b5680857d7629a39374451baae59459418c23f44f522838efd952e69db","y":"60033aa9807a0c081c530c30292eca71f61e17433513ead9caadce7997d97981"},{"x":"074cd428559b2bf9c0a6d54479254bdeedc26e655b7f0d822aec9a6d1f1068b3","y":"915958a8e24fefe8ec5e6338661567c8e6178d03ac35422284701bf2ab37d592"},{"x":"80d27e070c49f6d41d49254295a9e70ebaf82d8780c93f1c48f018609e3822dd","y":"5c5e365b72835ff1e24ff3a130e1cc332e36d891ba383752e469a667ecf5db01"},{"x":"6e9c63c3188c29d593417b5cb96ab0f149d905a194ace7a3e523d15f8f8922cd","y":"a91566f2cae911542a0658f63451b4a87cea5c668ab4220c974a640442021da6"}],"R":[{"x":"34f512b1fe0989ce77f8cb5b3a95dd31b55c38d4b6278e4923d6cf224d59c421","y":"8757d403eb39a181418db28f742f0ff3f59991929800c424ebce792f27353335"},{"x":"900bc03a4e26a61bb11de434ee150970a22dce77320b26fea1b172dbdfef5b10","y":"de9d1b833decc36f84ae4ce1be87414674ebef7b58009563f35a722347eb76d7"},{"x":"0ca13ff9d0a929b417a165ceb42c532dd54c34e8f1e9083208630dadafdb5ee4","y":"0bba4e3ac3b20cde8074d4683742f924bdb070a590c1d9d7c8f239748359015c"},{"x":"c29728cbb8393b65f8f224e587eccee9d332d2936b36bb460a9f9df720ef5add","y":"88295fbbe05f25534a43ac528a890b9a26ab3bf95a63e4803168d56b430190ce"},{"x":"4cd804793a0169e0bfe1206907cec530a733efb7cca86f08bff8d04bfb82394b","y":"453594ec2090934e75348db0db070c79b6302cf289d1907adbc198493fee417e"},{"x":"9d9a42ba4b30cc68841ce6528f0d4ffa7990b35fc533f7cb0e4bf0d26cc9c6b6","y":"d6dbdb179262c2bc1fc734d4e20a2c54c5e0b8b8d0c48ee630e146f4cef1f486"}],"A":"-2d23144bda297742e1eafb40911ccdbf2f5e950e8b73b4eaf8f78b857d5c7a93","B":"162325c6d1160e0c033135e32b57351c17ef79755cf4f19c6d6c0ba8117e8d09","Challenges":["139fb4a37d387933e43987862d67297c65af5302c6995bfe7181646dcfc169f1","d43aa9b8fdd97b12e3c4bfabf4c36c59dabb08ace9d5da221f676cc7244000e0","3013fdc8605a4dd479898c2b9abbab45ed9b9676267d8c86a0b7b737448c3d8e","b7a16a537783cd7a9e5bddad4536754a6b16a152fedd2df46a36f76a51cd66d9","5408a38e71aa569b6ddc2ccc84a3b2fa29253905072bf8ed5035406cda89e6fa","1ab61f234a5982567e51af82efd118ebcb44befabfaa5f07183471508e3f4208","3e666d903ccd1312a9a83506b298dfac2c2f11f796087d91104487a577802faa"]},"Comms":["71a29edddcf580c0ab4c992f34df4bcd7f23079bf29c07267e354c0e1b0c7b7d0d7d7f55259df2570dc37a2b6719eb58b2195f06163d29b7aca710cbf9b66560"],"A":"e7d28764b37d275ee1b0cc589ab2aa46cc60a406d6cb0def47dcb5fc49b3279dc65b2eb160328db9dc5953a263a5eba0855b51c66a5e23ac0a3b997d060643ed","S":"78aa79b16298d8c7cc587c8fe521d13b9c768256a8842e74e82970507efea3b9735e4324787071ded1e049c888aee40fe85cff0fff4ffdc4d2d3dc2a17ac306a","cy":"3e38e29bf805dc855b687f969a1a854a0c23fddb8ff1f480079929518e8df1f6","cz":"d794749e1c1442762943a40422a6f9bd284f938fce652c382fea05a0e33ca68f","T1":"45e56a5bc134b7b19d53670d57b45874a01442b58a853dd0960f4a7ec1a093f59be5d93e35a55f2006f03cf9c31801942d9a8739a9408c67d93c4dff6c9f7346","T2":"ad380e6564ae44cd6f91656e66c031cfe21aa47cc511ae51e4a2a8604bd181cd181be8eb1b8e0de71458eed4ce538667a392cc0e7c4e85ab7f4cff54161c8087","cx":"75327f647ab5124eefa92e51f769cdb64a24fe5f88a059b002f2d590c9236894","Th":"-ee380a27a0b95643f9a3a5a975c9b35638ba2a7105a36c7ffd90acdd16029323","Tau":"38059dcf3dfc1da9646f3dc66c8b1487c3375577e751e8341b2addd45ef06306","Mu":"e648b63a1857d1e9723b01ac3151c29fd723845f8aa2a7d6ae867feba8db53e5"} \ No newline at end of file diff --git a/core/vm/privacy/bulletproof_test.go b/core/vm/privacy/bulletproof_test.go new file mode 100644 index 000000000000..8088ac98e9d3 --- /dev/null +++ b/core/vm/privacy/bulletproof_test.go @@ -0,0 +1,527 @@ +package privacy + +import ( + "crypto/rand" + "encoding/json" + "fmt" + "io" + "math/big" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestInnerProductProveLen1(t *testing.T) { + fmt.Println("TestInnerProductProve1") + EC = genECPrimeGroupKey(1) + a := make([]*big.Int, 1) + b := make([]*big.Int, 1) + + a[0] = big.NewInt(1) + + b[0] = big.NewInt(1) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductProveLen2(t *testing.T) { + fmt.Println("TestInnerProductProve2") + EC = genECPrimeGroupKey(2) + a := make([]*big.Int, 2) + b := make([]*big.Int, 2) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + + b[0] = big.NewInt(1) + b[1] = big.NewInt(1) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + fmt.Println("P after two vector commitment with gen ", P) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductProveLen4(t *testing.T) { + fmt.Println("TestInnerProductProve4") + EC = genECPrimeGroupKey(4) + a := make([]*big.Int, 4) + b := make([]*big.Int, 4) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + a[2] = big.NewInt(1) + a[3] = big.NewInt(1) + + b[0] = big.NewInt(1) + b[1] = big.NewInt(1) + b[2] = big.NewInt(1) + b[3] = big.NewInt(1) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductProveLen8(t *testing.T) { + fmt.Println("TestInnerProductProve8") + EC = genECPrimeGroupKey(8) + a := make([]*big.Int, 8) + b := make([]*big.Int, 8) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + a[2] = big.NewInt(1) + a[3] = big.NewInt(1) + a[4] = big.NewInt(1) + a[5] = big.NewInt(1) + a[6] = big.NewInt(1) + a[7] = big.NewInt(1) + + b[0] = big.NewInt(2) + b[1] = big.NewInt(2) + b[2] = big.NewInt(2) + b[3] = big.NewInt(2) + b[4] = big.NewInt(2) + b[5] = big.NewInt(2) + b[6] = big.NewInt(2) + b[7] = big.NewInt(2) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductProveLen64Rand(t *testing.T) { + fmt.Println("TestInnerProductProveLen64Rand") + EC = genECPrimeGroupKey(64) + a := RandVector(64) + b := RandVector(64) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + fmt.Printf("Values Used: \n\ta = %s\n\tb = %s\n", a, b) + } + +} + +func TestInnerProductVerifyFastLen1(t *testing.T) { + fmt.Println("TestInnerProductProve1") + EC = genECPrimeGroupKey(1) + a := make([]*big.Int, 1) + b := make([]*big.Int, 1) + + a[0] = big.NewInt(2) + + b[0] = big.NewInt(2) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductVerifyFastLen2(t *testing.T) { + fmt.Println("TestInnerProductProve2") + EC = genECPrimeGroupKey(2) + a := make([]*big.Int, 2) + b := make([]*big.Int, 2) + + a[0] = big.NewInt(2) + a[1] = big.NewInt(3) + + b[0] = big.NewInt(2) + b[1] = big.NewInt(3) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductVerifyFastLen4(t *testing.T) { + fmt.Println("TestInnerProductProve4") + EC = genECPrimeGroupKey(4) + a := make([]*big.Int, 4) + b := make([]*big.Int, 4) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + a[2] = big.NewInt(1) + a[3] = big.NewInt(1) + + b[0] = big.NewInt(1) + b[1] = big.NewInt(1) + b[2] = big.NewInt(1) + b[3] = big.NewInt(1) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductVerifyFastLen8(t *testing.T) { + fmt.Println("TestInnerProductProve8") + EC = genECPrimeGroupKey(8) + a := make([]*big.Int, 8) + b := make([]*big.Int, 8) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + a[2] = big.NewInt(1) + a[3] = big.NewInt(1) + a[4] = big.NewInt(1) + a[5] = big.NewInt(1) + a[6] = big.NewInt(1) + a[7] = big.NewInt(1) + + b[0] = big.NewInt(2) + b[1] = big.NewInt(2) + b[2] = big.NewInt(2) + b[3] = big.NewInt(2) + b[4] = big.NewInt(2) + b[5] = big.NewInt(2) + b[6] = big.NewInt(2) + b[7] = big.NewInt(2) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductVerifyFastLen64Rand(t *testing.T) { + fmt.Println("TestInnerProductProveLen64Rand") + EC = genECPrimeGroupKey(64) + a := RandVector(64) + b := RandVector(64) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + fmt.Printf("Values Used: \n\ta = %s\n\tb = %s\n", a, b) + } + +} + +func TestMRPProveZERO(t *testing.T) { + + mRangeProof, _ := MRPProve([]*big.Int{ + new(big.Int).SetInt64(0), + }) + mv := MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") +} + +func TestMRPProve_MAX_2_POW_64(t *testing.T) { + + mRangeProof, _ := MRPProve([]*big.Int{ + new(big.Int).SetUint64(0xFFFFFFFFFFFFFFFF), + }) + mv := MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") +} + +func TestMRPProveOutOfSupportedRange(t *testing.T) { + + value, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFF", 16) + _, err := MRPProve([]*big.Int{ + value, + }) + assert.NotNil(t, err, " MRProof incorrect") +} + +func TestMRPProve_RANDOM(t *testing.T) { + + mRangeProof, _ := MRPProve(Rand64Vector(1)) + mv := MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") + + mRangeProof, _ = MRPProve(Rand64Vector(2)) + mv = MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") + + mRangeProof, _ = MRPProve(Rand64Vector(4)) + mv = MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") + + mRangeProof, _ = MRPProve(Rand64Vector(8)) + mv = MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") +} + +func Rand64Vector(l int) []*big.Int { + result := make([]*big.Int, l) + + for i := 0; i < l; i++ { + x, err := rand.Int(rand.Reader, big.NewInt(0xFFFFFFFFFFFFFFF)) + check(err) + result[i] = x + } + + return result +} + +func TestMRPProveValueNumberNotSupported(t *testing.T) { + + _, err := MRPProve(Rand64Vector(3)) + assert.NotNil(t, err, "MRProof incorrect - accepted 3 inputs") + + _, err = MRPProve(Rand64Vector(5)) + assert.NotNil(t, err, "MRProof incorrect - accepted 5 inputs") + + _, err = MRPProve(Rand64Vector(6)) + assert.NotNil(t, err, "MRProof incorrect - accepted 6 inputs") + + _, err = MRPProve(Rand64Vector(7)) + assert.NotNil(t, err, "MRProof incorrect - accepted 7 inputs") + + _, err = MRPProve(Rand64Vector(10)) + assert.NotNil(t, err, "MRProof incorrect - accepted 10 inputs") + + _, err = MRPProve(Rand64Vector(1)) + assert.Nil(t, err, "MRProof incorrect - not accepted 1 inputs") + + _, err = MRPProve(Rand64Vector(2)) + assert.Nil(t, err, "MRProof incorrect - not accepted 2 inputs") + + _, err = MRPProve(Rand64Vector(4)) + fmt.Println(err) + assert.Nil(t, err, "MRProof incorrect - not accepted 4 inputs") + + _, err = MRPProve(Rand64Vector(8)) + assert.Nil(t, err, "MRProof incorrect - not accepted 8 inputs") +} + +type Point struct { + x string + y string +} + +type IPP struct { + L []map[string]string `json:"L"` + R []map[string]string `json:"R"` + A string `json:"A"` + B string `json:"B"` + Challenges []string `json:"Challenges"` +} + +type BulletProof struct { + Comms []string `json:"Comms"` + A string `json:"A"` + S string `json:"S"` + Cx string `json:"Cx"` + Cy string `json:"Cy"` + Cz string `json:"Cz"` + T1 string `json:"T1"` + T2 string `json:"T2"` + Th string `json:"Th"` + Tau string `json:"Tau"` + Mu string `json:"Mu"` + Ipp IPP `json:"Ipp"` +} + +func parseTestData(filePath string) MultiRangeProof { + jsonFile, err := os.Open(filePath) + + if err != nil { + fmt.Println(err) + } + + defer jsonFile.Close() + + byteValue, _ := io.ReadAll(jsonFile) + + // we initialize our Users array + // var result map[string]interface{} + result := BulletProof{} + + json.Unmarshal([]byte(byteValue), &result) + + fmt.Println("result ", result.Tau) + fmt.Println("result ", result.Th) + fmt.Println("result.Ipp ", result.Ipp) + + ipp := result.Ipp + + proof := MultiRangeProof{ + Comms: MapECPointFromHex(result.Comms, ECPointFromHex), + A: ECPointFromHex(result.A), + S: ECPointFromHex(result.S), + T1: ECPointFromHex(result.T1), + T2: ECPointFromHex(result.T2), + Th: bigIFromHex(result.Th), + Tau: bigIFromHex(result.Tau), + Mu: bigIFromHex(result.Mu), + Cx: bigIFromHex(result.Cx), + Cy: bigIFromHex(result.Cy), + Cz: bigIFromHex(result.Cz), + IPP: InnerProdArg{ + L: MapECPoint(ipp.L, ECPointFromPoint), + R: MapECPoint(ipp.R, ECPointFromPoint), + A: bigIFromHex(ipp.A), + B: bigIFromHex(ipp.B), + Challenges: MapBigI(ipp.Challenges, bigIFromHex), + }, + } + + fmt.Println(proof) + return proof +} + +/* +* +Utils for parsing data from json +*/ +func MapBigI(list []string, f func(string) *big.Int) []*big.Int { + result := make([]*big.Int, len(list)) + + for i, item := range list { + result[i] = f(item) + } + return result +} + +func MapECPointFromHex(list []string, f func(string) ECPoint) []ECPoint { + result := make([]ECPoint, len(list)) + + for i, item := range list { + result[i] = f(item) + } + return result +} + +func MapECPoint(list []map[string]string, f func(Point) ECPoint) []ECPoint { + result := make([]ECPoint, len(list)) + + for i, item := range list { + result[i] = f(Point{ + x: item["x"], + y: item["y"], + }) + } + return result +} + +func bigIFromHex(hex string) *big.Int { + tmp, _ := new(big.Int).SetString(hex, 16) + return tmp +} + +func ECPointFromHex(hex string) ECPoint { + Px, _ := new(big.Int).SetString(hex[:64], 16) + Py, _ := new(big.Int).SetString(hex[64:], 16) + P := ECPoint{Px, Py} + return P +} + +func ECPointFromPoint(ecpoint Point) ECPoint { + Px, _ := new(big.Int).SetString(ecpoint.x, 16) + Py, _ := new(big.Int).SetString(ecpoint.y, 16) + P := ECPoint{Px, Py} + return P +} + +func TestMRPGeneration(t *testing.T) { + values := make([]*big.Int, 2) + values[0] = big.NewInt(1000) + values[1] = big.NewInt(100000) + mrp, err := MRPProve(values) + if err != nil { + t.Error("failed to generate bulletproof") + } + + v := MRPVerify(&mrp) + serilizedBp := mrp.Serialize() + + newMRP := new(MultiRangeProof) + if newMRP.Deserialize(serilizedBp) != nil { + t.Error("failed to deserialized bulletproof") + } + + v = v && MRPVerify(newMRP) + + if !v { + t.Error("failed to verify bulletproof") + } +} diff --git a/core/vm/privacy/ringct.go b/core/vm/privacy/ringct.go new file mode 100644 index 000000000000..93c97c56cee1 --- /dev/null +++ b/core/vm/privacy/ringct.go @@ -0,0 +1,632 @@ +package privacy + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" + + "github.com/XinFinOrg/XDPoSChain/log" +) + +// These constants define the lengths of serialized public keys. +const ( + PubKeyBytesLenCompressed = 33 + PubKeyBytesLenUncompressed = 65 + PubKeyBytesLenHybrid = 65 +) + +const ( + pubkeyCompressed byte = 0x2 // y_bit + x coord + pubkeyUncompressed byte = 0x4 // x coord + y coord + pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord +) + +// The proof contains pretty much stuffs +// The proof contains pretty much stuffs +// Ring size rs: 1 byte => proof[0] +// num input: number of real inputs: 1 byte => proof[1] +// List of inputs/UTXO index typed uint64 => total size = rs * numInput * 8 = proof[0]*proof[1]*8 +// List of key images: total size = numInput * 33 = proof[1] * 33 +// number of output n: 1 byte +// List of output => n * 130 bytes +// transaction fee: uint256 => 32 byte +// ringCT proof size ctSize: uint16 => 2 byte +// ringCT proof: ctSize bytes +// bulletproofs: bp +type PrivateSendVerifier struct { + proof []byte + //ringCT RingCT +} + +type Ring []*ecdsa.PublicKey + +type RingSignature struct { + NumRing int + Size int // size of ring + M [32]byte // message + C *big.Int // ring signature value, 1 element + S [][]*big.Int // ring signature values: [NumRing][Size] + Ring []Ring // array of rings of pubkeys: [NumRing] + I []*ecdsa.PublicKey // key images, size = the number of rings [NumRing] + Curve elliptic.Curve + SerializedRing []byte //temporary memory stored the raw ring ct used in case of verifying ringCT with message verification +} + +func (p *PrivateSendVerifier) verify() bool { + return false +} + +func (p *PrivateSendVerifier) deserialize() { + +} + +// helper function, returns type of v +func typeof(v interface{}) string { + return fmt.Sprintf("%T", v) +} + +func isOdd(a *big.Int) bool { + return a.Bit(0) == 1 +} + +// SerializeCompressed serializes a public key in a 33-byte compressed format. +func SerializeCompressed(p *ecdsa.PublicKey) []byte { + b := make([]byte, 0, PubKeyBytesLenCompressed) + format := pubkeyCompressed + if isOdd(p.Y) { + format |= 0x1 + } + b = append(b, format) + return append(b, PadTo32Bytes(p.X.Bytes())...) +} + +func DeserializeCompressed(curve elliptic.Curve, b []byte) *ecdsa.PublicKey { + x := new(big.Int).SetBytes(b[1:33]) + // Y = +-sqrt(x^3 + B) + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + x3.Add(x3, curve.Params().B) + + // now calculate sqrt mod p of x2 + B + // This code used to do a full sqrt based on tonelli/shanks, + // but this was replaced by the algorithms referenced in + // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 + PPlus1Div4 := new(big.Int).Add(curve.Params().P, big.NewInt(1)) + PPlus1Div4 = PPlus1Div4.Div(PPlus1Div4, big.NewInt(4)) + y := new(big.Int).Exp(x3, PPlus1Div4, curve.Params().P) + ybit := b[0]%2 == 1 + if ybit != isOdd(y) { + y.Sub(curve.Params().P, y) + } + if ybit != isOdd(y) { + return nil + } + return &ecdsa.PublicKey{curve, x, y} +} + +// bytes returns the public key ring as a byte slice. +func (r Ring) Bytes() (b []byte) { + for _, pub := range r { + b = append(b, PadTo32Bytes(pub.X.Bytes())...) + b = append(b, PadTo32Bytes(pub.Y.Bytes())...) + } + return +} + +func PadTo32Bytes(in []byte) []byte { + padded := make([]byte, 32) + if len(in) >= 32 { + copy(padded, in) + } else { + copy(padded[32-len(in):], in) + } + + return padded +} + +// converts the signature to a byte array +// this is the format that will be used when passing EVM bytecode +func (r *RingSignature) Serialize() ([]byte, error) { + sig := []byte{} + // add size and message + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(r.NumRing)) + sig = append(sig, b[:]...) // 8 bytes + + b = make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(r.Size)) + sig = append(sig, b[:]...) // 8 bytes + + sig = append(sig, PadTo32Bytes(r.M[:])...) // 32 bytes + sig = append(sig, PadTo32Bytes(r.C.Bytes())...) // 32 bytes + + for k := 0; k < r.NumRing; k++ { + // 96 bytes each iteration + for i := 0; i < r.Size; i++ { + sig = append(sig, PadTo32Bytes(r.S[k][i].Bytes())...) + } + } + for k := 0; k < r.NumRing; k++ { + // 96 bytes each iteration + for i := 0; i < r.Size; i++ { + rb := SerializeCompressed(r.Ring[k][i]) + sig = append(sig, rb...) + } + } + + for k := 0; k < r.NumRing; k++ { + // 64 bytes + rb := SerializeCompressed(r.I[k]) + sig = append(sig, rb...) + } + + if len(sig) != 8+8+32+32+(32+33)*r.NumRing*r.Size+33*r.NumRing { + return []byte{}, errors.New("Could not serialize ring signature") + } + + return sig, nil +} + +func computeSignatureSize(numRing int, ringSize int) int { + const MaxInt = int(^uint(0) >> 1) + + if numRing < 0 || ringSize < 0 { + return -1 + } + + // Calculate term and check for overflow + + term := numRing * ringSize * 65 + + if term < 0 || term < numRing || term < ringSize { + return -1 + } + + return 8 + 8 + 32 + 32 + numRing*ringSize*32 + numRing*ringSize*33 + numRing*33 +} + +// deserializes the byteified signature into a RingSignature struct +func Deserialize(r []byte) (*RingSignature, error) { + if len(r) < 16 { + return nil, errors.New("Failed to deserialize ring signature") + } + offset := 0 + sig := new(RingSignature) + numRing := r[offset : offset+8] + offset += 8 + size := r[offset : offset+8] + offset += 8 + + size_uint := binary.BigEndian.Uint64(size) + size_int := int(size_uint) + sig.Size = size_int + + size_uint = binary.BigEndian.Uint64(numRing) + size_int = int(size_uint) + sig.NumRing = size_int + + if len(r) != computeSignatureSize(sig.NumRing, sig.Size) { + return nil, fmt.Errorf("incorrect ring size, len r: %d, sig.NumRing: %d sig.Size: %d", len(r), sig.NumRing, sig.Size) + } + + m := r[offset : offset+32] + offset += 32 + + var m_byte [32]byte + copy(m_byte[:], m) + + sig.M = m_byte + sig.C = new(big.Int).SetBytes(r[offset : offset+32]) + offset += 32 + + sig.S = make([][]*big.Int, sig.NumRing) + for i := 0; i < sig.NumRing; i++ { + sig.S[i] = make([]*big.Int, sig.Size) + for j := 0; j < sig.Size; j++ { + sig.S[i][j] = new(big.Int).SetBytes(r[offset : offset+32]) + offset += 32 + } + } + + sig.Curve = crypto.S256() + + sig.Ring = make([]Ring, sig.NumRing) + for i := 0; i < sig.NumRing; i++ { + sig.Ring[i] = make([]*ecdsa.PublicKey, sig.Size) + for j := 0; j < sig.Size; j++ { + compressedKey := r[offset : offset+33] + offset += 33 + compressedPubKey := DeserializeCompressed(sig.Curve, compressedKey) + sig.Ring[i][j] = compressedPubKey + } + } + + sig.I = make([]*ecdsa.PublicKey, sig.NumRing) + for i := 0; i < sig.NumRing; i++ { + compressedKey := r[offset : offset+33] + offset += 33 + compressedPubKey := DeserializeCompressed(sig.Curve, compressedKey) + sig.I[i] = compressedPubKey + } + + sig.SerializedRing = r + + if !Verify(sig, false) { + return nil, errors.New("failed to deserialize, invalid ring signature") + } + + return sig, nil +} + +// takes public key ring and places the public key corresponding to `privkey` in index s of the ring +// returns a key ring of type []*ecdsa.PublicKey +func GenKeyRing(ring []*ecdsa.PublicKey, privkey *ecdsa.PrivateKey, s int) ([]*ecdsa.PublicKey, error) { + size := len(ring) + 1 + new_ring := make([]*ecdsa.PublicKey, size) + pubkey := privkey.Public().(*ecdsa.PublicKey) + + if s > len(ring) { + return nil, errors.New("index s out of bounds") + } + + new_ring[s] = pubkey + for i := 1; i < size; i++ { + idx := (i + s) % size + new_ring[idx] = ring[i-1] + } + + return new_ring, nil +} + +// creates a ring with size specified by `size` and places the public key corresponding to `privkey` in index s of the ring +// returns a new key ring of type []*ecdsa.PublicKey +func GenNewKeyRing(size int, privkey *ecdsa.PrivateKey, s int) ([]*ecdsa.PublicKey, error) { + ring := make([]*ecdsa.PublicKey, size) + pubkey := privkey.Public().(*ecdsa.PublicKey) + + if s >= len(ring) { + return nil, errors.New("index s out of bounds") + } + + ring[s] = pubkey + + for i := 1; i < size; i++ { + idx := (i + s) % size + priv, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + + pub := priv.Public() + ring[idx] = pub.(*ecdsa.PublicKey) + } + + return ring, nil +} + +// calculate key image I = x * H_p(P) where H_p is a hash function that returns a point +// H_p(P) = sha3(P) * G +func GenKeyImage(privkey *ecdsa.PrivateKey) *ecdsa.PublicKey { + pubkey := privkey.Public().(*ecdsa.PublicKey) + image := new(ecdsa.PublicKey) + + // calculate sha3(P) + h_x, h_y := HashPoint(pubkey) + + // calculate H_p(P) = x * sha3(P) * G + i_x, i_y := privkey.Curve.ScalarMult(h_x, h_y, privkey.D.Bytes()) + + image.X = i_x + image.Y = i_y + return image +} + +func HashPoint(p *ecdsa.PublicKey) (*big.Int, *big.Int) { + input := append(PadTo32Bytes(p.X.Bytes()), PadTo32Bytes(p.Y.Bytes())...) + log.Info("HashPoint", "input ", common.Bytes2Hex(input)) + hash := crypto.Keccak256(input) + log.Info("HashPoint", "hash ", common.Bytes2Hex(hash)) + return p.Curve.ScalarBaseMult(hash[:]) +} + +// create ring signature from list of public keys given inputs: +// msg: byte array, message to be signed +// ring: array of *ecdsa.PublicKeys to be included in the ring +// privkey: *ecdsa.PrivateKey of signer +// s: index of signer in ring +func Sign(m [32]byte, rings []Ring, privkeys []*ecdsa.PrivateKey, s int) (*RingSignature, error) { + numRing := len(rings) + if numRing < 1 { + return nil, errors.New("there is no ring to make signature") + } + // check ringsize > 1 + ringsize := len(rings[0]) + if ringsize < 2 { + return nil, errors.New("size of ring less than two") + } else if s >= ringsize || s < 0 { + return nil, errors.New("secret index out of range of ring size") + } + + // setup + //pubkey := privkey.Public().(*ecdsa.PublicKey) + pubkeys := make([]*ecdsa.PublicKey, numRing) + for i := 0; i < numRing; i++ { + pubkeys[i] = &privkeys[i].PublicKey + } + //cast to BitCurve used in go-eth since elliptic.Curve.Add() and elliptic.Curve.ScalarMult() is deprecated + curve := pubkeys[0].Curve.(*secp256k1.BitCurve) + sig := new(RingSignature) + sig.Size = ringsize + sig.NumRing = numRing + sig.M = m + sig.Ring = rings + sig.Curve = curve + + // check that key at index s is indeed the signer + for i := 0; i < numRing; i++ { + if rings[i][s] != pubkeys[i] { + return nil, errors.New("secret index in ring is not signer") + } + } + + // generate key image + images := make([]*ecdsa.PublicKey, numRing) + for i := 0; i < numRing; i++ { + images[i] = GenKeyImage(privkeys[i]) + } + sig.I = images + + // start at c[1] + // pick random scalar u (glue value), calculate c[1] = H(m, u*G) where H is a hash function and G is the base point of the curve + C := make([]*big.Int, ringsize) + S := make([][]*big.Int, numRing) + for i := 0; i < numRing; i++ { + S[i] = make([]*big.Int, ringsize) + } + + //Initialize S except S[..][s] + for i := 0; i < numRing; i++ { + for j := 0; j < ringsize; j++ { + if j != s { + randomGenerated, err := rand.Int(rand.Reader, curve.Params().P) + if err != nil { + return nil, err + } + S[i][j] = randomGenerated + } + } + } + + L := make([][]*ecdsa.PublicKey, numRing) + R := make([][]*ecdsa.PublicKey, numRing) + for i := 0; i < numRing; i++ { + L[i] = make([]*ecdsa.PublicKey, ringsize) + R[i] = make([]*ecdsa.PublicKey, ringsize) + } + alpha := make([]*big.Int, numRing) + + var l []byte + //compute L[i][s], R[i][s], i = 0..numRing + for i := 0; i < numRing; i++ { + randomGenerated, err := rand.Int(rand.Reader, curve.Params().P) + if err != nil { + return nil, err + } + alpha[i] = randomGenerated + // start at secret index s/PI + // compute L_s = u*G + l_x, l_y := curve.ScalarBaseMult(PadTo32Bytes(alpha[i].Bytes())) + L[i][s] = &ecdsa.PublicKey{curve, l_x, l_y} + lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) + l = append(l, lT...) + // compute R_s = u*H_p(P[s]) + h_x, h_y := HashPoint(pubkeys[i]) + r_x, r_y := curve.ScalarMult(h_x, h_y, PadTo32Bytes(alpha[i].Bytes())) + R[i][s] = &ecdsa.PublicKey{curve, r_x, r_y} + rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) + l = append(l, rT...) + } + + // concatenate m and u*G and calculate c[s+1] = H(m, L_s, R_s) + C_j := crypto.Keccak256(append(m[:], l...)) + idx := s + 1 + if idx == ringsize { + idx = 0 + } + if idx == 0 { + C[0] = new(big.Int).SetBytes(C_j[:]) + } else { + C[idx] = new(big.Int).SetBytes(C_j[:]) + } + for idx != s { + var l []byte + for j := 0; j < numRing; j++ { + // calculate L[j][idx] = s[j][idx]*G + c[idx]*Ring[j][idx] + px, py := curve.ScalarMult(rings[j][idx].X, rings[j][idx].Y, PadTo32Bytes(C[idx].Bytes())) // px, py = c_i*P_i + sx, sy := curve.ScalarBaseMult(PadTo32Bytes(S[j][idx].Bytes())) // sx, sy = s[n-1]*G + if px == nil || py == nil || sx == nil || sy == nil { + return nil, errors.New("Could not create ring signature") + } + l_x, l_y := curve.Add(sx, sy, px, py) + L[j][idx] = &ecdsa.PublicKey{curve, l_x, l_y} + lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) + l = append(l, lT...) + + // calculate R[j][idx] = s[j][idx]*H_p(Ring[j][idx]) + c[idx]*I[j] + px, py = curve.ScalarMult(images[j].X, images[j].Y, C[idx].Bytes()) // px, py = c_i*I + hx, hy := HashPoint(rings[j][idx]) + sx, sy = curve.ScalarMult(hx, hy, S[j][idx].Bytes()) // sx, sy = s[n-1]*H_p(P_i) + if px == nil || py == nil || sx == nil || sy == nil { + return nil, errors.New("Could not create ring signature") + } + r_x, r_y := curve.Add(sx, sy, px, py) + R[j][idx] = &ecdsa.PublicKey{curve, r_x, r_y} + rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) + l = append(l, rT...) + } + + idx++ + if idx == ringsize { + idx = 0 + } + + var ciIdx int + if idx == 0 { + ciIdx = 0 + } else { + ciIdx = idx + } + cSha := crypto.Keccak256(append(PadTo32Bytes(m[:]), l...)) + C[ciIdx] = new(big.Int).SetBytes(cSha[:]) + } + + //compute S[j][s] = alpha[j] - c[s] * privkeys[j], privkeys[j] = private key corresponding to key image I[j] + for j := 0; j < numRing; j++ { + cx := C[s] + // close ring by finding S[j][s] = (alpha[j] - c[s]*privkeys[s] ) mod P where k[s] is the private key and P is the order of the curve + S[j][s] = new(big.Int).Mod(new(big.Int).Sub(alpha[j], new(big.Int).Mul(cx, privkeys[j].D)), curve.Params().N) + } + + // everything ok, add values to signature + sig.S = S + sig.C = C[0] + sig.NumRing = numRing + sig.Size = ringsize + sig.C = C[0] + + return sig, nil +} + +// verify ring signature contained in RingSignature struct +// returns true if a valid signature, false otherwise +func Verify(sig *RingSignature, verifyMes bool) bool { + // setup + rings := sig.Ring + ringsize := sig.Size + numRing := sig.NumRing + S := sig.S + C := make([]*big.Int, ringsize+1) + C[0] = sig.C + //cast to BitCurve used in go-eth since elliptic.Curve.Add() and elliptic.Curve.ScalarMult() is deprecated + curve := sig.Curve.(*secp256k1.BitCurve) + image := sig.I + + //check on curve + for i := 0; i < numRing; i++ { + onCurve := curve.IsOnCurve(image[i].X, image[i].Y) + if !onCurve { + return false + } + for j := 0; j < ringsize; j++ { + onCurve := curve.IsOnCurve(rings[i][j].X, rings[i][j].Y) + if !onCurve { + return false + } + } + } + + // calculate c[i+1] = H(m, s[i]*G + c[i]*P[i]) + // and c[0] = H)(m, s[n-1]*G + c[n-1]*P[n-1]) where n is the ring size + //log.Info("C", "0", common.Bytes2Hex(C[0].Bytes())) + for j := 0; j < ringsize; j++ { + var l []byte + for i := 0; i < numRing; i++ { + // Validate S[i][j] and C[j] + if !isValidScalar(S[i][j], curve) || !isValidScalar(C[j], curve) { + return false // Or handle the error as required + } + + // calculate L[i][j] = s[i][j]*G + c[j]*Ring[i][j] + px, py := curve.ScalarMult(rings[i][j].X, rings[i][j].Y, C[j].Bytes()) // px, py = c_i*P_i + sx, sy := curve.ScalarBaseMult(S[i][j].Bytes()) // sx, sy = s[i]*G + if px == nil || py == nil || sx == nil || sy == nil { + return false + } + l_x, l_y := curve.Add(sx, sy, px, py) + lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) + //log.Info("L[i][j]", "i", i, "j", j, "L", common.Bytes2Hex(lT)) + l = append(l, lT...) + + // calculate R_i = s[i][j]*H_p(Ring[i][j]) + c[j]*I[j] + px, py = curve.ScalarMult(image[i].X, image[i].Y, C[j].Bytes()) // px, py = c[i]*I + hx, hy := HashPoint(rings[i][j]) + + // Validate S[i][j], hx, and hy + if !isValidScalar(S[i][j], curve) || !isValidScalar(hx, curve) || !isValidScalar(hy, curve) { + return false // Or handle the error as required + } + //log.Info("H[i][j]", "i", i, "j", j, "x.input", common.Bytes2Hex(rings[i][j].X.Bytes()), "y.input", common.Bytes2Hex(rings[i][j].Y.Bytes())) + //log.Info("H[i][j]", "i", i, "j", j, "x", common.Bytes2Hex(hx.Bytes()), "y", common.Bytes2Hex(hy.Bytes())) + sx, sy = curve.ScalarMult(hx, hy, S[i][j].Bytes()) // sx, sy = s[i]*H_p(P[i]) + if px == nil || py == nil || sx == nil || sy == nil { + return false + } + r_x, r_y := curve.Add(sx, sy, px, py) + rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) + //log.Info("R[i][j]", "i", i, "j", j, "L", common.Bytes2Hex(rT)) + l = append(l, rT...) + } + + // calculate c[i+1] = H(m, L_i, R_i) + //cj_mes := append(PadTo32Bytes(sig.M[:]), l...) + C_j := crypto.Keccak256(append(PadTo32Bytes(sig.M[:]), l...)) + //log.Info("C hash input", "j", j + 1, "C_input", common.Bytes2Hex(cj_mes)) + + /*if j == ringsize-1 { + C[0] = new(big.Int).SetBytes(C_j[:]) + } else {*/ + C[j+1] = new(big.Int).SetBytes(C_j[:]) + //log.Info("C", "j", j + 1, "C", common.Bytes2Hex(C[j + 1].Bytes())) + //} + } + + return bytes.Equal(sig.C.Bytes(), C[ringsize].Bytes()) +} + +func isValidScalar(scalar *big.Int, curve elliptic.Curve) bool { + return scalar.Sign() >= 0 && scalar.Cmp(curve.Params().N) < 0 +} + +func Link(sig_a *RingSignature, sig_b *RingSignature) bool { + for i := 0; i < len(sig_a.I); i++ { + for j := 0; j < len(sig_b.I); j++ { + if sig_a.I[i].X == sig_b.I[j].X && sig_a.I[i].Y == sig_b.I[j].Y { + return true + } + } + } + return false +} + +// function returns(mutiple rings, private keys, message, error) +func GenerateMultiRingParams(numRing int, ringSize int, s int) (rings []Ring, privkeys []*ecdsa.PrivateKey, m [32]byte, err error) { + for i := 0; i < numRing; i++ { + privkey, err := crypto.GenerateKey() + if err != nil { + return nil, nil, [32]byte{}, err + } + privkeys = append(privkeys, privkey) + + ring, err := GenNewKeyRing(ringSize, privkey, s) + if err != nil { + return nil, nil, [32]byte{}, err + } + rings = append(rings, ring) + } + + _, err = rand.Read(m[:]) + if err != nil { + return nil, nil, [32]byte{}, err + } + return rings, privkeys, m, nil +} diff --git a/core/vm/privacy/ringct_test.go b/core/vm/privacy/ringct_test.go new file mode 100644 index 000000000000..3e5b771c0bce --- /dev/null +++ b/core/vm/privacy/ringct_test.go @@ -0,0 +1,324 @@ +package privacy + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/stretchr/testify/assert" + + "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" +) + +func TestSign(t *testing.T) { + /*for i := 14; i < 15; i++ { + for j := 14; j < 15; j++ { + for k := 0; k <= j; k++ {*/ + numRing := 5 + ringSize := 10 + s := 9 + fmt.Println("Generate random ring parameter ") + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + + fmt.Println("numRing ", numRing) + fmt.Println("ringSize ", ringSize) + fmt.Println("index of real one ", s) + + fmt.Println("Ring ", rings) + fmt.Println("privkeys ", privkeys) + fmt.Println("m ", m) + + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("Failed to create Ring signature") + } + + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + + deserializedSig, err := Deserialize(sig) + if err != nil { + t.Error("Failed to Deserialize Ring signature") + } + verified := Verify(deserializedSig, false) + + if !verified { + t.Error("Failed to verify Ring signature") + } + +} + +func TestDeserialize(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 5 + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("Failed to create Ring signature") + } + + // A normal signature. + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + + // Modify the serialized signature s.t. + // the new signature passes the length check + // but triggers buffer overflow in Deserialize(). + // ringSize: 10 -> 56759212534490939 + // len(sig): 3495 -> 3804 + // 80 + 5 * (56759212534490939*65 + 33) = 18446744073709551616 + 3804 + bs := make([]byte, 8) + binary.BigEndian.PutUint64(bs, 56759212534490939) + for i := 0; i < 8; i++ { + sig[i+8] = bs[i] + } + tail := make([]byte, 3804-len(sig)) + sig = append(sig, tail...) + + _, err = Deserialize(sig) + assert.EqualError(t, err, "incorrect ring size, len r: 3804, sig.NumRing: 5 sig.Size: 56759212534490939") +} + +func TestVerify1(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 7 + + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + if err != nil { + t.Error("fail to generate rings") + } + + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("fail to create ring signature") + } + + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("fail to serialize input ring signature") + } + + deserializedSig, err := Deserialize(sig) + if err != nil { + t.Error("fail to deserialize ring signature") + } + + assert.True(t, Verify(deserializedSig, false), "Verify should return true") +} + +func TestDeserialize2(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 7 + + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + if err != nil { + t.Error("fail to generate rings") + } + + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("fail to create ring signature") + } + + // change one sig to the scalar field + ringSignature.S[0][0] = curve.Params().N + + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("fail to serialize input ring signature") + } + + _, err = Deserialize(sig) + assert.EqualError(t, err, "failed to deserialize, invalid ring signature") +} + +func TestPadTo32Bytes(t *testing.T) { + arr := [44]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34} + + // test input slice is longer than 32 bytes + assert.True(t, bytes.Equal(PadTo32Bytes(arr[0:]), arr[0:32]), "Test PadTo32Bytes longer than 32 bytes #1") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[1:]), arr[1:33]), "Test PadTo32Bytes longer than 32 bytes #2") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[2:]), arr[2:34]), "Test PadTo32Bytes longer than 32 bytes #3") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[3:]), arr[3:35]), "Test PadTo32Bytes longer than 32 bytes #4") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[4:]), arr[4:36]), "Test PadTo32Bytes longer than 32 bytes #5") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[5:]), arr[5:37]), "Test PadTo32Bytes longer than 32 bytes #6") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[6:]), arr[6:38]), "Test PadTo32Bytes longer than 32 bytes #7") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[7:]), arr[7:39]), "Test PadTo32Bytes longer than 32 bytes #8") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[8:]), arr[8:40]), "Test PadTo32Bytes longer than 32 bytes #9") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[9:]), arr[9:41]), "Test PadTo32Bytes longer than 32 bytes #10") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:]), arr[10:42]), "Test PadTo32Bytes longer than 32 bytes #11") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[11:]), arr[11:43]), "Test PadTo32Bytes longer than 32 bytes #12") + + // test input slice is equal 32 bytes + assert.True(t, bytes.Equal(PadTo32Bytes(arr[0:32]), arr[0:32]), "Test PadTo32Bytes equal 32 bytes #1") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[1:33]), arr[1:33]), "Test PadTo32Bytes equal 32 bytes #2") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[2:34]), arr[2:34]), "Test PadTo32Bytes equal 32 bytes #3") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[3:35]), arr[3:35]), "Test PadTo32Bytes equal 32 bytes #4") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[4:36]), arr[4:36]), "Test PadTo32Bytes equal 32 bytes #5") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[5:37]), arr[5:37]), "Test PadTo32Bytes equal 32 bytes #6") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[6:38]), arr[6:38]), "Test PadTo32Bytes equal 32 bytes #7") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[7:39]), arr[7:39]), "Test PadTo32Bytes equal 32 bytes #8") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[8:40]), arr[8:40]), "Test PadTo32Bytes equal 32 bytes #9") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[9:41]), arr[9:41]), "Test PadTo32Bytes equal 32 bytes #10") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:42]), arr[10:42]), "Test PadTo32Bytes equal 32 bytes #11") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[11:43]), arr[11:43]), "Test PadTo32Bytes equal 32 bytes #12") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[12:44]), arr[12:44]), "Test PadTo32Bytes equal 32 bytes #13") + + // test input slice is shorter than 32 bytes + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:32]), arr[0:32]), "Test PadTo32Bytes shorter than 32 bytes #1") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:33]), arr[1:33]), "Test PadTo32Bytes shorter than 32 bytes #2") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:34]), arr[2:34]), "Test PadTo32Bytes shorter than 32 bytes #3") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:35]), arr[3:35]), "Test PadTo32Bytes shorter than 32 bytes #4") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:36]), arr[4:36]), "Test PadTo32Bytes shorter than 32 bytes #5") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:37]), arr[5:37]), "Test PadTo32Bytes shorter than 32 bytes #6") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:38]), arr[6:38]), "Test PadTo32Bytes shorter than 32 bytes #7") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:39]), arr[7:39]), "Test PadTo32Bytes shorter than 32 bytes #8") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:40]), arr[8:40]), "Test PadTo32Bytes shorter than 32 bytes #9") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:41]), arr[9:41]), "Test PadTo32Bytes shorter than 32 bytes #10") +} + +func TestCurveAddNegative(t *testing.T) { + curve := crypto.S256().(*secp256k1.BitCurve) + + x1, y1 := curve.ScalarBaseMult(new(big.Int).SetUint64(uint64(2)).Bytes()) + fmt.Printf("Point(%x, %x)\n", x1, y1) + + x2 := x1 + y2 := new(big.Int).Neg(y1) // negative of point (x1,y1) + + x3, y3 := curve.Add(x1, y1, x2, y2) + fmt.Printf("Output is Point(%x, %x)\n", x3, y3) + + x0 := new(big.Int).SetUint64(uint64(0)) + y0 := new(big.Int).SetUint64(uint64(0)) // infinity + + if (x3.Cmp(x0) == 0) && (y3.Cmp(y0) == 0) { + // fmt.Printf("Correct, add negative of self should yield (0,0)") + } else { + t.Error("Incorrect, add negative of self did not yield (0,0)") + } +} + +func TestCurveAddZero(t *testing.T) { + // curve := crypto.S256() + curve := crypto.S256().(*secp256k1.BitCurve) + + x1, y1 := curve.ScalarBaseMult(new(big.Int).SetUint64(uint64(1)).Bytes()) + fmt.Printf("Point(%x, %x)\n", x1, y1) + + x0 := new(big.Int).SetUint64(uint64(0)) + y0 := new(big.Int).SetUint64(uint64(0)) // infinity + fmt.Printf("Is point (%d,%d) on the curve: %t \n", x0, y0, curve.IsOnCurve(x0, y0)) + + x2, y2 := curve.Add(x1, y1, x0, y0) + fmt.Printf("Output is Point(%x, %x)\n", x2, y2) + + if (x1.Cmp(x2) == 0) && (y1.Cmp(y2) == 0) { + // fmt.Printf("Correct, Point on curve is the same after Zero addition\n") + } else { + t.Error("Incorrect, Point on curve changed after Zero addition\n") + } +} + +func TestOnCurveVerify(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 5 + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("Failed to create Ring signature") + } + + valid := Verify(ringSignature, false) + if !valid { + t.Error("Incorrect, unmodified ringSignature should be valid") + } + + ringsModified := ringSignature.Ring + ringsModified[0][0].X = big.NewInt(1) + ringsModified[0][0].Y = big.NewInt(1) + valid = Verify(ringSignature, false) + if valid { + t.Error("Incorrect, modified ringSignature should be invalid") + } +} + +func TestOnCurveDeserialize(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 5 + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("Failed to create Ring signature") + } + + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + _, err = Deserialize(sig) + if err != nil { + t.Error("Failed to Deserialize") + } + + ringsModified := ringSignature.Ring + ringsModified[0][0].X = big.NewInt(1) + ringsModified[0][0].Y = big.NewInt(1) + + sig, err = ringSignature.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + _, err = Deserialize(sig) + assert.EqualError(t, err, "failed to deserialize, invalid ring signature") +} + +func TestCurveScalarMult(t *testing.T) { + curve := crypto.S256().(*secp256k1.BitCurve) + + x, y := curve.ScalarBaseMult(curve.Params().N.Bytes()) + if x == nil && y == nil { + fmt.Println("Scalar multiplication with base point returns nil when scalar is the scalar field") + } + + x2, y2 := curve.ScalarMult(new(big.Int).SetUint64(uint64(100)), new(big.Int).SetUint64(uint64(2)), curve.Params().N.Bytes()) + if x2 == nil && y2 == nil { + fmt.Println("Scalar multiplication with a point (not necessarily on curve) returns nil when scalar is the scalar field") + } +} + +func TestNilPointerDereferencePanic(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 7 + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + + ringSig, err := Sign(m, rings, privkeys, s) + if err != nil { + fmt.Println("Failed to set up") + } + + ringSig.S[0][0] = curve.Params().N // change one sig to the scalar field + + sig, err := ringSig.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + + _ , err = Deserialize(sig) + // Should failed to verify Ring signature as the signature is invalid + assert.EqualError(t, err, "failed to deserialize, invalid ring signature") +}