diff --git a/.gitignore b/.gitignore index daa4145..41d2545 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ __pycache__ .coverage.* /node_modules package-lock.json +lextab.py +yacctab.py diff --git a/.solhint.json b/.solhint.json new file mode 100644 index 0000000..35f4806 --- /dev/null +++ b/.solhint.json @@ -0,0 +1,15 @@ +{ + "extends": "default", + "rules": { + "indent": false, + "var-name-mixedcase": false, + "func-name-mixedcase": false, + "func-param-name-mixedcase": false, + "not-rely-on-time": false, + "bracket-align": false, + "expression-indent": false, + "max-line-length": false, + "two-lines-top-level-separator": false, + "separate-by-one-line-in-contract": false + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index 83a3125..876b6ac 100644 --- a/Makefile +++ b/Makefile @@ -105,7 +105,7 @@ coverage-html: ####################################################################### -lint: python-pyflakes python-pylint cxx-lint +lint: python-pyflakes python-pylint cxx-lint solidity-lint python-pyflakes: $(PYTHON) -mpyflakes $(NAME) @@ -141,6 +141,13 @@ mac-dependencies: ####################################################################### +solidity-lint: + $(NPM) run lint + + +####################################################################### + + nvm-install: ./utils/nvm-install nvm install --lts diff --git a/appendix/ejubjub.sage b/appendix/ejubjub.sage index 9ef1670..0396587 100644 --- a/appendix/ejubjub.sage +++ b/appendix/ejubjub.sage @@ -1,19 +1,7 @@ -p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 -Fp = GF(p) -R.=QQ[] -A=2*(a+d)/(a-d) -B=4/(a-d) -S=R.quotient(a*x^2+y^2-(1+d*x^2*y^2)) -u=(1+y)/(1-y) -v=(1+y)/((1-y)*x) -0 == S((B*v^2-u^3-A*u^2-u).numerator()) - - JUBJUB_C = 8 # Cofactor JUBJUB_A = 168700 # Coefficient A JUBJUB_D = 168696 # Coefficient D MONT_A = 168698 - p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 Fp = GF(p) E = EllipticCurve(Fp, [0, MONT_A, 0, 1, 0]) @@ -23,6 +11,18 @@ assert E.order() == 218882428718392752222464057452572750886145117772685380736017 assert E.quadratic_twist().order() == 21888242871839275222246405745257275088482217023563530613794683085564038006908 # factor(E.quadratic_twist().order()) == 2^2 * 5472060717959818805561601436314318772120554255890882653448670771391009501727 +assert E.trace_of_frobenius() not in [0, 1] + +twistCofactor = 4 +curveCofactor = 8 + +curveOrder = E.order() + +twistOrder = 2 * (p+1) - curveOrder +assert E.quadratic_twist().order() == twistOrder +assert is_prime(twistOrder // twistCofactor) +assert is_prime(E.order() // curveCofactor) + jubjub_valid = lambda x, y: (JUBJUB_A * x^2 + y^2) == 1 + JUBJUB_D * x^2 * y^2 mont_valid = lambda x, y: E.is_on_curve(x, y) mont_to_jubjub = lambda x, y: (x/y, (x-1)/(x+1)) diff --git a/contracts/EdDSA.sol b/contracts/EdDSA.sol new file mode 100644 index 0000000..e51a252 --- /dev/null +++ b/contracts/EdDSA.sol @@ -0,0 +1,41 @@ +// Copyright (c) 2018 @HarryR +// License: LGPL-3.0+ + +pragma solidity 0.4.24; + +import "./JubJub.sol"; + + +contract EdDSA +{ + function HashToInt( bytes data ) + public pure returns (uint256) + { + uint256 hashed = uint256(sha256(data)); + + // (2<<249) - 1 + uint256 mask = 1809251394333065553493296640760748560207343510400633813116524750123642650623; + + return hashed & mask; + } + + function Verify( uint256[2] pubkey, uint256 hashed_msg, uint256[2] R, uint256 s ) + public view returns (bool) + { + uint256[2] memory B = JubJub.Generator(); + uint256[2] memory lhs; + uint256[2] memory rhs; + + (lhs[0], lhs[1]) = JubJub.scalarMult(B[0], B[1], s); + + uint256 t = HashToInt(abi.encodePacked( + R[0], R[1], + pubkey[0], pubkey[1], + hashed_msg + )); + + (rhs[0], rhs[1]) = JubJub.scalarMult(pubkey[0], pubkey[1], t); + + return lhs[0] == rhs[0] && lhs[1] == rhs[1]; + } +} diff --git a/contracts/JubJub.sol b/contracts/JubJub.sol index 4736290..9386d7b 100644 --- a/contracts/JubJub.sol +++ b/contracts/JubJub.sol @@ -2,138 +2,142 @@ // Copyright (c) 2018 @yondonfu // License: LGPL-3.0+ -pragma solidity ^0.4.19; +pragma solidity 0.4.24; library JubJub { - // A should be a square in Q - uint256 constant JUBJUB_A = 168700; + // A should be a square in Q + uint256 constant public JUBJUB_A = 168700; - // D should not be a square in Q - uint256 constant JUBJUB_D = 168696; + // D should not be a square in Q + uint256 constant public JUBJUB_D = 168696; - uint256 constant COFACTOR = 8; + uint256 constant public COFACTOR = 8; - // XXX: is Q square? - uint256 constant Q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant public Q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - // XXX: is R square? - uint256 constant R = 2736030358979909402780800718157159386076813972158567259200215660948447373041; + // L * COFACTOR = Curve Order + uint256 constant public L = 2736030358979909402780800718157159386076813972158567259200215660948447373041; - struct Point { - uint x; - uint y; - } + function Generator() + internal pure returns (uint256[2]) + { + return +[17777552123799933955779906779655732241715742912184938656739573121738514868268, + 2626589144620713026669568689430873010625803728049924121243784502389097019475]; + } + + + function submod(uint256 a, uint256 b, uint256 modulus) + internal pure returns (uint256) + { + uint256 n = a; - function submod(uint256 a, uint256 b, uint256 modulus) - internal pure returns (uint256) - { - uint256 n = a; + if (a <= b) { + n += modulus; + } - if( a <= b ) - n += modulus; + return (n - b) % modulus; + } - return (n - b) % modulus; - } + function modexp(uint256 base, uint256 exponent, uint256 modulus) + internal view returns (uint256) + { + uint256[1] memory output; + uint256[6] memory input; + input[0] = 0x20; + input[1] = 0x20; + input[2] = 0x20; + input[3] = base; + input[4] = exponent; + input[5] = modulus; + + bool success; + assembly { + success := staticcall(sub(gas, 2000), 5, input, 0xc0, output, 0x20) + } + require(success); + return output[0]; + } + + + function inv(uint256 value, uint256 field_modulus) + internal view returns (uint256) + { + return modexp(value, field_modulus - 2, field_modulus); + } - function modexp(uint256 base, uint256 exponent, uint256 modulus) - internal view returns (uint256) - { - uint256[1] memory output; - uint256[6] memory input; - input[0] = 0x20; - input[1] = 0x20; - input[2] = 0x20; - input[3] = base; - input[4] = exponent; - input[5] = modulus; - bool success; - assembly { - success := staticcall(sub(gas, 2000), 5, input, 0xc0, output, 0x20) - } - require(success); - return output[0]; - } + function scalarMult(uint256 x, uint256 y, uint256 value) + internal view returns (uint256, uint256) + { + uint256[4] memory p; + (p[0], p[1], p[2]) = pointToEac(x, y); + p[3] = 1; + uint256[4] memory a = [uint256(0), uint256(1), uint256(0), uint256(1)]; - function inv(uint256 value, uint256 field_modulus) - internal view returns (uint256) - { - return modexp(value, field_modulus - 2, field_modulus); - } + uint256 i = 0; + while (value != 0) + { + if ((value & 1) != 0) + { + a = etecAdd(a, p); + } - function scalarMult(uint256 x, uint256 y, uint256 value) - internal view returns (uint256, uint256) - { - uint256[4] memory p; - (p[0], p[1], p[2]) = pointToEac(x, y); - p[3] = 1; + p = etecAdd(p, p); - uint256[4] memory a = [uint256(0), uint256(1), uint256(0), uint256(1)]; + value = value / 2; - uint256 i = 0; + i += 1; + } - while( value != 0 ) - { - if( (value & 1) != 0 ) - { - a = etecAdd(a, p); - } + return etecToPoint(a[0], a[1], a[2], a[3]); + } - p = etecAdd(p, p); - value = value / 2; + /** + * Project X,Y point to extended affine coordinates + */ + function pointToEac( uint256 X, uint256 Y ) + internal pure returns (uint256, uint256, uint256) + { + return (X, Y, mulmod(X, Y, Q)); + } - i += 1; - } - return etecToPoint(a[0], a[1], a[2], a[3]); - } + /** + * Extended twisted edwards coordinates to extended affine coordinates + */ + function etecToEac( uint256 X, uint256 Y, uint256 T, uint256 Z ) + internal view returns (uint256, uint256, uint256) + { + Z = inv(Z, Q); + return (mulmod(X, Z, Q), mulmod(Y, Z, Q), mulmod(T, Z, Q)); + } - /** - * Project X,Y point to extended affine coordinates - */ - function pointToEac( uint256 X, uint256 Y ) - internal pure returns (uint256, uint256, uint256) - { - return (X, Y, mulmod(X, Y, Q)); - } + /** + * Extended twisted edwards coordinates to extended affine coordinates + */ + function etecToPoint( uint256 X, uint256 Y, uint256 T, uint256 Z ) + internal view returns (uint256, uint256) + { + Z = inv(Z, Q); + return (mulmod(X, Z, Q), mulmod(Y, Z, Q)); + } - /** - * Extended twisted edwards coordinates to extended affine coordinates - */ - function etecToEac( uint256 X, uint256 Y, uint256 T, uint256 Z ) - internal view returns (uint256, uint256, uint256) - { - Z = inv(Z, Q); - return (mulmod(X, Z, Q), mulmod(Y, Z, Q), mulmod(T, Z, Q)); - } - + function eacToPoint( uint256 X, uint256 Y, uint256 T ) + internal pure returns (uint256, uint256) + { + return (X, Y); + } - /** - * Extended twisted edwards coordinates to extended affine coordinates - */ - function etecToPoint( uint256 X, uint256 Y, uint256 T, uint256 Z ) - internal view returns (uint256, uint256) - { - Z = inv(Z, Q); - return (mulmod(X, Z, Q), mulmod(Y, Z, Q)); - } - - - function eacToPoint( uint256 X, uint256 Y, uint256 T ) - internal pure returns (uint256, uint256) - { - return (X, Y); - } - - /** + /** * @dev Add 2 etec points on baby jubjub curve * x3 = (x1y2 + y1x2) * (z1z2 - dt1t2) * y3 = (y1y2 - ax1x2) * (z1z2 + dt1t2) @@ -148,7 +152,7 @@ library JubJub pure returns (uint256[4] p3) { - // inf + (x,y) = (x,y) + // inf + (x,y) = (x,y) if (_p1[0] == 0 && _p1[1] == 1 && _p1[2] == 0 && _p1[3] == 1) { return _p2; } @@ -208,46 +212,46 @@ library JubJub } } - function pointAdd(uint256[2] self, uint256[2] other) - internal view returns (uint256[2]) - { - if( self[0] == 0 && self[1] == 0 ) { - return other; - } - else if( other[0] == 0 && other[1] == 0 ) { - return self; - } + function pointAdd(uint256[2] self, uint256[2] other) + internal view returns (uint256[2]) + { + if (self[0] == 0 && self[1] == 0) { + return other; + } else if (other[0] == 0 && other[1] == 0) + { + return self; + } - uint256 x1x2 = mulmod(self[0], other[0], Q); - uint256 y1y2 = mulmod(self[1], other[1], Q); + uint256 x1x2 = mulmod(self[0], other[0], Q); + uint256 y1y2 = mulmod(self[1], other[1], Q); - // ---------------- + // ---------------- - // (x1*y2 + y1*x2) - uint256 x3_lhs = addmod(mulmod(self[0], other[1], Q), mulmod(self[1], other[0], Q), Q); + // (x1*y2 + y1*x2) + uint256 x3_lhs = addmod(mulmod(self[0], other[1], Q), mulmod(self[1], other[0], Q), Q); - // JUBJUB_D*x1*x2*y1*y2 - uint256 dx1x2y1y2 = mulmod(mulmod(JUBJUB_D, x1x2, Q), y1y2, Q); + // JUBJUB_D*x1*x2*y1*y2 + uint256 dx1x2y1y2 = mulmod(mulmod(JUBJUB_D, x1x2, Q), y1y2, Q); - // (Fq.ONE + JUBJUB_D*u1*u2*v1*v2) - uint256 x3_rhs = addmod(1, dx1x2y1y2, Q); + // (Fq.ONE + JUBJUB_D*u1*u2*v1*v2) + uint256 x3_rhs = addmod(1, dx1x2y1y2, Q); - // (Fq.ONE - JUBJUB_D*u1*u2*v1*v2) - uint256 y3_rhs = submod(1, dx1x2y1y2, Q); + // (Fq.ONE - JUBJUB_D*u1*u2*v1*v2) + uint256 y3_rhs = submod(1, dx1x2y1y2, Q); - // (y1*y2 - A*x1*x2) - uint256 y3_lhs = submod(y1y2, mulmod(JUBJUB_A, x1x2, Q), Q); + // (y1*y2 - A*x1*x2) + uint256 y3_lhs = submod(y1y2, mulmod(JUBJUB_A, x1x2, Q), Q); - // ---------------- + // ---------------- - // lhs / rhs - return [ - // x3 = (x1*y2 + y1*x2) / (Fq.ONE + D*x1*x2*y1*y2) - mulmod(x3_lhs, inv(x3_rhs, Q), Q), + // lhs / rhs + return [ + // x3 = (x1*y2 + y1*x2) / (Fq.ONE + D*x1*x2*y1*y2) + mulmod(x3_lhs, inv(x3_rhs, Q), Q), - // y3 = (y1*y2 - A*x1*x2) / (Fq.ONE - D*x1*x2*y1*y2) - mulmod(y3_lhs, inv(y3_rhs, Q), Q) - ]; - } + // y3 = (y1*y2 - A*x1*x2) / (Fq.ONE - D*x1*x2*y1*y2) + mulmod(y3_lhs, inv(y3_rhs, Q), Q) + ]; + } } diff --git a/contracts/JubJubPublic.sol b/contracts/JubJubPublic.sol index 39b1736..5cdcb79 100644 --- a/contracts/JubJubPublic.sol +++ b/contracts/JubJubPublic.sol @@ -2,37 +2,37 @@ // Copyright (c) 2018 @yondonfu // License: LGPL-3.0+ -pragma solidity ^0.4.24; +pragma solidity 0.4.24; import "./JubJub.sol"; contract JubJubPublic { - function pointAddViaEtec(uint256[2] a, uint256[2] b) - public view returns (uint256[2]) - { - uint256[4] memory p; - uint256[4] memory q; - uint256[4] memory r; - (p[0], p[1], p[2]) = JubJub.pointToEac(a[0], b[0]); - (q[0], q[1], q[2]) = JubJub.pointToEac(a[0], b[0]); - p[3] = 1; - q[3] = 1; - r = JubJub.etecAdd(p, q); + function pointAddViaEtec(uint256[2] a, uint256[2] b) + public view returns (uint256[2]) + { + uint256[4] memory p; + uint256[4] memory q; + uint256[4] memory r; + (p[0], p[1], p[2]) = JubJub.pointToEac(a[0], b[0]); + (q[0], q[1], q[2]) = JubJub.pointToEac(a[0], b[0]); + p[3] = 1; + q[3] = 1; + r = JubJub.etecAdd(p, q); - (p[0], p[1]) = JubJub.etecToPoint(r[0], r[1], r[2], r[3]); - return [p[0], p[1]]; - } + (p[0], p[1]) = JubJub.etecToPoint(r[0], r[1], r[2], r[3]); + return [p[0], p[1]]; + } - function pointAdd(uint256[2] a, uint256[2] b) - public view returns (uint256[2]) - { - return JubJub.pointAdd(a, b); - } + function pointAdd(uint256[2] a, uint256[2] b) + public view returns (uint256[2]) + { + return JubJub.pointAdd(a, b); + } - function scalarMult(uint256[2] a, uint256 s) - public view returns (uint256, uint256) - { - return JubJub.scalarMult(a[0], a[1], s); - } + function scalarMult(uint256[2] a, uint256 s) + public view returns (uint256, uint256) + { + return JubJub.scalarMult(a[0], a[1], s); + } } diff --git a/contracts/LongsightL.sol b/contracts/LongsightL.sol index 553c6a3..56cb660 100644 --- a/contracts/LongsightL.sol +++ b/contracts/LongsightL.sol @@ -1,12 +1,12 @@ // Copyright (c) 2018 HarryR // License: LGPL-3.0+ -pragma solidity ^0.4.24; +pragma solidity 0.4.24; library LongsightL { // altBN curve order - uint256 constant SCALAR_FIELD = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001; + uint256 constant public SCALAR_FIELD = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001; function GetScalarField() internal pure returns (uint256) @@ -20,14 +20,12 @@ library LongsightL function LongsightL_round( uint256 in_x, uint256 in_k, uint256 in_C ) internal pure returns (uint256 out_x) { - uint256 t; - uint256 j; - - t = addmod(in_x, in_C, SCALAR_FIELD); - t = addmod(t, in_k, SCALAR_FIELD); - j = mulmod(t, t, SCALAR_FIELD); // t^2 - j = mulmod(j, j, SCALAR_FIELD); // t^4 - out_x = mulmod(j, t, SCALAR_FIELD); // t^5 + assembly { + let localQ := 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001 + let t := addmod(addmod(in_x, in_C, localQ), in_k, localQ) + let j := mulmod(t, t, localQ) + out_x := mulmod(mulmod(j, j, localQ), t, localQ) + } } @@ -41,10 +39,11 @@ library LongsightL uint256 i; + // TODO: convert to assembly for faster running / lower gas + in_x = LongsightL_round(in_x, in_k, 0); - for( i = 0; i < 10; i++ ) - { + for (i = 0; i < 10; i++) { in_x = LongsightL_round(in_x, in_k, C[i]); } @@ -83,7 +82,7 @@ library LongsightL uint256 k_i = in_IV; H_i = 0; - for( i = 0; i < in_M.length; i++ ) { + for (i = 0; i < in_M.length; i++) { k_i = LongsightL12p5(in_M[i], k_i, in_C); H_i = addmod(H_i, in_M[i], SCALAR_FIELD); H_i = addmod(H_i, k_i, SCALAR_FIELD); diff --git a/contracts/MerkleTree.sol b/contracts/MerkleTree.sol index eb70878..e64c480 100644 --- a/contracts/MerkleTree.sol +++ b/contracts/MerkleTree.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity 0.4.24; import "./LongsightL.sol"; @@ -94,15 +94,15 @@ library MerkleTree uint256[29] memory proof_path; - for (uint depth=0 ; depth < TREE_DEPTH; depth++) + for (uint depth=0; depth < TREE_DEPTH; depth++) { address_bits[depth] = index % 2 == 0 ? false : true; if (index%2 == 0) { proof_path[depth] = GetUniqueLeaf(depth, index, self.leaves[depth][index + 1]); - } - else { + } else + { proof_path[depth] = GetUniqueLeaf(depth, index, self.leaves[depth][index - 1]); } @@ -147,8 +147,7 @@ library MerkleTree leaf1 = self.leaves[depth][CurrentIndex]; leaf2 = GetUniqueLeaf(depth, CurrentIndex + 1, self.leaves[depth][CurrentIndex + 1]); - } - else + } else { leaf1 = GetUniqueLeaf(depth, CurrentIndex - 1, self.leaves[depth][CurrentIndex - 1]); diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol index 526d4c3..3684eff 100644 --- a/contracts/Migrations.sol +++ b/contracts/Migrations.sol @@ -1,23 +1,25 @@ -pragma solidity ^0.4.24; +pragma solidity 0.4.24; contract Migrations { - address public owner; - uint256 public last_completed_migration; + address public owner; + uint256 public last_completed_migration; - modifier restricted() { - if(msg.sender == owner) _; - } + modifier restricted() { + if (msg.sender == owner) { + _; + } + } - constructor () public { - owner = msg.sender; - } + constructor () public { + owner = msg.sender; + } - function setCompleted(uint256 completed) public restricted { - last_completed_migration = completed; - } + function setCompleted(uint256 completed) public restricted { + last_completed_migration = completed; + } - function upgrade(address new_address) public restricted { - Migrations upgraded = Migrations(new_address); - upgraded.setCompleted(last_completed_migration); - } + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } } \ No newline at end of file diff --git a/contracts/Miximus.sol b/contracts/Miximus.sol index a7cd497..28425e9 100644 --- a/contracts/Miximus.sol +++ b/contracts/Miximus.sol @@ -17,7 +17,7 @@ along with Miximus. If not, see . */ -pragma solidity ^0.4.24; +pragma solidity 0.4.24; pragma experimental ABIEncoderV2; import "./Verifier.sol"; @@ -29,14 +29,11 @@ contract Miximus { using MerkleTree for MerkleTree.Data; - uint constant AMOUNT = 1 ether; + uint constant public AMOUNT = 1 ether; - mapping (uint256 => bool) nullifiers; + mapping (uint256 => bool) public nullifiers; - MerkleTree.Data tree; - - function GetVerifyingKey () - internal pure returns (Verifier.VerifyingKey memory); + MerkleTree.Data internal tree; function Deposit(uint256 leaf) public payable returns (uint256) @@ -78,4 +75,8 @@ contract Miximus msg.sender.transfer(AMOUNT); } + + function GetVerifyingKey () + internal pure returns (Verifier.VerifyingKey memory); + } diff --git a/contracts/Pairing.sol b/contracts/Pairing.sol index 5b9a408..d40bbe6 100644 --- a/contracts/Pairing.sol +++ b/contracts/Pairing.sol @@ -1,6 +1,6 @@ // This code is taken from https://github.com/JacobEberhardt/ZoKrates -pragma solidity ^0.4.19; +pragma solidity 0.4.24; library Pairing { struct G1Point { diff --git a/contracts/SnarkUtils.sol b/contracts/SnarkUtils.sol index 65605e7..6d850b1 100644 --- a/contracts/SnarkUtils.sol +++ b/contracts/SnarkUtils.sol @@ -1,7 +1,7 @@ // Copyright (c) 2018 HarryR // License: LGPL-3.0+ -pragma solidity ^0.4.24; +pragma solidity 0.4.24; library SnarkUtils { @@ -44,19 +44,19 @@ library SnarkUtils uint256 dest = 0; uint dest_offset = 0; - for( o = 0; o < out_words.length; o++ ) + for (o = 0; o < out_words.length; o++) { - while( dest_offset < 253 ) + while (dest_offset < 253) { uint bits_needed = 253 - dest_offset; uint bits_avail = 256 - source_offset; uint bits_to_copy; - if( bits_needed < bits_avail ) + if (bits_needed < bits_avail) { bits_to_copy = bits_needed; - } - else { + } else + { bits_to_copy = bits_avail; } @@ -66,10 +66,10 @@ library SnarkUtils dest_offset += bits_to_copy; // When all bits in source have been read, go to next source - if( source_offset >= 256 ) + if (source_offset >= 256) { i += 1; - if( i >= in_words.length ) { + if (i >= in_words.length) { break; } source = ReverseBits(in_words[i]); @@ -94,7 +94,7 @@ library SnarkUtils uint256 s = 256; uint256 mask = ~uint(0); - while( (s >>= 1) > 0 ) + while ((s >>= 1) > 0) { mask ^= (mask << s); v = ((v >> s) & mask) | ((v << s) & ~mask); diff --git a/contracts/Verifier.sol b/contracts/Verifier.sol index 9db5193..1ff03d3 100644 --- a/contracts/Verifier.sol +++ b/contracts/Verifier.sol @@ -1,6 +1,6 @@ // this code is taken from https://github.com/JacobEberhardt/ZoKrates -pragma solidity ^0.4.24; +pragma solidity 0.4.24; import "./Pairing.sol"; diff --git a/ethsnarks/eddsa.py b/ethsnarks/eddsa.py new file mode 100644 index 0000000..6a4858a --- /dev/null +++ b/ethsnarks/eddsa.py @@ -0,0 +1,105 @@ +import math +from hashlib import sha256 +from .field import FQ +from .jubjub import Point, JUBJUB_L, JUBJUB_Q, JUBJUB_ORDER + +""" +Implements EdDSA + +The signer has two secret values: + + * k = Secret key + * r = Per-(message,key) nonce + +The signer provides a signature consiting of two pars: + + * R = Point, image of `r*B` + * s = Image of `r + (k*t)` + +The signer provides the verifier with their public key: + + * A = k*R + +Both the verifier and the signer calculate the common reference string: + + * t = H(R, A, m) + +The nonce `r` is secret, and protects the value `s` from revealing the +signers secret key. +""" + + +def encodeint(y): + b = 253 + bits = [(y >> i) & 1 for i in range(b)] + data = [bytes([sum([bits[i * 8 + j] << j for j in range(8)])]) for i in range(b//8)] + return b''.join(data) + + +def make_bytes(arg): + if isinstance(arg, bytes): + return arg + elif isinstance(arg, FQ): + return encodeint(arg.n) + elif isinstance(arg, Point): + return make_bytes(arg.x) + make_bytes(arg.y) + raise TypeError("Cannot convert unknown type to bytes: " + str(type(arg))) + + +def HashToBytes(*args): + return sha256(b''.join([make_bytes(_) for _ in args])).digest() + + +def HashToInt(*args): + """ + Hashes arguments, returns first 250 least significant bits + """ + # Verify that any 250 bits will be less than `L` + assert math.ceil(math.log2(JUBJUB_L)) > 250 + data = HashToBytes(*args) + value = int.from_bytes(data, 'big') + mask = (2<<249) - 1 + return value & mask + + +def eddsa_verify(A, R, s, m, B): + """ + @param A public key + @param R Signature point + @param s Signature scalar + @param m Message being signed + @param B base point + """ + assert isinstance(R, Point) + #assert isinstance(s, FQ) + A = A.as_point() + + assert s < JUBJUB_Q + + mhash = HashToBytes(m) + t = HashToInt(R, A, mhash) + lhs = B * s + rhs = R + (A * t) + return lhs == rhs + + +def eddsa_sign(m, k, B, A=None): + """ + @param m Message being signed + @param k secret key + @param B base point + """ + assert isinstance(k, FQ) + assert k.n < JUBJUB_L + assert k.n > 0 + + if A is None: + A = k * B + + mhash = HashToBytes(m) + khash = HashToBytes(k) + r = HashToInt(khash, mhash) + R = B * r + t = HashToInt(R, A, mhash) + s = ((k.n*t) + r) % JUBJUB_ORDER + return [R, s] diff --git a/ethsnarks/jubjub.py b/ethsnarks/jubjub.py index 5ac0147..70ad68f 100644 --- a/ethsnarks/jubjub.py +++ b/ethsnarks/jubjub.py @@ -34,7 +34,7 @@ From "Twisted Edwards Curves", 2008-BBJLP Theorem 3.2 """ -MONT_A = 168698 # -int(2*(JUBJUB_A+JUBJUB_D)/(JUBJUB_A-JUBJUB_D)) +MONT_A = 168698 # int(2*(JUBJUB_A+JUBJUB_D)/(JUBJUB_A-JUBJUB_D)) MONT_B = 1 # int(4/(JUBJUB_A-JUBJUB_D)) MONT_A24 = int((MONT_A+2)/4) assert MONT_A24*4 == MONT_A+2 @@ -65,9 +65,20 @@ def __mul__(self, n): def double(self): return self.add(self) + def rescale(self): + return self + + def is_negative(self): + p = self.as_point() + q = Point.from_y(p.y) + return p.x == -q.x + + def sign(self): + return 1 if self.is_negative() else 0 + def mult(self, scalar): if isinstance(scalar, FQ): - if scalar.m != SNARK_SCALAR_FIELD: + if scalar.m not in [SNARK_SCALAR_FIELD, JUBJUB_ORDER, JUBJUB_L]: raise ValueError("Invalid field modulus") scalar = scalar.n p = self @@ -132,6 +143,13 @@ def from_hash(cls, entropy): def as_edwards_yz(self): return EdwardsYZPoint(self.y, FQ(1)) + def as_mont_xz(self): + """ + IACR 2008/218 + 2.Switching to Edwards curves and back + """ + return MontXZPoint(FQ(1) + self.y, FQ(1) - self.y) + def as_proj(self): return ProjPoint(self.x, self.y, FQ(1)) @@ -184,19 +202,37 @@ def as_point(self): """ return Point.from_y(self.y / self.z) - def as_mont_xy(self): + def as_edwards_yz(self): + return self + + def rescale(self): + return EdwardsYZPoint(self.y / self.z, FQ(1)) + + def as_proj(self): + return self.as_point().as_proj() + + def as_mont_xz(self): """ IACR 2008/218 2.Switching to Edwards curves and back """ return MontXZPoint(self.z + self.y, self.z - self.y) + def as_mont(self): + return self.as_mont_xz().as_mont() + + def as_etec(self): + return self.as_point().as_etec() + def double(self): - return self.as_mont_xy().double() + return self.as_mont_xz().double() def infinity(self): return EdwardsYZPoint(FQ(1), FQ(1)) + def valid(self): + return self.as_point().valid() + class MontXZPoint(AbstractCurveOps, namedtuple('_MontXZPoint', ('x', 'z'))): def as_edwards_yz(self): @@ -210,7 +246,26 @@ def as_mont(self): """ Rescale the point, then recover the Y coordinate """ - return MontPoint.from_x(self.x / self.z) + # XXX: needs conversion via Edwards YZ, then to full point, then back to Montgomery form!! + return self.as_edwards_yz().as_point().as_mont() + + def as_mont_xz(self): + return self + + def as_etec(self): + return self.as_point().as_etec() + + def as_proj(self): + return self.as_point().as_etec().as_proj() + + def as_point(self): + return self.as_edwards_yz().as_point() + + def valid(self): + return self.as_point().valid() + + def rescale(self): + return MontXZPoint(self.x / self.z, FQ(1)) def double(self): """ @@ -272,6 +327,12 @@ def from_x(cls, x): y2 = x3 + MONT_A*x2 + x return cls(x, y2.sqrt()) + def as_mont(self): + return self + + def as_mont_xz(self): + return MontXZPoint(self.x, FQ(1)) + def as_point(self): """ IACR 2008/218 @@ -309,21 +370,25 @@ def double(self): class ProjPoint(AbstractCurveOps, namedtuple('_ProjPoint', ('x', 'y', 'z'))): - def valid(self): - """ - From Twisted Curves Revisited: - - (a * x^2 + y^2) * z^2 = z^4 + (d * x^2 * y^2) - """ - xx = self.x * self.x - yy = self.y * self.y - zz = self.z * self.z - z4 = zz * zz - return (JUBJUB_A * xx + yy) * zz == z4 * (JUBJUB_D * xx * yy) + def rescale(self): + return ProjPoint(self.x / self.z, self.y / self.z, FQ(1)) def as_proj(self): return self + def as_mont(self): + return self.as_point().as_mont() + + def as_mont_xz(self): + """ + IACR 2008/218 + 2.Switching to Edwards curves and back + """ + return MontXZPoint(self.z + self.y, self.z - self.y) + + def as_edwards_yz(self): + return EdwardsYZPoint(self.y, self.z) + def as_etec(self): """ (X, Y, Z) -> (X, Y, X*Y, Z) @@ -413,6 +478,16 @@ class EtecPoint(AbstractCurveOps, namedtuple('_EtecPoint', ('x', 'y', 't', 'z')) def as_etec(self): return self + def as_mont_xz(self): + """ + IACR 2008/218 + 2.Switching to Edwards curves and back + """ + return MontXZPoint(self.z + self.y, self.z - self.y) + + def as_edwards_yz(self): + return EdwardsYZPoint(self.y, self.z) + def as_point(self): """ Ignoring the T value, project from 3d X,Y,Z to 2d X,Y coordinates @@ -431,6 +506,11 @@ def as_proj(self): """ return ProjPoint(self.x, self.y, self.z) + def as_mont(self): + u = (1 + self.y) / (1 - self.y) + v = (1 + self.y) / ( (1 - self.y) * self.x ) + return MontPoint(u, v) + @staticmethod def infinity(): return EtecPoint(FQ(0), FQ(1), FQ(0), FQ(1)) diff --git a/ethsnarks/longsight.py b/ethsnarks/longsight.py index c021edb..e4e3eb4 100644 --- a/ethsnarks/longsight.py +++ b/ethsnarks/longsight.py @@ -24,7 +24,6 @@ from __future__ import print_function import math -import struct from binascii import unhexlify from random import randint @@ -182,4 +181,4 @@ def LongsightL12p5_MP(M, IV): if __name__ == "__main__": - print(make_constants_cxx_L("LongsightL", 12, 5)) + print(make_constants_cxx("LongsightL", 12)) diff --git a/ethsnarks/numbertheory.py b/ethsnarks/numbertheory.py index 983cd24..2b8884d 100644 --- a/ethsnarks/numbertheory.py +++ b/ethsnarks/numbertheory.py @@ -13,7 +13,6 @@ import sys import math -import types from functools import reduce if sys.version_info.major == 2: @@ -503,17 +502,17 @@ def __main__(): # p = square_root_mod_prime( 2, 3 ) - print_("Testing gcd...") + print("Testing gcd...") assert gcd( 3*5*7, 3*5*11, 3*5*13 ) == 3*5 assert gcd( [ 3*5*7, 3*5*11, 3*5*13 ] ) == 3*5 assert gcd( 3 ) == 3 - print_("Testing lcm...") + print("Testing lcm...") assert lcm( 3, 5*3, 7*3 ) == 3*5*7 assert lcm( [ 3, 5*3, 7*3 ] ) == 3*5*7 assert lcm( 3 ) == 3 - print_("Testing next_prime...") + print("Testing next_prime...") bigprimes = ( 999671, 999683, 999721, @@ -543,7 +542,7 @@ def __main__(): # Test the square_root_mod_prime function: for p in smallprimes: - print_("Testing square_root_mod_prime for modulus p = %d." % p) + print("Testing square_root_mod_prime for modulus p = %d." % p) squares = [] for root in range( 0, 1+p//2 ): @@ -552,7 +551,7 @@ def __main__(): calculated = square_root_mod_prime( sq, p ) if ( calculated * calculated ) % p != sq: error_tally = error_tally + 1 - print_("Failed to find %d as sqrt( %d ) mod %d. Said %d." % \ + print("Failed to find %d as sqrt( %d ) mod %d. Said %d." % \ ( root, sq, p, calculated )) for nonsquare in range( 0, p ): @@ -563,24 +562,24 @@ def __main__(): pass else: error_tally = error_tally + 1 - print_("Failed to report no root for sqrt( %d ) mod %d." % \ + print("Failed to report no root for sqrt( %d ) mod %d." % \ ( nonsquare, p )) # Test the jacobi function: for m in range( 3, 400, 2 ): - print_("Testing jacobi for modulus m = %d." % m) + print("Testing jacobi for modulus m = %d." % m) if is_prime( m ): squares = [] for root in range( 1, m ): if jacobi( root * root, m ) != 1: error_tally = error_tally + 1 - print_("jacobi( %d * %d, %d ) != 1" % ( root, root, m )) + print("jacobi( %d * %d, %d ) != 1" % ( root, root, m )) squares.append( root * root % m ) for i in range( 1, m ): if not i in squares: if jacobi( i, m ) != -1: error_tally = error_tally + 1 - print_("jacobi( %d, %d ) != -1" % ( i, m )) + print("jacobi( %d, %d ) != -1" % ( i, m )) else: # m is not prime. f = factorization( m ) for a in range( 1, m ): @@ -589,11 +588,11 @@ def __main__(): c = c * jacobi( a, i[0] ) ** i[1] if c != jacobi( a, m ): error_tally = error_tally + 1 - print_("%d != jacobi( %d, %d )" % ( c, a, m )) + print("%d != jacobi( %d, %d )" % ( c, a, m )) # Test the inverse_mod function: - print_("Testing inverse_mod . . .") + print("Testing inverse_mod . . .") import random n_tests = 0 for i in range( 100 ): @@ -605,12 +604,12 @@ def __main__(): inv = inverse_mod( a, m ) if inv <= 0 or inv >= m or ( a * inv ) % m != 1: error_tally = error_tally + 1 - print_("%d = inverse_mod( %d, %d ) is wrong." % ( inv, a, m )) + print("%d = inverse_mod( %d, %d ) is wrong." % ( inv, a, m )) assert n_tests > 1000 - print_(n_tests, " tests of inverse_mod completed.") + print(n_tests, " tests of inverse_mod completed.") class FailedTest(Exception): pass - print_(error_tally, "errors detected.") + print(error_tally, "errors detected.") if error_tally != 0: raise FailedTest("%d errors detected" % error_tally) diff --git a/ethsnarks/pedersen.py b/ethsnarks/pedersen.py new file mode 100644 index 0000000..3955f18 --- /dev/null +++ b/ethsnarks/pedersen.py @@ -0,0 +1,108 @@ +""" +This module implements a Pedersen hash function. +It can hash points, scalar values or blocks of message data. + +It is possible to create two variants, however only the +non-homomorphic variant has been implemented. + +The homomorphic variant users the same base point for every +input, whereas the non-homomorphic version uses a different +base point for every input. + +For example to non-homomorphically hash the two points P1 and P2 + + Four base points are chosen (consistently) + + B0, B1, B2, B3 + + The result of the hash is the point: + + B0*P1.x + B1*P1.y + B2*P2.x + B3*P2.y + +To homomorphically hash the two points: + + Two base points are chosen + + BX, BY + + The result of the hash is the point: + + BX*P1.x + BY.P1.y + BX.P2.x + BY.P2.y + + The hash will be the same if either point is swapped + with the other, e.g. H(P1,P2) is the same as H(P2,P1). +""" + +import math +from struct import pack + +from .jubjub import Point, JUBJUB_L + + +MAX_SEGMENT_BITS = math.floor(math.log2(JUBJUB_L)) +MAX_SEGMENT_BYTES = MAX_SEGMENT_BITS // 8 + + +def pedersen_hash_basepoint(name, i): + """ + Create a base point for use with the windowed pedersen + hash function. + The name and sequence numbers are used a unique identifier. + Then HashToPoint is run on the name+seq to get the base point + + XXX: Ethereum compatible + """ + seq = pack('>L', i) + max_name_len = MAX_SEGMENT_BYTES - len(seq) + if not isinstance(name, bytes): + raise TypeError("Name not bytes") + if len(name) > max_name_len: + raise ValueError("Name too long") + padding_needed = (max_name_len - len(name)) % max_name_len + data = bytes(padding_needed) + seq + return Point.from_hash(data) + + +def pedersen_hash_points(name, *points): + # XXX: should the coordinate be truncated? + result = Point.infinity() + for i, p in enumerate(points): + p = p.as_point() + for j, c in enumerate([p.x, p.y]): + base = pedersen_hash_basepoint(name, i*2 + j) + result += base * c + return result + + +def pedersen_hash_scalars(name, *scalars): + result = Point.infinity() + for i, s in enumerate(scalars): + if s >= JUBJUB_L: + raise ValueError("Scalar must be below L") + if s <= 0: + raise ValueError("Scalar must be above zero") + base = pedersen_hash_basepoint(name, i) + result += base * s + return result + + +def pedersen_hash_bytes(name, *args): + """ + Split the message data into segments, then hash each segment + + Data is split into bytes for convenience, rather + than bits. e.g. if snark scalar field is 253 bits + then only 248 will be used (31 bytes), the reason is: + + 1) Conversion is easier + 2) All values are below L (location of the curve twist) + """ + data = b''.join(args) + segments_list = [data[i:i+MAX_SEGMENT_BYTES] + for i in range(0, len(data), MAX_SEGMENT_BYTES)] + result = Point.infinity() + for i, segment in enumerate(segments_list): + base = pedersen_hash_basepoint(name, i) + scalar = int.from_bytes(segment, 'big') + result += base * scalar + return result diff --git a/test/test_eddsa.py b/test/test_eddsa.py new file mode 100644 index 0000000..84aceba --- /dev/null +++ b/test/test_eddsa.py @@ -0,0 +1,21 @@ +import unittest +from os import urandom + + +from ethsnarks.jubjub import JUBJUB_L, Point, FQ +from ethsnarks.eddsa import eddsa_sign, eddsa_verify + + +class TestEdDSA(unittest.TestCase): + def test_signverify(self): + B = Point.from_hash(b'eddsa_base') + k = FQ.random(JUBJUB_L) + A = B * k + m = urandom(32) + R, s = eddsa_sign(m, k, B, A) + + self.assertTrue(eddsa_verify(A, R, s, m, B)) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_jubjub.py b/test/test_jubjub.py index 6cd02d8..e5eb464 100644 --- a/test/test_jubjub.py +++ b/test/test_jubjub.py @@ -3,7 +3,7 @@ from os import urandom from ethsnarks.field import FQ -from ethsnarks.jubjub import Point, MontPoint, EtecPoint, ProjPoint, JUBJUB_L, JUBJUB_C, MONT_A, MONT_B +from ethsnarks.jubjub import Point, MontPoint, EtecPoint, ProjPoint, JUBJUB_L, JUBJUB_C, MONT_A, MONT_B, JUBJUB_ORDER from ethsnarks.numbertheory import SquareRootError @@ -30,7 +30,20 @@ def test_2_mont_form(self): self.assertTrue(r.valid()) self.assertTrue(q.valid()) + def _verify_via_all(self, p): + points = [p.as_point(), p.as_mont(), p.as_etec(), p.as_proj(), p.as_mont_xz(), p.as_edwards_yz()] + for q in points: + self.assertTrue(q.valid()) + qoints = [q.as_point(), q.as_mont(), q.as_etec(), q.as_proj(), q.as_mont_xz(), q.as_edwards_yz()] + for i, r in enumerate(qoints): + self.assertTrue(r.valid()) + self.assertEqual(r.rescale(), points[i].rescale()) + def test_3_validity(self): + """ + Verify that 10 random points can be converted to and from every other + coordinate system without losing information or corruption. + """ self.assertTrue(self._point_a().valid()) self.assertTrue(Point.infinity().valid()) self.assertTrue(EtecPoint.infinity().valid()) @@ -38,9 +51,8 @@ def test_3_validity(self): for _ in range(0, 10): p = self._point_r() - for q in [p.as_point(), p.as_mont(), p.as_etec(), p.as_proj()]: - self.assertTrue(q.valid()) - self.assertTrue(q.neg().valid()) + self._verify_via_all(p) + def test_5_recover_x(self): """ @@ -67,7 +79,13 @@ def test_7_negate(self): """ p = self._point_a() for q in [p.as_point(), p.as_etec(), p.as_proj()]: + self.assertFalse(q.is_negative()) + r = q.neg() + self.assertTrue(r.valid()) + self.assertTrue(r.is_negative()) + self.assertEqual(q.infinity().neg(), q.infinity()) + # (x,y) + (-(x,y)) = infinity r = q.add( q.neg() ) self.assertEqual(r.as_point(), p.infinity()) @@ -163,8 +181,8 @@ def test_12_nonsquares(self): of order 4 at infinity of the desingularization of E_{E,a,d} """ try: - x = (MONT_A-2) / MONT_B - FQ(int(x)).sqrt() + x = int((MONT_A-2) / MONT_B) + FQ(x).sqrt() self.assertTrue(False) except SquareRootError: pass @@ -190,6 +208,85 @@ def test_15_random_mont(self): self.assertEqual(s.x, r.x) self.assertTrue(s.y, [r.x, -r.y]) + def test_negate_order(self): + p = self._point_r() + self.assertEqual(p * (JUBJUB_ORDER+1), p) + self.assertEqual(p * (JUBJUB_ORDER-1), p.neg()) + self.assertEqual(p - p - p, p.neg()) + + def test_multiplicative(self): + G = self._point_r() + a = FQ.random() + A = G*a + b = FQ.random() + B = G*b + + ab = (a.n * b.n) % JUBJUB_ORDER + AB = G*ab + self.assertEqual(A*b, AB) + self.assertEqual(B*a, AB) + + def test_cyclic(self): + G = self._point_r() + self.assertEqual(G * (JUBJUB_ORDER+1), G) + + def test_loworder_points_mont(self): + """ + From "Twisted Edwards Curves" - BBJLP + - Exceptional Points for the Birational Equivalence + + The birational equivalence is undefined where `y = 0` or `x + 1 = 0` + """ + # Both Montgomery and twisted Edwards forms are valid + # EM(0,0) -> EE(0,-1) + p1m = MontPoint(FQ(0), FQ(0)) + p1e = p1m.as_point() + self.assertEqual(p1e.x, FQ(0)) + self.assertEqual(p1e.y, FQ(-1)) + self.assertTrue(p1m.valid()) + self.assertTrue(p1e.valid()) + self.assertEqual(p1e.as_mont(), p1m) + + # Both Montgomery and twisted Edwards forms are valid + # Theorem 3.4 case 1 + # EM(1, sqrt(A+2)) -> EE(?, 0) + p2m = MontPoint(FQ(1), FQ(MONT_A+2).sqrt()) + self.assertTrue(p2m.valid()) + self.assertTrue(p2m.as_point().valid()) + self.assertEqual(p2m.as_point().as_mont(), p2m) + + # Montgomery form is invalid + # EM(0,-1) -> EE(0,-1) + p2m = MontPoint(FQ(0), FQ(-1)) + p2e = p2m.as_point() + self.assertEqual(p2e.x, FQ(0)) + self.assertEqual(p2e.y, FQ(-1)) + self.assertFalse(p2m.valid()) + self.assertTrue(p2e.valid()) + self.assertNotEqual(p2e.as_mont(), p2m) + + # Montgomery form is invalid + # EM(0,-1) -> EE(0,-1) + p3m = MontPoint(FQ(0), FQ(1)) + p3e = p3m.as_point() + self.assertEqual(p3e.x, FQ(0)) + self.assertEqual(p3e.y, FQ(-1)) + self.assertFalse(p3m.valid()) + self.assertTrue(p3e.valid()) + self.assertNotEqual(p3e.as_mont(), p3m) + + # Montgomery form is invalid + # EM(-1,?) -> EE(?,0) + p4my = FQ.random() + p4m = MontPoint(FQ(-1), p4my) + p4e = p4m.as_point() + p4mb = p4e.as_mont() + self.assertNotEqual(p4mb, p4m) + self.assertNotEqual(p4e.x, p4my) + self.assertEqual(p4e.y, FQ(0)) + self.assertFalse(p4m.valid()) + self.assertTrue(p3e.valid()) + def test_mont_double(self): """ Verified in Sage, using `ejubjub.py` diff --git a/test/test_jubjub_lmask.py b/test/test_jubjub_lmask.py new file mode 100644 index 0000000..eaee793 --- /dev/null +++ b/test/test_jubjub_lmask.py @@ -0,0 +1,37 @@ +import unittest + +from collections import defaultdict + +from ethsnarks.jubjub import FQ, JUBJUB_L + + +class TestJubJubLMask(unittest.TestCase): + def test_jubjub_l_size(self): + """ + Any 250 bits will be below JUBJUB_L + This ensures that any 250 bit value cannot + be used to exceed the twist point of the curve + """ + self.assertTrue(int('1' * 250, 2) < JUBJUB_L) + + def test_mask_bits(self): + histogram = defaultdict(int) + n_samples = 256*2 + for _ in range(0, n_samples): + randfq = FQ.random() + randfq_mod_l = randfq.n % JUBJUB_L + as_binary = bin(randfq_mod_l)[2:][::-1] + for i, bit in enumerate(as_binary): + histogram[i] += 1 + + """ + # Display histogram of bit probablities + for i in range(0, 256): + print(i, histogram[i], histogram[i] / n_samples) + """ + + for i in range(251, 256): + self.assertEqual(histogram[i], 0) + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_pedersen.py b/test/test_pedersen.py new file mode 100644 index 0000000..d2e961b --- /dev/null +++ b/test/test_pedersen.py @@ -0,0 +1,29 @@ +import unittest +from os import urandom +from random import randint + +from ethsnarks.jubjub import Point +from ethsnarks.pedersen import pedersen_hash_points, pedersen_hash_scalars, pedersen_hash_bytes + + +class TestPedersenHash(unittest.TestCase): + def test_bytes(self): + d = urandom(randint(1, 256)) + p = pedersen_hash_bytes(b'test', d) + q = pedersen_hash_bytes(b'test', d, d) + self.assertTrue(p.valid()) + self.assertTrue(q.valid()) + self.assertNotEqual(p, q) + + def test_points(self): + d = urandom(10) + p = Point.from_hash(d) + q = pedersen_hash_points(b'test', p) + r = pedersen_hash_points(b'test', q) + self.assertTrue(q.valid()) + self.assertTrue(r.valid()) + self.assertNotEqual(q, r) + + +if __name__ == "__main__": + unittest.main()