Compare commits
4 Commits
c66175a577
...
92a8ce4b44
| Author | SHA1 | Date | |
|---|---|---|---|
| 92a8ce4b44 | |||
| 98031ae4c5 | |||
| ee2eb2c805 | |||
| 3c5963eab9 |
138
README.md
138
README.md
@ -38,42 +38,150 @@ For full background and protocol details see the [project wiki](https://git.ghos
|
||||
+==================================================================================================+
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| 2624055 | 11923 | | | | |
|
||||
| 3247879 | 14807 | | | | |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| | | | | | |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| Function Name | Min | Avg | Median | Max | # Calls |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| addJacobian | 1896 | 1896 | 1896 | 1896 | 88 |
|
||||
| addGhost | 1450 | 1450 | 1450 | 1450 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| addProjective | 1110 | 1110 | 1110 | 1110 | 44 |
|
||||
| addJacobian | 1976 | 1976 | 1976 | 1976 | 88 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| addProjectiveMixed | 1084 | 1084 | 1084 | 1084 | 44 |
|
||||
| addMixedGhost | 1084 | 1084 | 1084 | 1084 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| doubleJacobian | 889 | 889 | 889 | 889 | 45 |
|
||||
| addMixedProjective | 1098 | 1098 | 1098 | 1098 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| doubleProjective | 641 | 641 | 641 | 641 | 45 |
|
||||
| addProjective | 1252 | 1252 | 1252 | 1252 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| isOnCurve | 280 | 280 | 280 | 280 | 262 |
|
||||
| doubleGhost | 732 | 732 | 732 | 732 | 45 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulEcPair | 103258 | 731053 | 1058263 | 1269945 | 44 |
|
||||
| doubleJacobian | 1101 | 1101 | 1101 | 1101 | 45 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulEcQuartet | 308151 | 1561360 | 1982690 | 2672830 | 42 |
|
||||
| doubleProjective | 710 | 710 | 710 | 710 | 45 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulEcTriplet | 205666 | 1140527 | 1546387 | 1958933 | 43 |
|
||||
| isOnCurve | 335 | 335 | 335 | 335 | 306 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulProjectivePair | 4265 | 178529 | 311013 | 338160 | 44 |
|
||||
| isOnCurveGhost | 863 | 863 | 863 | 863 | 306 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulProjectiveQuartet | 12001 | 206222 | 345559 | 396103 | 42 |
|
||||
| mulPairEc | 103705 | 731500 | 1058710 | 1270392 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulProjectiveTriplet | 6622 | 199854 | 346604 | 397492 | 43 |
|
||||
| mulPairGhost | 4241 | 178420 | 310213 | 337584 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| toAffineJacobian | 40300 | 47765 | 47884 | 53888 | 133 |
|
||||
| mulPairProjective | 5275 | 182401 | 327577 | 361377 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| toAffineProjective | 11129 | 13876 | 13864 | 16178 | 262 |
|
||||
| mulQuartetEc | 308036 | 1561245 | 1982575 | 2672715 | 42 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulQuartetProjective | 12468 | 206689 | 346026 | 396570 | 42 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulSingleEc | 2093 | 327159 | 408874 | 600125 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulSingleGhost | 1803 | 113386 | 148753 | 219187 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulSingleProjective | 1916 | 114289 | 149838 | 220860 | 44 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulTripletEc | 205416 | 1140277 | 1546137 | 1958683 | 43 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| mulTripletProjective | 6636 | 199868 | 346618 | 397506 | 43 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| toAffineGhost | 11853 | 13845 | 13925 | 15527 | 176 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| toAffineJacobian | 40395 | 47860 | 47979 | 53983 | 133 |
|
||||
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||
| toAffineProjective | 11165 | 13918 | 13900 | 16214 | 351 |
|
||||
╰----------------------------------------+-----------------+---------+---------+---------+---------╯
|
||||
|
||||
╭-----------------------------------------------------+-----------------+--------+--------+--------+---------╮
|
||||
| test/GhostVerifier.t.sol:GhostVerifierImpl Contract | | | | | |
|
||||
+============================================================================================================+
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
|-----------------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| 1871747 | 8463 | | | | |
|
||||
|-----------------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| | | | | | |
|
||||
|-----------------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| Function Name | Min | Avg | Median | Max | # Calls |
|
||||
|-----------------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| setPubkey | 111552 | 111565 | 111564 | 111576 | 9 |
|
||||
|-----------------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| verify | 611046 | 680989 | 648023 | 955747 | 9 |
|
||||
╰-----------------------------------------------------+-----------------+--------+--------+--------+---------╯
|
||||
```
|
||||
|
||||
The `verify` function consumes a maximum of 955,747 gas units when processing 1,000 signers with 300 missing signatures. Assuming an ETH price of $4,200 and gas cost of 2 gwei:
|
||||
|
||||
* test_verify_5_of_5_works() (gas: 735686 = $6.18) - Baseline (100%)
|
||||
* test_verify_67_of_100() (gas: 812318 = $6.82) - 10% increase
|
||||
* test_verify_700_of_1000() (gas: 1118143 = $9.39) - 51% increase
|
||||
|
||||
The gas cost scales reasonably across different numbers of signers, making the solution practical for real-world use cases.
|
||||
|
||||
### Compiler Inconsistency Note
|
||||
|
||||
We observed unexpected behavior in the Solidity compiler. Despite identical code in `ECMathProjective` and `GhostEllipticCurves` libraries, gas costs vary for critical elliptic curve operations:
|
||||
|
||||
* `doubleProjective` and `addProjective` show inconsistent gas costs between implementations
|
||||
* `mulSingleGhost` unexpectedly consumes less gas than `mulSingleProjective`
|
||||
|
||||
These variations occur even with clean compilation runs, suggesting compiler-level optimizations that behave unpredictably with identical code in different library contexts. The root cause remains unclear.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
Final implementation formula is:
|
||||
|
||||
```
|
||||
s*G + sum_i^n (e*ai*(Pi) + b*R1m + d*R2m) = R + e*Hagg
|
||||
s*G + sum_i^n (e*ai*(Pi)) + sum_i^n (b*R1m) + sum_i^n (b*R2m) = b*R1 + d*R2 + e*Hagg
|
||||
s*G + e*(Hagg - sum_i^n Hi) = b*(R1 - sum_i^n R1m) + d*(R2 - sum_i^n R2m)
|
||||
s*G + e*(Hagg - sum_i^n Hi) = b*R1e + d*R2e
|
||||
|
||||
where
|
||||
R1 = R1e + R1m (existing nonce + missing nonce)
|
||||
R2 = R2e + R2m
|
||||
Hi = ai*Pi
|
||||
```
|
||||
|
||||
### Nonce Abuse
|
||||
|
||||
Our approach enables any participant to submit pairs of (`R1e`, `R1m`) and (`R2e`, `R2m`), which we use to reconstruct the original nonces `R1` and `R2`. These reconstructed values are essential for computing `b` and `d` during the verification process, ultimately deriving the final `R` required for commitment `e`.
|
||||
|
||||
This design leverages circular dependencies and cryptographic hash functions to ensure verification integrity.
|
||||
|
||||
```
|
||||
arbitrary data: s, msg, R1e, R1m, R2e, R2m
|
||||
s*G + e*H` = b*R1e + d*R2e
|
||||
s*G = b*R1e + d*R2e - e*H`
|
||||
s*G = target_value (Discrete Log Problem)
|
||||
```
|
||||
|
||||
However, the hash dependencies create a circular constraint similar to the "chicken and egg" problem:
|
||||
|
||||
```
|
||||
b = sha256(R1e + R1m, R2e + R2m, H, msg)
|
||||
d = sha256(msg, H, R2e + R2m, R1e + R1m)
|
||||
e = sha256(b*(R1e+R1m) + d*(R2e+R2m), H, msg)
|
||||
|
||||
b*R1e + d*R2e - s*G = e*H`
|
||||
```
|
||||
|
||||
For now it looks safe if `sha256` or `keccak256` hashing algorithm is used.
|
||||
|
||||
### Missing Aggregation Abuse
|
||||
|
||||
We are proposing the to store not only final `Hagg` point but the hash of all signers (both coordinates for each) in the sequence that they were aggregated. Thus each `verify` call will have all list of `Xi=ai*Pi` with indexes of missing signers. Here we are relying on fact that `sha256` and `keccak256` do not have any known ability to find collisions and resistant to preimage attack.
|
||||
|
||||
We propose storing not only the final aggregated point `Hagg` but also the cryptographic hash of all signers' coordinates in their aggregation sequence. This approach ensures each verify call has access to the complete list of `Xi = ai * Pi` along with indexes of missing signers.
|
||||
|
||||
This design relies on the cryptographic security of `sha256` and `keccak256`, both of which:
|
||||
|
||||
* Have no known practical collision attacks
|
||||
* Maintain strong resistance to preimage attacks
|
||||
* Provide robust security guarantees for our verification process
|
||||
|
||||
### Precompile Abuse
|
||||
|
||||
The step by step hack is described [here](https://git.ghostchain.io/ghostchain/ghost-exodus-draft/wiki/Description#abuse-of-ecrecover-precompile).
|
||||
|
||||
## Contributing
|
||||
|
||||
All contributions are welcome — whether it's code, documentation, tests, performance benchmarks, or review. Please submit commits, issues, or pull requests; any help to improve correctness, security, or gas efficiency is greatly appreciated.
|
||||
|
||||
@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
|
||||
|
||||
import {EllipticCurve} from "./libraries/ECMath.sol";
|
||||
import {EllipticCurveProjective} from "./libraries/ECMathProjective.sol";
|
||||
import {GhostEllipticCurves} from "./libraries/GhostEllipticCurves.sol";
|
||||
|
||||
contract MathTester {
|
||||
// Constants are taken from https://en.bitcoin.it/wiki/Secp256k1
|
||||
@ -29,6 +30,12 @@ contract MathTester {
|
||||
return EllipticCurveProjective.projectiveAdd(x1, y1, 1, x2, y2, 1);
|
||||
}
|
||||
|
||||
function addGhost(
|
||||
uint256 x1, uint256 y1, uint256 x2, uint256 y2
|
||||
) public pure returns (uint256, uint256, uint256) {
|
||||
return GhostEllipticCurves.projectiveAdd(x1, y1, 1, x2, y2, 1);
|
||||
}
|
||||
|
||||
function doubleJacobian(uint256 x1, uint256 y1) public pure returns (uint256, uint256, uint256) {
|
||||
return EllipticCurve.jacDouble(x1, y1, 1, A, P);
|
||||
}
|
||||
@ -37,7 +44,11 @@ contract MathTester {
|
||||
return EllipticCurveProjective.projectiveDouble(x1, y1, 1);
|
||||
}
|
||||
|
||||
function addProjectiveMixed(
|
||||
function doubleGhost(uint256 x1, uint256 y1) public pure returns (uint256, uint256, uint256) {
|
||||
return GhostEllipticCurves.projectiveDouble(x1, y1, 1);
|
||||
}
|
||||
|
||||
function addMixedProjective(
|
||||
uint256 x1,
|
||||
uint256 y1,
|
||||
uint256 x2,
|
||||
@ -46,16 +57,16 @@ contract MathTester {
|
||||
return EllipticCurveProjective.projectiveAddMixed(x1, y1, 1, x2, y2);
|
||||
}
|
||||
|
||||
function mulEcTriplet(
|
||||
uint256 x1,
|
||||
uint256 y1,
|
||||
uint256 k1,
|
||||
uint256 x2,
|
||||
uint256 y2,
|
||||
uint256 k2,
|
||||
uint256 x3,
|
||||
uint256 y3,
|
||||
uint256 k3
|
||||
function addMixedGhost(
|
||||
uint256 x1, uint256 y1, uint256 x2, uint256 y2
|
||||
) public pure returns (uint256, uint256, uint256) {
|
||||
return GhostEllipticCurves.projectiveAddMixed(x1, y1, 1, x2, y2);
|
||||
}
|
||||
|
||||
function mulTripletEc(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2,
|
||||
uint256 x3, uint256 y3, uint256 k3
|
||||
) public pure returns(uint256, uint256) {
|
||||
(x1, y1) = EllipticCurve.ecMul(k1, x1, y1, A, P);
|
||||
(x2, y2) = EllipticCurve.ecMul(k2, x2, y2, A, P);
|
||||
@ -67,27 +78,17 @@ contract MathTester {
|
||||
return (x1, y1);
|
||||
}
|
||||
|
||||
function mulProjectiveTriplet(
|
||||
uint256 x1,
|
||||
uint256 y1,
|
||||
uint256 k1,
|
||||
uint256 x2,
|
||||
uint256 y2,
|
||||
uint256 k2,
|
||||
uint256 x3,
|
||||
uint256 y3,
|
||||
uint256 k3
|
||||
function mulTripletProjective(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2,
|
||||
uint256 x3, uint256 y3, uint256 k3
|
||||
) public pure returns(uint256, uint256, uint256) {
|
||||
return EllipticCurveProjective.mulAddProjectiveTriplet(x1, y1, k1, x2, y2, k2, x3, y3, k3);
|
||||
}
|
||||
|
||||
function mulEcPair(
|
||||
uint256 x1,
|
||||
uint256 y1,
|
||||
uint256 k1,
|
||||
uint256 x2,
|
||||
uint256 y2,
|
||||
uint256 k2
|
||||
function mulPairEc(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2
|
||||
) public pure returns(uint256, uint256) {
|
||||
(x1, y1) = EllipticCurve.ecMul(k1, x1, y1, A, P);
|
||||
(x2, y2) = EllipticCurve.ecMul(k2, x2, y2, A, P);
|
||||
@ -95,18 +96,39 @@ contract MathTester {
|
||||
return (x1, y1);
|
||||
}
|
||||
|
||||
function mulProjectivePair(
|
||||
uint256 x1,
|
||||
uint256 y1,
|
||||
uint256 k1,
|
||||
uint256 x2,
|
||||
uint256 y2,
|
||||
uint256 k2
|
||||
function mulPairProjective(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2
|
||||
) public pure returns(uint256, uint256, uint256) {
|
||||
return EllipticCurveProjective.mulAddProjectivePair(x1, y1, k1, x2, y2, k2);
|
||||
return EllipticCurveProjective.mulAddProjectivePair(x1, y1, 1, k1, x2, y2, 1, k2);
|
||||
}
|
||||
|
||||
function mulEcQuartet(
|
||||
function mulPairGhost(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2
|
||||
) public pure returns(uint256, uint256, uint256) {
|
||||
return GhostEllipticCurves.mulAddAffinePair(x1, y1, k1, x2, y2, k2);
|
||||
}
|
||||
|
||||
function mulSingleEc(
|
||||
uint256 x1, uint256 y1, uint256 k1
|
||||
) public pure returns (uint256, uint256) {
|
||||
return EllipticCurve.ecMul(k1, x1, y1, A, P);
|
||||
}
|
||||
|
||||
function mulSingleProjective(
|
||||
uint256 x1, uint256 y1, uint256 k1
|
||||
) public pure returns (uint256, uint256, uint256) {
|
||||
return EllipticCurveProjective.mulAddProjectiveSingle(x1, y1, k1);
|
||||
}
|
||||
|
||||
function mulSingleGhost(
|
||||
uint256 x1, uint256 y1, uint256 k1
|
||||
) public pure returns (uint256, uint256, uint256) {
|
||||
return GhostEllipticCurves.mulAddAffineSingle(x1, y1, k1);
|
||||
}
|
||||
|
||||
function mulQuartetEc(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2,
|
||||
uint256 x3, uint256 y3, uint256 k3,
|
||||
@ -124,7 +146,7 @@ contract MathTester {
|
||||
return (x1, y1);
|
||||
}
|
||||
|
||||
function mulProjectiveQuartet(
|
||||
function mulQuartetProjective(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2,
|
||||
uint256 x3, uint256 y3, uint256 k3,
|
||||
@ -146,7 +168,15 @@ contract MathTester {
|
||||
return EllipticCurveProjective.toAffine(x, y, z);
|
||||
}
|
||||
|
||||
function toAffineGhost(uint256 x, uint256 y, uint256 z) public pure returns (uint256, uint256) {
|
||||
return GhostEllipticCurves.toAffine(x, y, z);
|
||||
}
|
||||
|
||||
function isOnCurve(uint256 x, uint256 y) public pure returns (bool) {
|
||||
return EllipticCurveProjective.isOnCurve(x, y);
|
||||
}
|
||||
|
||||
function isOnCurveGhost(uint256 x, uint256 y) public pure returns (bool) {
|
||||
return GhostEllipticCurves.isOnCurve(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
304
src/Verifier.sol
Normal file
304
src/Verifier.sol
Normal file
@ -0,0 +1,304 @@
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {GhostEllipticCurves} from "./libraries/GhostEllipticCurves.sol";
|
||||
|
||||
abstract contract GhostVerifier {
|
||||
uint256 public pubkeyX;
|
||||
uint256 public pubkeyY;
|
||||
uint256 public maxLost;
|
||||
bytes32 public signersHash;
|
||||
|
||||
function _setPubkey(
|
||||
uint256 _pubkeyX,
|
||||
uint256 _pubkeyY,
|
||||
bytes32 _signersHash,
|
||||
uint256 _totalSigners
|
||||
) internal {
|
||||
require(GhostEllipticCurves.isOnCurve(_pubkeyX, _pubkeyY)); // is pubkey on curve
|
||||
pubkeyX = _pubkeyX;
|
||||
pubkeyY = _pubkeyY;
|
||||
signersHash = _signersHash;
|
||||
maxLost = _totalSigners - (_totalSigners * 2 / 3 + 1);
|
||||
}
|
||||
|
||||
function _verify(
|
||||
uint256 s, bytes32 m,
|
||||
bytes calldata nonces,
|
||||
bytes calldata proof,
|
||||
bytes calldata missedIndexes
|
||||
) internal view returns (uint256 res) {
|
||||
uint256 px = pubkeyX;
|
||||
uint256 py = pubkeyY;
|
||||
uint256 e;
|
||||
uint256 rx;
|
||||
uint256 ry;
|
||||
|
||||
{
|
||||
uint256[] memory rNonces = new uint256[](4);
|
||||
uint256[] memory coefficients = new uint256[](2);
|
||||
|
||||
_reconstructNonces(rNonces, nonces);
|
||||
_computeCoefficients(px, m, rNonces, coefficients);
|
||||
|
||||
(rx, ry) = _aggregateNonce(rNonces, coefficients);
|
||||
e = _computeChallenge(bytes32(rx), bytes32(px), m);
|
||||
(rx, ry) = _restoreAdaptiveNonce(nonces, coefficients);
|
||||
}
|
||||
|
||||
{
|
||||
require(signersHash == sha256(proof)); // check proof correctness
|
||||
(px, py) = _aggregatePubkey(px, py, proof, missedIndexes);
|
||||
}
|
||||
|
||||
unchecked {
|
||||
s = GhostEllipticCurves.N - mulmod(s, px, GhostEllipticCurves.N);
|
||||
e = GhostEllipticCurves.N - mulmod(e, px, GhostEllipticCurves.N);
|
||||
}
|
||||
|
||||
uint8 parity = py % 2 == 0 ? 27 : 28;
|
||||
require(ecrecover(bytes32(s), parity, bytes32(px), bytes32(e)) == _nonceCommitment(rx, ry));
|
||||
}
|
||||
|
||||
function _reconstructNonces(
|
||||
uint256[] memory rNonces,
|
||||
bytes calldata nonces
|
||||
) internal pure {
|
||||
require(nonces.length == 256); // nonces length check
|
||||
|
||||
uint256 i;
|
||||
for (; i < 2;) {
|
||||
uint256 rmx;
|
||||
uint256 rmy;
|
||||
uint256 rex;
|
||||
uint256 rey;
|
||||
|
||||
assembly {
|
||||
let base := add(nonces.offset, mul(i, 128))
|
||||
rmx := calldataload(base)
|
||||
rmy := calldataload(add(base, 32))
|
||||
rex := calldataload(add(base, 64))
|
||||
rey := calldataload(add(base, 96))
|
||||
}
|
||||
|
||||
if (rex != 0 && rey != 0) {
|
||||
(rmx, rmy, rex) = GhostEllipticCurves.projectiveAddMixed(
|
||||
rmx, rmy, 1, rex, rey
|
||||
);
|
||||
(rmx, rmy) = GhostEllipticCurves.toAffine(rmx, rmy, rex);
|
||||
}
|
||||
require(GhostEllipticCurves.isOnCurve(rmx, rmy)); // is missing nonce on curve
|
||||
|
||||
rNonces[i*2] = rmx;
|
||||
rNonces[i*2 + 1] = rmy;
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
}
|
||||
|
||||
function _computeCoefficients(
|
||||
uint256 px, bytes32 m,
|
||||
uint256[] memory rNonces,
|
||||
uint256[] memory coefficients
|
||||
) internal pure {
|
||||
uint256 r1x = rNonces[0]; // gas savings
|
||||
uint256 r2x = rNonces[2]; // gas savings
|
||||
|
||||
coefficients[0] = _computeCoefficientB(r1x, r2x, px, m);
|
||||
coefficients[1] = _computeCoefficientD(r1x, r2x, px, m);
|
||||
}
|
||||
|
||||
function _aggregateNonce(
|
||||
uint256[] memory rNonces,
|
||||
uint256[] memory coefficients
|
||||
) internal pure returns (uint256 rx, uint256 ry) {
|
||||
uint256 rz;
|
||||
|
||||
(rx, ry, rz) = GhostEllipticCurves.mulAddAffinePair(
|
||||
rNonces[0], rNonces[1], coefficients[0],
|
||||
rNonces[2], rNonces[3], coefficients[1]
|
||||
);
|
||||
(rx, ry) = GhostEllipticCurves.toAffine(rx, ry, rz);
|
||||
require(GhostEllipticCurves.isOnCurve(rx, ry)); // is aggnonce on curve
|
||||
}
|
||||
|
||||
function _restoreAdaptiveNonce(
|
||||
bytes calldata nonces,
|
||||
uint256[] memory coefficients
|
||||
) internal pure returns (uint256, uint256) {
|
||||
uint256 r1x;
|
||||
uint256 r1y;
|
||||
uint256 r2x;
|
||||
uint256 r2y;
|
||||
|
||||
assembly {
|
||||
let base := nonces.offset
|
||||
r1x := calldataload(add(base, 0))
|
||||
r1y := calldataload(add(base, 32))
|
||||
r2x := calldataload(add(base, 128))
|
||||
r2y := calldataload(add(base, 160))
|
||||
}
|
||||
|
||||
(r1x, r1y, r2x) = GhostEllipticCurves.mulAddAffinePair(
|
||||
r1x, r1y, coefficients[0],
|
||||
r2x, r2y, coefficients[1]
|
||||
);
|
||||
(r1x, r1y) = GhostEllipticCurves.toAffine(r1x, r1y, r2x);
|
||||
require(GhostEllipticCurves.isOnCurve(r1x, r1y)); // is restored nonce on curve
|
||||
|
||||
return (r1x, r1y);
|
||||
}
|
||||
|
||||
function _computeAggregationCoefficients(
|
||||
uint16 length,
|
||||
bytes32[] memory ais,
|
||||
bytes calldata proof
|
||||
) internal pure {
|
||||
uint16 i = length;
|
||||
for (; i > 0;) {
|
||||
unchecked { --i; }
|
||||
|
||||
uint256 pix;
|
||||
uint16 l;
|
||||
uint16 r;
|
||||
|
||||
assembly {
|
||||
let base := add(proof.offset, mul(i, 128))
|
||||
pix := calldataload(base)
|
||||
|
||||
l := add(shl(1, i), 1)
|
||||
r := add(l, 1)
|
||||
}
|
||||
|
||||
ais[i] = bytes32(_computeCoefficientKeyAgg(
|
||||
l < length ? ais[l] : bytes32(0x0),
|
||||
r < length ? ais[r] : bytes32(0x0),
|
||||
bytes32(pix)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function _checkAggregationCorrectness(
|
||||
uint256 xx, uint256 yy, uint256 ai,
|
||||
bytes calldata proof
|
||||
) internal pure returns (uint256 res) {
|
||||
uint256 px;
|
||||
uint256 py;
|
||||
uint256 pz;
|
||||
|
||||
uint16 i;
|
||||
for (; i < 3;) {
|
||||
uint256 x;
|
||||
uint256 y;
|
||||
|
||||
assembly {
|
||||
let base := add(proof.offset, mul(i, 128))
|
||||
let j := mul(gt(i, 0), 64)
|
||||
x := calldataload(add(base, j))
|
||||
y := calldataload(add(base, add(32, j)))
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
(px, py, pz) = GhostEllipticCurves.mulAddAffineSingle(x, y, ai);
|
||||
} else {
|
||||
(px, py, pz) = GhostEllipticCurves.projectiveAddMixed(px, py, pz, x, y);
|
||||
}
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
|
||||
(px, py) = GhostEllipticCurves.toAffine(px, py, pz);
|
||||
uint256 hix;
|
||||
assembly {
|
||||
let base := proof.offset
|
||||
hix := calldataload(add(base, 64))
|
||||
let hiy := calldataload(add(base, 96))
|
||||
|
||||
if iszero(and(eq(xx, px), eq(xx, hix))) {
|
||||
revert(0, 0)
|
||||
}
|
||||
|
||||
if iszero(and(eq(yy, py), eq(yy, hiy))) {
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _aggregatePubkey(
|
||||
uint256 px, uint256 py,
|
||||
bytes calldata proof,
|
||||
bytes calldata missedIndexes
|
||||
) internal view returns (uint256, uint256) {
|
||||
uint256 pz = 1;
|
||||
uint16 length = uint16(proof.length);
|
||||
length = (length & 63) == 0 ? (length >> 6) : 0;
|
||||
|
||||
uint16 i = length;
|
||||
uint16 lost;
|
||||
|
||||
for (; i > 0;) {
|
||||
unchecked { --i; }
|
||||
|
||||
uint256 hix;
|
||||
uint256 hiy;
|
||||
bool isMissed;
|
||||
|
||||
assembly {
|
||||
isMissed := byte(0, calldataload(add(missedIndexes.offset, i)))
|
||||
}
|
||||
|
||||
if (isMissed) {
|
||||
assembly {
|
||||
let base := add(proof.offset, mul(i, 64))
|
||||
hix := calldataload(base)
|
||||
hiy := calldataload(add(base, 32))
|
||||
}
|
||||
|
||||
(px, py, pz) = GhostEllipticCurves.projectiveAddMixed(
|
||||
px, py, pz, hix, GhostEllipticCurves.P - hiy
|
||||
);
|
||||
unchecked { ++lost; }
|
||||
}
|
||||
}
|
||||
|
||||
(px, py) = GhostEllipticCurves.toAffine(px, py, pz);
|
||||
require(GhostEllipticCurves.isOnCurve(px, py)); // is point (Hagg - Hmissing) on curve
|
||||
require(maxLost >= lost); // is enough threshold
|
||||
|
||||
return (px, py);
|
||||
}
|
||||
|
||||
function _nonceCommitment(uint256 px, uint256 py) internal pure returns (address) {
|
||||
bytes32 h = keccak256(abi.encodePacked(bytes32(px), bytes32(py)));
|
||||
return address(uint160(uint256(h)));
|
||||
}
|
||||
|
||||
function _computeCoefficientKeyAgg(bytes32 l, bytes32 r, bytes32 s) internal pure returns (uint256) {
|
||||
// Below line is the same as: bytes32 tag = sha256("EXODUS/KeyAggCoef");
|
||||
// Double check it with help of `https://emn178.github.io/online-tools/sha256.html`
|
||||
bytes32 tag = 0x172d284f34ce926b36667a8a8617b9cd810cc61b43d644e7b770d5859a3a3d43;
|
||||
|
||||
bytes32 sHash = sha256(abi.encode(tag, tag, s));
|
||||
return uint256(sha256(abi.encodePacked(tag, tag, l, r, sHash))) % GhostEllipticCurves.N;
|
||||
}
|
||||
|
||||
function _computeCoefficientB(uint256 r1x, uint256 r2x, uint256 px, bytes32 m) internal pure returns (uint256) {
|
||||
// Below line is the same as: bytes32 tag = sha256("EXODUS/nonceB");
|
||||
// Double check it with help of `https://emn178.github.io/online-tools/sha256.html`
|
||||
bytes32 tag = 0x9163366544d6028e0142472531d58dba1006e5a0a528d94030f178ec4ddc5f8e;
|
||||
return uint256(sha256(abi.encodePacked(tag, tag, r1x, r2x, px, m))) % GhostEllipticCurves.N;
|
||||
}
|
||||
|
||||
function _computeCoefficientD(uint256 r1x, uint256 r2x, uint256 px, bytes32 m) internal pure returns (uint256) {
|
||||
// Below line is the same as: bytes32 tag = sha256("EXODUS/nonceD");
|
||||
// Double check it with help of `https://emn178.github.io/online-tools/sha256.html`
|
||||
bytes32 tag = 0x25b86693490e759343e5fb3e272667c19273dea862f4ef52f7656cfb1563e9f6;
|
||||
return uint256(sha256(abi.encodePacked(tag, tag, m, px, r1x, r2x))) % GhostEllipticCurves.N;
|
||||
}
|
||||
|
||||
function _computeChallenge(bytes32 rx, bytes32 px, bytes32 m) internal pure returns (uint256) {
|
||||
// Below line is the same as: bytes32 tag = sha256("EXODUS/challenge");
|
||||
// Double check it with help of `https://emn178.github.io/online-tools/sha256.html`
|
||||
bytes32 tag = 0xf0bf915ac954f4aa752d09a0b6a57c577bf0bcca4661ee26c7e613466cf5f9a1;
|
||||
return uint256(sha256(abi.encodePacked(tag, tag, rx, px, m))) % GhostEllipticCurves.N;
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,9 @@ pragma solidity ^0.8.0;
|
||||
** @author Witnet Foundation
|
||||
*/
|
||||
library EllipticCurve {
|
||||
// Pre-computed constant for 2 ** 128 - 1
|
||||
uint256 private constant U128_MAX = 340282366920938463463374607431768211455;
|
||||
|
||||
// Pre-computed constant for 2 ** 255
|
||||
uint256 private constant U255_MAX_PLUS_1 =
|
||||
57896044618658097711785492504343953926634992332820282019728792003956564819968;
|
||||
|
||||
@ -14,8 +14,6 @@ library EllipticCurveProjective {
|
||||
uint256 public constant P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
|
||||
uint256 public constant N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
||||
|
||||
uint256 private constant B3 = 21;
|
||||
|
||||
function findMaxBitLength(
|
||||
uint256 k1,
|
||||
uint256 k2,
|
||||
@ -72,9 +70,41 @@ library EllipticCurveProjective {
|
||||
}
|
||||
}
|
||||
|
||||
function mulAddProjectiveSingle(
|
||||
uint256 x1, uint256 y1, uint256 k1
|
||||
) internal pure returns (uint256 x2, uint256 y2, uint256 z2) {
|
||||
// We implement the Straus-Shamir trick described in
|
||||
// Trading Inversions for Multiplications in Elliptic Curve Cryptography.
|
||||
// (https://eprint.iacr.org/2003/257.pdf Page 7).
|
||||
|
||||
// TODO: handle edge case k1 == 0
|
||||
uint256 bits = findMaxBitLength(k1, 0, 0, 0);
|
||||
|
||||
x2 = 0;
|
||||
y2 = 1;
|
||||
z2 = 0;
|
||||
|
||||
for (; bits > 0;) {
|
||||
unchecked { --bits; }
|
||||
|
||||
(x2, y2, z2) = projectiveDouble(x2, y2, z2);
|
||||
|
||||
uint8 mask;
|
||||
assembly {
|
||||
mask := and(shr(bits, k1), 1)
|
||||
}
|
||||
|
||||
if (mask != 0) {
|
||||
(x2, y2, z2) = projectiveAddMixed(
|
||||
x2, y2, z2, x1, y1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mulAddProjectivePair(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2
|
||||
uint256 x1, uint256 y1, uint256 z1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 z2, uint256 k2
|
||||
) internal pure returns (uint256 x3, uint256 y3, uint256 z3) {
|
||||
// We implement the Straus-Shamir trick described in
|
||||
// Trading Inversions for Multiplications in Elliptic Curve Cryptography.
|
||||
@ -86,10 +116,10 @@ library EllipticCurveProjective {
|
||||
uint256[4] memory precomputedYs;
|
||||
uint256[4] memory precomputedZs;
|
||||
|
||||
precomputedXs[1] = x2; precomputedYs[1] = y2; precomputedZs[1] = 1; // 01: P2
|
||||
precomputedXs[2] = x1; precomputedYs[2] = y1; precomputedZs[2] = 1; // 10: P1
|
||||
precomputedXs[1] = x2; precomputedYs[1] = y2; precomputedZs[1] = z2; // 01: P2
|
||||
precomputedXs[2] = x1; precomputedYs[2] = y1; precomputedZs[2] = z1; // 10: P1
|
||||
|
||||
(x3, y3, z3) = projectiveAddMixed(x1, y1, 1, x2, y2); // 11: P1+P2
|
||||
(x3, y3, z3) = projectiveAdd(x1, y1, z1, x2, y2, z2); // 11: P1+P2
|
||||
precomputedXs[3] = x3;
|
||||
precomputedYs[3] = y3;
|
||||
precomputedZs[3] = z3;
|
||||
@ -111,18 +141,10 @@ library EllipticCurveProjective {
|
||||
)
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mask == 3) {
|
||||
if (mask != 0) {
|
||||
(x3, y3, z3) = projectiveAdd(
|
||||
x3, y3, z3, precomputedXs[mask], precomputedYs[mask], precomputedZs[mask]
|
||||
);
|
||||
} else {
|
||||
(x3, y3, z3) = projectiveAddMixed(
|
||||
x3, y3, z3, precomputedXs[mask], precomputedYs[mask]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,7 +226,7 @@ library EllipticCurveProjective {
|
||||
uint256 x2, uint256 y2, uint256 k2,
|
||||
uint256 x3, uint256 y3, uint256 k3,
|
||||
uint256 x4, uint256 y4, uint256 k4
|
||||
) internal pure returns (uint256 x5, uint256 y5, uint256 z5) {
|
||||
) internal pure returns (uint256, uint256, uint256) {
|
||||
// We implement the Straus-Shamir trick described in
|
||||
// Trading Inversions for Multiplications in Elliptic Curve Cryptography.
|
||||
// (https://eprint.iacr.org/2003/257.pdf Page 7).
|
||||
@ -267,14 +289,14 @@ library EllipticCurveProjective {
|
||||
y4
|
||||
); // 1111: P4+P3+P2+P1
|
||||
|
||||
x5 = 0;
|
||||
y5 = 1;
|
||||
z5 = 0;
|
||||
x1 = 0;
|
||||
y1 = 1;
|
||||
uint256 z1 = 0;
|
||||
|
||||
for (; bits > 0;) {
|
||||
unchecked { --bits; }
|
||||
|
||||
(x5, y5, z5) = projectiveDouble(x5, y5, z5);
|
||||
(x1, y1, z1) = projectiveDouble(x1, y1, z1);
|
||||
|
||||
uint8 mask;
|
||||
uint16 bitmask;
|
||||
@ -300,15 +322,16 @@ library EllipticCurveProjective {
|
||||
}
|
||||
|
||||
if (bitmask == 1) {
|
||||
(x5, y5, z5) = projectiveAddMixed(
|
||||
x5, y5, z5, precomputedXs[mask], precomputedYs[mask]
|
||||
(x1, y1, z1) = projectiveAddMixed(
|
||||
x1, y1, z1, precomputedXs[mask], precomputedYs[mask]
|
||||
);
|
||||
} else {
|
||||
(x5, y5, z5) = projectiveAdd(
|
||||
x5, y5, z5, precomputedXs[mask], precomputedYs[mask], precomputedZs[mask]
|
||||
(x1, y1, z1) = projectiveAdd(
|
||||
x1, y1, z1, precomputedXs[mask], precomputedYs[mask], precomputedZs[mask]
|
||||
);
|
||||
}
|
||||
}
|
||||
return (x1, y1, z1);
|
||||
}
|
||||
|
||||
function projectiveAddMixed(
|
||||
@ -335,10 +358,10 @@ library EllipticCurveProjective {
|
||||
Y3 = mulmod(X2, Z1, P); // 8. Y3 ← X2 · Z1 => (X2·Z1)
|
||||
Y3 = addmod(Y3, X1, P); // 9. Y3 ← Y3 + X1 => (X2·Z1 + X1)
|
||||
t0 = mulmod(3, t0, P); // 10. t0 ← X3 + t0 => (3·(X1·X2))
|
||||
uint256 t2 = mulmod(B3, Z1, P); // 11. t2 ← b3 · Z1 => (b3·Z1)
|
||||
uint256 t2 = mulmod(21, Z1, P); // 11. t2 ← b3 · Z1 => (b3·Z1)
|
||||
Z3 = addmod(t1, t2, P); // 12. Z3 ← t1 + t2 => (Y1·Y2 + b·3·Z1)
|
||||
t1 = addmod(t1, P - t2, P); // 13. t1 ← t1 − t2 => (Y1·Y2 - b·3·Z1)
|
||||
Y3 = mulmod(B3, Y3, P); // 14. Y3 ← b3 · Y3 => 3·b·(X2·Z1 + X1)
|
||||
Y3 = mulmod(21, Y3, P); // 14. Y3 ← b3 · Y3 => 3·b·(X2·Z1 + X1)
|
||||
X3 = mulmod(t4, Y3, P); // 15. X3 ← t4 · Y3 => (Y2·Z1 + Y1)·b·3·(X2·Z1 + X1)
|
||||
t2 = mulmod(t3, t1, P); // 16. t2 ← t3 · t1 => ((X2·Y1 + X1·Y2)·(Y1·Y2 - b3·Z1))
|
||||
X3 = addmod(t2, P - X3, P); // 17. X3 ← t2 − X3 => ((X2·Y1 + X1·Y2)·(Y1·Y2 - b3·Z1) - 3·B·(Y2·Z1 + Y1)·(X2·Z1 + X1))
|
||||
@ -385,10 +408,10 @@ library EllipticCurveProjective {
|
||||
Y3 = addmod(X3, P - Y3, P); // 18. Y3 ← X3 - Y3 => ((X1 + Z1)·(X2 + Z2) - X1·X2 - Z1·Z2)
|
||||
X3 = addmod(t0, t0, P); // 19. X3 ← t0 + t0 => (2·X1·X2)
|
||||
t0 = addmod(X3, t0, P); // 20. t0 ← X3 + t0 => (3·X1·X2)
|
||||
t2 = mulmod(B3, t2, P); // 21. t2 ← B3 · t2 => 3b · Z1·Z2
|
||||
t2 = mulmod(21, t2, P); // 21. t2 ← B3 · t2 => 3b · Z1·Z2
|
||||
Z3 = addmod(t1, t2, P); // 22. Z3 ← t1 + t2 => Y1·Y2 + 3·b·Z1·Z2
|
||||
t1 = addmod(t1, P - t2, P); // 23. t1 ← t1 - t2 => Y1·Y2 - 3·b·Z1·Z2
|
||||
Y3 = mulmod(B3, Y3, P); // 24. Y3 ← B3 · Y3 => 3b · ((X1+Z1)(X2+Z2) - X1·X2 - Z1·Z2)
|
||||
Y3 = mulmod(21, Y3, P); // 24. Y3 ← B3 · Y3 => 3b · ((X1+Z1)(X2+Z2) - X1·X2 - Z1·Z2)
|
||||
X3 = mulmod(t4, Y3, P); // 25. X3 ← t4 · Y3 => 3b·((Y1+Z1)(Y2+Z2)-Y1·Y2-Z1·Z2) · ((X1+Z1)(X2+Z2)-X1·X2-Z1·Z2)
|
||||
t2 = mulmod(t3, t1, P); // 26. t2 ← t3 · t1 => ((X1+Y1)(X2+Y2)-X1·X2-Y1·Y2) · (Y1·Y2 - 3·b·Z1·Z2)
|
||||
X3 = addmod(t2, P - X3, P); // 27. X3 ← t2 - X3 => (X1Y2+X2Y1)(Y1·Y2-3·b·Z1·Z2) - 3b(Y1·Z2+Y2·Z1)(X1·Z2+X2·Z1)
|
||||
|
||||
300
src/libraries/GhostEllipticCurves.sol
Normal file
300
src/libraries/GhostEllipticCurves.sol
Normal file
@ -0,0 +1,300 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
// Vectors for secp256k1 are difficult to find. These are the vectors from:
|
||||
// https://web.archive.org/web/20190724010836/https://chuckbatson.wordpress.com/2014/11/26/secp256k1-test-vectors
|
||||
|
||||
library GhostEllipticCurves {
|
||||
// Constants are taken from https://en.bitcoin.it/wiki/Secp256k1
|
||||
uint256 internal constant B = 7;
|
||||
uint256 internal constant P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
|
||||
uint256 internal constant N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
||||
|
||||
function findMaxBitLength(uint256 k1, uint256 k2) internal pure returns (uint256 bits) {
|
||||
assembly {
|
||||
// Find maximum of the three scalars
|
||||
let max := k1
|
||||
if gt(k2, max) { max := k2 }
|
||||
|
||||
// if (v >> 128 != 0) { v >>= 128; bits += 128; }
|
||||
if gt(max, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
|
||||
max := shr(128, max)
|
||||
bits := 128
|
||||
}
|
||||
// if (v >> 64 != 0) { v >>= 64; bits += 64; }
|
||||
if gt(max, 0xFFFFFFFFFFFFFFFF) {
|
||||
max := shr(64, max)
|
||||
bits := add(bits, 64)
|
||||
}
|
||||
// if (v >> 32 != 0) { v >>= 32; bits += 32; }
|
||||
if gt(max, 0xFFFFFFFF) {
|
||||
max := shr(32, max)
|
||||
bits := add(bits, 32)
|
||||
}
|
||||
// if (v >> 16 != 0) { v >>= 16; bits += 16; }
|
||||
if gt(max, 0xFFFF) {
|
||||
max := shr(16, max)
|
||||
bits := add(bits, 16)
|
||||
}
|
||||
// if (v >> 8 != 0) { v >>= 8; bits += 8; }
|
||||
if gt(max, 0xFF) {
|
||||
max := shr(8, max)
|
||||
bits := add(bits, 8)
|
||||
}
|
||||
// if (v >> 4 != 0) { v >>= 4; bits += 4; }
|
||||
if gt(max, 0xF) {
|
||||
max := shr(4, max)
|
||||
bits := add(bits, 4)
|
||||
}
|
||||
// if (v >> 2 != 0) { v >>= 2; bits += 2; }
|
||||
if gt(max, 0x3) {
|
||||
max := shr(2, max)
|
||||
bits := add(bits, 2)
|
||||
}
|
||||
// if (v >> 1 != 0) { /* v >>= 1; */ bits += 1; }
|
||||
if gt(max, 0x1) {
|
||||
bits := add(bits, 1)
|
||||
}
|
||||
bits := add(bits, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function mulAddAffinePair(
|
||||
uint256 x1, uint256 y1, uint256 k1,
|
||||
uint256 x2, uint256 y2, uint256 k2
|
||||
) internal pure returns (uint256 x3, uint256 y3, uint256 z3) {
|
||||
// We implement the Straus-Shamir trick described in
|
||||
// Trading Inversions for Multiplications in Elliptic Curve Cryptography.
|
||||
// (https://eprint.iacr.org/2003/257.pdf Page 7).
|
||||
|
||||
uint256 bits = findMaxBitLength(k1, k2);
|
||||
|
||||
uint256[4] memory precomputedXs;
|
||||
uint256[4] memory precomputedYs;
|
||||
uint256[4] memory precomputedZs;
|
||||
|
||||
precomputedXs[1] = x2; precomputedYs[1] = y2; precomputedZs[1] = 1; // 01: P2
|
||||
precomputedXs[2] = x1; precomputedYs[2] = y1; precomputedZs[2] = 1; // 10: P1
|
||||
(precomputedXs[3], precomputedYs[3], precomputedZs[3]) = projectiveAdd(x1, y1, 1, x2, y2, 1); // 11: P1+P2
|
||||
|
||||
y3 = 1;
|
||||
|
||||
for (; bits > 0;) {
|
||||
unchecked { --bits; }
|
||||
|
||||
(x3, y3, z3) = projectiveDouble(x3, y3, z3);
|
||||
|
||||
uint8 mask;
|
||||
assembly {
|
||||
mask := or(
|
||||
shl(1, and(shr(bits, k1), 1)),
|
||||
and(shr(bits, k2), 1)
|
||||
)
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mask == 3) {
|
||||
(x3, y3, z3) = projectiveAdd(
|
||||
x3, y3, z3, precomputedXs[mask], precomputedYs[mask], precomputedZs[mask]
|
||||
);
|
||||
} else {
|
||||
(x3, y3, z3) = projectiveAddMixed(
|
||||
x3, y3, z3, precomputedXs[mask], precomputedYs[mask]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mulAddAffineSingle(
|
||||
uint256 x, uint256 y, uint256 k
|
||||
) internal pure returns (uint256 x_, uint256 y_, uint256 z_) {
|
||||
// We implement the Straus-Shamir trick described in
|
||||
// Trading Inversions for Multiplications in Elliptic Curve Cryptography.
|
||||
// (https://eprint.iacr.org/2003/257.pdf Page 7).
|
||||
|
||||
y_ = 1;
|
||||
uint256 bits = findMaxBitLength(k, 0);
|
||||
|
||||
for (; bits > 0;) {
|
||||
unchecked { --bits; }
|
||||
|
||||
(x_, y_, z_) = projectiveDouble(x_, y_, z_);
|
||||
|
||||
uint8 mask;
|
||||
assembly {
|
||||
mask := and(shr(bits, k), 1)
|
||||
}
|
||||
|
||||
if (mask != 0) {
|
||||
(x_, y_, z_) = projectiveAddMixed(x_, y_, z_, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function projectiveDouble(
|
||||
uint256 X,
|
||||
uint256 Y,
|
||||
uint256 Z
|
||||
) internal pure returns (uint256 X3, uint256 Y3, uint256 Z3) {
|
||||
// We implement the complete addition formula from Renes-Costello-Batina 2015
|
||||
// (https://eprint.iacr.org/2015/1060 Algorithm 9).
|
||||
|
||||
// X3 = 2XY (Y^2 − 9bZ^2), (ok)
|
||||
// Y3 = (Y^2 − 9bZ^2)(Y^2 + 3bZ^2) + 24bY^2Z^2,
|
||||
// Z3 = 8Y^3Z
|
||||
|
||||
uint256 t0 = mulmod(Y, Y, P); // 1. t0 ← Y · Y => (Y²)
|
||||
Z3 = mulmod(8, t0, P); // 2. Z3 ← t0 + t0 => (8Y²)
|
||||
uint256 t1 = mulmod(Y, Z, P); // 3. t1 ← Y · Z => (YZ)
|
||||
uint256 t2 = mulmod(Z, Z, P); // 4. t2 ← Z · Z => (Z²)
|
||||
t2 = mulmod(21, t2, P); // 5. t2 ← b3 · t2 => (3bZ²)
|
||||
X3 = mulmod(t2, Z3, P); // 6. X3 ← t2 · Z3 => (3bZ²8Y²)
|
||||
Y3 = addmod(t0, t2, P); // 7. Y3 ← t0 + t2 => (Y² + 3bZ²)
|
||||
Z3 = mulmod(t1, Z3, P); // 8. Z3 ← t1 · Z3 => (YZ · 8Y² = 8Y³Z)
|
||||
t1 = addmod(t0, P - mulmod(3, t2, P), P); // 9. t1 ← t0 - (3 · t2) => (Y² - 9bZ²)
|
||||
Y3 = addmod(X3, mulmod(t1, Y3, P), P); // 10. Y3 ← t1 · (t1 · Y3) => ((Y² - 9bZ²) · (Y² + 3bZ²))
|
||||
X3 = mulmod(t1, mulmod(X, Y, P), P); // 11. X3 ← t1 · (X1 · Y1) => ((Y² - 9bZ²) · XY)
|
||||
X3 = addmod(X3, X3, P); // 12. X3 ← X3 + X3 => ((Y² - 9bZ²) · 2XY)
|
||||
}
|
||||
|
||||
function projectiveAddMixed(
|
||||
uint256 X1,
|
||||
uint256 Y1,
|
||||
uint256 Z1,
|
||||
uint256 X2,
|
||||
uint256 Y2
|
||||
) internal pure returns (uint256 X3, uint256 Y3, uint256 Z3) {
|
||||
// We implement the complete addition formula from Renes-Costello-Batina 2015
|
||||
// (https://eprint.iacr.org/2015/1060 Algorithm 8).
|
||||
|
||||
// X3 = (X1Y2 + X2Y1)(Y1Y2 − 3bZ1) − 3b(Y1 + Y2Z1)(X1 + X2Z1),
|
||||
// Y3 = (Y1Y2 + 3bZ1)(Y1Y2 − 3bZ1) + 9bX1X2(X1 + X2Z1),
|
||||
// Z3 = (Y1 + Y2Z1)(Y1Y2 + 3bZ1) + 3X1X2(X1Y2 + X2Y1),
|
||||
|
||||
assembly {
|
||||
let t0 := mulmod(X1, X2, P) // 1. t0 ← X1 · X2 => (X1·X2)
|
||||
let t1 := mulmod(Y1, Y2, P) // 2. t1 ← Y1 · Y2 => (Y1·Y2)
|
||||
let t3 := mulmod(X2, Y1, P) // 3. t3 ← X2 + Y2 => (X2·Y1)
|
||||
let t4 := mulmod(X1, Y2, P) // 4. t4 ← X1 + Y1 => (X1·Y2)
|
||||
t3 := addmod(t3, t4, P) // 5. t3 ← t3 − t4 => (X2·Y1 + X1·Y2)
|
||||
t4 := mulmod(Y2, Z1, P) // 6. t4 ← Y2 · Z1 => (Y2·Z1)
|
||||
t4 := addmod(t4, Y1, P) // 7. t4 ← t4 + Y1 => (Y2·Z1 + Y1)
|
||||
Y3 := mulmod(X2, Z1, P) // 8. Y3 ← X2 · Z1 => (X2·Z1)
|
||||
Y3 := addmod(Y3, X1, P) // 9. Y3 ← Y3 + X1 => (X2·Z1 + X1)
|
||||
t0 := mulmod(3, t0, P) // 10. t0 ← X3 + t0 => (3·(X1·X2))
|
||||
let t2 := mulmod(21, Z1, P) // 11. t2 ← b3 · Z1 => (b3·Z1)
|
||||
Z3 := addmod(t1, t2, P) // 12. Z3 ← t1 + t2 => (Y1·Y2 + b·3·Z1)
|
||||
t1 := addmod(t1, sub(P, t2), P) // 13. t1 ← t1 − t2 => (Y1·Y2 - b·3·Z1)
|
||||
Y3 := mulmod(21, Y3, P) // 14. Y3 ← b3 · Y3 => 3·b·(X2·Z1 + X1)
|
||||
X3 := mulmod(t4, Y3, P) // 15. X3 ← t4 · Y3 => (Y2·Z1 + Y1)·b·3·(X2·Z1 + X1)
|
||||
t2 := mulmod(t3, t1, P) // 16. t2 ← t3 · t1 => ((X2·Y1 + X1·Y2)·(Y1·Y2 - b3·Z1))
|
||||
X3 := addmod(t2, sub(P, X3), P) // 17. X3 ← t2 − X3 => ((X2·Y1 + X1·Y2)·(Y1·Y2 - b3·Z1) - 3·B·(Y2·Z1 + Y1)·(X2·Z1 + X1))
|
||||
Y3 := mulmod(Y3, t0, P) // 18. Y3 ← Y3 · t0 => (9·b·(X2·Z1 + X1)·X1·X2)
|
||||
t1 := mulmod(t1, Z3, P) // 19. t1 ← t1 · Z3 => (Y1·Y2 - b·3·Z1)·(Y1·Y2 + b·3·Z1)
|
||||
Y3 := addmod(t1, Y3, P) // 20. Y3 ← t1 + Y3 => ((Y1·Y2 - b·3·Z1)·(Y1·Y2 + 3·b·Z1) + 9·b·(X2·Z1 + X1)·X1·X2)
|
||||
t0 := mulmod(t0, t3, P) // 21. t0 ← t0 · t3 => (3·X2·Y1 + (X2·Y1 + X1·Y2))
|
||||
Z3 := mulmod(Z3, t4, P) // 22. Z3 ← Z3 · t4 => (Y1·Y2 + b·3·Z1)·(Y2·Z1 + Y1)
|
||||
Z3 := addmod(Z3, t0, P) // 23. Z3 ← Z3 + t0 => ((Y1·Y2 + b·3·Z1)·(Y2·Z1 + Y1) + 3·X2·Y1·(X2·Y1 + X1·Y2))
|
||||
}
|
||||
}
|
||||
|
||||
function projectiveAdd(
|
||||
uint256 X1,
|
||||
uint256 Y1,
|
||||
uint256 Z1,
|
||||
uint256 X2,
|
||||
uint256 Y2,
|
||||
uint256 Z2
|
||||
) internal pure returns (uint256 X3, uint256 Y3, uint256 Z3) {
|
||||
// We implement the complete addition formula from Renes-Costello-Batina 2015
|
||||
// (https://eprint.iacr.org/2015/1060 Algorithm 7).
|
||||
|
||||
// X3 = (X1Y2 + X2Y1)(Y1Y2 − 3bZ1Z2) − 3b(Y1Z2 + Y2Z1)(X1Z2 + X2Z1),
|
||||
// Y3 = (Y1Y2 + 3bZ1Z2)(Y1Y2 − 3bZ1Z2) + 9bX1X2(X1Z2 + X2Z1),
|
||||
// Z3 = (Y1Z2 + Y2Z1)(Y1Y2 + 3bZ1Z2) + 3X1X2(X1Y2 + X2Y1),
|
||||
|
||||
uint256 t0 = mulmod(X1, X2, P); // 1. t0 ← X1 · X2 => (X1·X2)
|
||||
uint256 t1 = mulmod(Y1, Y2, P); // 2. t1 ← Y1 · Y2 => (Y1·Y2)
|
||||
uint256 t2 = mulmod(Z1, Z2, P); // 3. t2 ← Z1 · Z2 => (Z1·Z2)
|
||||
uint256 t3 = addmod(X1, Y1, P); // 4. t3 ← X1 + Y1 => (X1 + Y1)
|
||||
uint256 t4 = addmod(X2, Y2, P); // 5. t4 ← X2 + Y2 => (X2 + Y2)
|
||||
t3 = mulmod(t3, t4, P); // 6. t3 ← t3 · t4 => ((X1 + Y1) · (X2 + Y2))
|
||||
t4 = addmod(t0, t1, P); // 7. t4 ← t0 + t1 => (X1·X2 + Y1·Y2)
|
||||
t3 = addmod(t3, P - t4, P); // 8. t3 ← t3 - t4 => ((X1 + Y1)·(X2 + Y2) - X1·X2 - Y1·Y2)
|
||||
t4 = addmod(Y1, Z1, P); // 9. t4 ← Y1 + Z1 => (Y1 + Z1)
|
||||
X3 = addmod(Y2, Z2, P); // 10. X3 ← Y2 + Z2 => (Y2 + Z2)
|
||||
t4 = mulmod(t4, X3, P); // 11. t4 ← t4 · X3 => ((Y1 + Z1) · (Y2 + Z2))
|
||||
X3 = addmod(t1, t2, P); // 12. X3 ← t1 + t2 => (Y1·Y2 + Z1·Z2)
|
||||
t4 = addmod(t4, P - X3, P); // 13. t4 ← t4 - X3 => ((Y1 + Z1)·(Y2 + Z2) - Y1·Y2 - Z1·Z2)
|
||||
X3 = addmod(X1, Z1, P); // 14. X3 ← X1 + Z1 => (X1 + Z1)
|
||||
Y3 = addmod(X2, Z2, P); // 15. Y3 ← X2 + Z2 => (X2 + Z2)
|
||||
X3 = mulmod(X3, Y3, P); // 16. X3 ← X3 · Y3 => ((X1 + Z1) · (X2 + Z2))
|
||||
Y3 = addmod(t0, t2, P); // 17. Y3 ← t0 + t2 => (X1·X2 + Z1·Z2)
|
||||
Y3 = addmod(X3, P - Y3, P); // 18. Y3 ← X3 - Y3 => ((X1 + Z1)·(X2 + Z2) - X1·X2 - Z1·Z2)
|
||||
X3 = addmod(t0, t0, P); // 19. X3 ← t0 + t0 => (2·X1·X2)
|
||||
t0 = addmod(X3, t0, P); // 20. t0 ← X3 + t0 => (3·X1·X2)
|
||||
t2 = mulmod(21, t2, P); // 21. t2 ← B3 · t2 => 3b · Z1·Z2
|
||||
Z3 = addmod(t1, t2, P); // 22. Z3 ← t1 + t2 => Y1·Y2 + 3·b·Z1·Z2
|
||||
t1 = addmod(t1, P - t2, P); // 23. t1 ← t1 - t2 => Y1·Y2 - 3·b·Z1·Z2
|
||||
Y3 = mulmod(21, Y3, P); // 24. Y3 ← B3 · Y3 => 3b · ((X1+Z1)(X2+Z2) - X1·X2 - Z1·Z2)
|
||||
X3 = mulmod(t4, Y3, P); // 25. X3 ← t4 · Y3 => 3b·((Y1+Z1)(Y2+Z2)-Y1·Y2-Z1·Z2) · ((X1+Z1)(X2+Z2)-X1·X2-Z1·Z2)
|
||||
t2 = mulmod(t3, t1, P); // 26. t2 ← t3 · t1 => ((X1+Y1)(X2+Y2)-X1·X2-Y1·Y2) · (Y1·Y2 - 3·b·Z1·Z2)
|
||||
X3 = addmod(t2, P - X3, P); // 27. X3 ← t2 - X3 => (X1Y2+X2Y1)(Y1·Y2-3·b·Z1·Z2) - 3b(Y1·Z2+Y2·Z1)(X1·Z2+X2·Z1)
|
||||
Y3 = mulmod(Y3, t0, P); // 28. Y3 ← Y3 · t0 => 3·b·((X1+Z1)(X2+Z2) - X1·X2 - Z1·Z2) · 3·X1·X2 = 9·b·X1·X2·(X1·Z2+X2·Z1)
|
||||
t1 = mulmod(t1, Z3, P); // 29. t1 ← t1 · Z3 => (Y1·Y2-3·b·Z1·Z2) · (Y1·Y2+3·b·Z1·Z2)
|
||||
Y3 = addmod(t1, Y3, P); // 30. Y3 ← t1 + Y3 => (Y1Y2+3·b·Z1·Z2)(Y1·Y2-3·b·Z1·Z2) + 9b·X1·X2·(X1·Z2+X2·Z1)
|
||||
t0 = mulmod(t0, t3, P); // 31. t0 ← t0 · t3 => (3·X1·X2) · ((X1+Y1)(X2+Y2)-X1·X2-Y1·Y2)
|
||||
Z3 = mulmod(Z3, t4, P); // 32. Z3 ← Z3 · t4 => (Y1·Y2+3·b·Z1·Z2) · ((Y1+Z1)(Y2+Z2)-Y1·Y2-Z1·Z2)
|
||||
Z3 = addmod(Z3, t0, P); // 33. Z3 ← Z3 + t0 => (Y1·Z2+Y2·Z1)·(Y1·Y2+3·b·Z1·Z2) + 3·X1·X2·(X1·Y2+X2·Y1)
|
||||
}
|
||||
|
||||
function isOnCurve(uint256 x, uint256 y) internal pure returns (bool) {
|
||||
uint lhs = mulmod(y, y, P); // y^2
|
||||
uint rhs = mulmod(mulmod(x, x, P), x, P); // x^3
|
||||
rhs = addmod(rhs, B, P); // x^3 + 7
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
function toAffine(
|
||||
uint256 x,
|
||||
uint256 y,
|
||||
uint256 z
|
||||
) internal pure returns (uint256, uint256) {
|
||||
uint256 t0 = modInverse(z);
|
||||
x = mulmod(x, t0, P);
|
||||
y = mulmod(y, t0, P);
|
||||
return (x, y);
|
||||
}
|
||||
|
||||
function modInverse(uint256 r1) internal pure returns (uint256 t0) {
|
||||
// Extended Euclidean algorithm (iterative, assembly)
|
||||
// Typically lower average gas than the modexp precompile for single inversions,
|
||||
// but execution time (and gas) depends on input values — not constant-time.
|
||||
assembly {
|
||||
let t1 := 1
|
||||
let r0 := P
|
||||
|
||||
for {} r1 {} {
|
||||
let q := div(r0, r1)
|
||||
|
||||
// t0, t1 = t1, t0 - q * t1
|
||||
let t1_new := sub(t0, mul(q, t1))
|
||||
t0 := t1
|
||||
t1 := t1_new
|
||||
|
||||
// r0, r1 = r1, r0 - q * r1
|
||||
let r1_new := sub(r0, mul(q, r1))
|
||||
r0 := r1
|
||||
r1 := r1_new
|
||||
}
|
||||
|
||||
if slt(t0, 0) {
|
||||
t0 := add(t0, P)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
209
test/GhostVerifier.t.sol
Normal file
209
test/GhostVerifier.t.sol
Normal file
File diff suppressed because one or more lines are too long
@ -22,10 +22,44 @@ contract MathTesterTest is Test {
|
||||
points = abi.decode(data, (Point[]));
|
||||
}
|
||||
|
||||
function test_single() public view {
|
||||
uint256 len = points.length - 1;
|
||||
for (uint256 i; i < len;) {
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.mulSingleProjective(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k)
|
||||
);
|
||||
(x_p, y_p) = math.toAffineProjective(x_p, y_p, z_p);
|
||||
|
||||
(uint256 x_g, uint256 y_g, uint256 z_g) = math.mulSingleGhost(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k)
|
||||
);
|
||||
(x_g, y_g) = math.toAffineGhost(x_g, y_g, z_g);
|
||||
|
||||
(uint256 x_j, uint256 y_j) = math.mulSingleEc(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k)
|
||||
);
|
||||
|
||||
assertEq(x_p, x_j);
|
||||
assertEq(x_p, x_g);
|
||||
assertEq(y_p, y_j);
|
||||
assertEq(y_p, y_g);
|
||||
assertEq(math.isOnCurve(x_p, y_p), true);
|
||||
assertEq(math.isOnCurveGhost(x_p, y_p), true);
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
}
|
||||
|
||||
function test_quartet() public view {
|
||||
uint256 len = points.length - 3;
|
||||
for (uint256 i; i < len;) {
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.mulProjectiveQuartet(
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.mulQuartetProjective(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k),
|
||||
@ -41,7 +75,7 @@ contract MathTesterTest is Test {
|
||||
);
|
||||
(x_p, y_p) = math.toAffineProjective(x_p, y_p, z_p);
|
||||
|
||||
(uint256 x_j, uint256 y_j) = math.mulEcQuartet(
|
||||
(uint256 x_j, uint256 y_j) = math.mulQuartetEc(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k),
|
||||
@ -59,6 +93,7 @@ contract MathTesterTest is Test {
|
||||
assertEq(x_p, x_j);
|
||||
assertEq(y_p, y_j);
|
||||
assertEq(math.isOnCurve(x_p, y_p), true);
|
||||
assertEq(math.isOnCurveGhost(x_p, y_p), true);
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
@ -67,7 +102,7 @@ contract MathTesterTest is Test {
|
||||
function test_pair() public view {
|
||||
uint256 len = points.length - 1;
|
||||
for (uint256 i; i < len;) {
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.mulProjectivePair(
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.mulPairProjective(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k),
|
||||
@ -77,7 +112,17 @@ contract MathTesterTest is Test {
|
||||
);
|
||||
(x_p, y_p) = math.toAffineProjective(x_p, y_p, z_p);
|
||||
|
||||
(uint256 x_j, uint256 y_j) = math.mulEcPair(
|
||||
(uint256 x_g, uint256 y_g, uint256 z_g) = math.mulPairGhost(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k),
|
||||
uint256(points[i+1].x),
|
||||
uint256(points[i+1].y),
|
||||
uint256(points[i+1].k)
|
||||
);
|
||||
(x_g, y_g) = math.toAffineGhost(x_g, y_g, z_g);
|
||||
|
||||
(uint256 x_j, uint256 y_j) = math.mulPairEc(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k),
|
||||
@ -87,8 +132,11 @@ contract MathTesterTest is Test {
|
||||
);
|
||||
|
||||
assertEq(x_p, x_j);
|
||||
assertEq(x_p, x_g);
|
||||
assertEq(y_p, y_j);
|
||||
assertEq(y_p, y_g);
|
||||
assertEq(math.isOnCurve(x_p, y_p), true);
|
||||
assertEq(math.isOnCurveGhost(x_p, y_p), true);
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
@ -97,7 +145,7 @@ contract MathTesterTest is Test {
|
||||
function test_triplet() public view {
|
||||
uint256 len = points.length - 2;
|
||||
for (uint256 i; i < len;) {
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.mulProjectiveTriplet(
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.mulTripletProjective(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k),
|
||||
@ -110,7 +158,7 @@ contract MathTesterTest is Test {
|
||||
);
|
||||
(x_p, y_p) = math.toAffineProjective(x_p, y_p, z_p);
|
||||
|
||||
(uint256 x_j, uint256 y_j) = math.mulEcTriplet(
|
||||
(uint256 x_j, uint256 y_j) = math.mulTripletEc(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i].k),
|
||||
@ -125,6 +173,7 @@ contract MathTesterTest is Test {
|
||||
assertEq(x_p, x_j);
|
||||
assertEq(y_p, y_j);
|
||||
assertEq(math.isOnCurve(x_p, y_p), true);
|
||||
assertEq(math.isOnCurveGhost(x_p, y_p), true);
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
@ -141,6 +190,14 @@ contract MathTesterTest is Test {
|
||||
);
|
||||
(x_p, y_p) = math.toAffineProjective(x_p, y_p, z_p);
|
||||
|
||||
(uint256 x_g, uint256 y_g, uint256 z_g) = math.addGhost(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i+1].x),
|
||||
uint256(points[i+1].y)
|
||||
);
|
||||
(x_g, y_g) = math.toAffineGhost(x_g, y_g, z_g);
|
||||
|
||||
(uint256 x_j, uint256 y_j, uint256 z_j) = math.addJacobian(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
@ -150,8 +207,11 @@ contract MathTesterTest is Test {
|
||||
(x_j, y_j) = math.toAffineJacobian(x_j, y_j, z_j);
|
||||
|
||||
assertEq(x_p, x_j);
|
||||
assertEq(x_p, x_g);
|
||||
assertEq(y_p, y_j);
|
||||
assertEq(y_p, y_g);
|
||||
assertEq(math.isOnCurve(x_p, y_p), true);
|
||||
assertEq(math.isOnCurveGhost(x_p, y_p), true);
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
@ -160,7 +220,7 @@ contract MathTesterTest is Test {
|
||||
function test_mixedAddition() public view {
|
||||
uint256 len = points.length - 1;
|
||||
for (uint256 i; i < len;) {
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.addProjectiveMixed(
|
||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.addMixedProjective(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i+1].x),
|
||||
@ -168,6 +228,14 @@ contract MathTesterTest is Test {
|
||||
);
|
||||
(x_p, y_p) = math.toAffineProjective(x_p, y_p, z_p);
|
||||
|
||||
(uint256 x_g, uint256 y_g, uint256 z_g) = math.addMixedGhost(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
uint256(points[i+1].x),
|
||||
uint256(points[i+1].y)
|
||||
);
|
||||
(x_g, y_g) = math.toAffineGhost(x_g, y_g, z_g);
|
||||
|
||||
(uint256 x_j, uint256 y_j, uint256 z_j) = math.addJacobian(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y),
|
||||
@ -179,6 +247,7 @@ contract MathTesterTest is Test {
|
||||
assertEq(x_p, x_j);
|
||||
assertEq(y_p, y_j);
|
||||
assertEq(math.isOnCurve(x_p, y_p), true);
|
||||
assertEq(math.isOnCurveGhost(x_p, y_p), true);
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
@ -193,6 +262,12 @@ contract MathTesterTest is Test {
|
||||
);
|
||||
(x_p, y_p) = math.toAffineProjective(x_p, y_p, z_p);
|
||||
|
||||
(uint256 x_g, uint256 y_g, uint256 z_g) = math.doubleGhost(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y)
|
||||
);
|
||||
(x_g, y_g) = math.toAffineProjective(x_g, y_g, z_g);
|
||||
|
||||
(uint256 x_j, uint256 y_j, uint256 z_j) = math.doubleJacobian(
|
||||
uint256(points[i].x),
|
||||
uint256(points[i].y)
|
||||
@ -200,8 +275,11 @@ contract MathTesterTest is Test {
|
||||
(x_j, y_j) = math.toAffineJacobian(x_j, y_j, z_j);
|
||||
|
||||
assertEq(x_p, x_j);
|
||||
assertEq(x_p, x_g);
|
||||
assertEq(y_p, y_j);
|
||||
assertEq(y_p, y_g);
|
||||
assertEq(math.isOnCurve(x_p, y_p), true);
|
||||
assertEq(math.isOnCurveGhost(x_p, y_p), true);
|
||||
|
||||
unchecked { ++i; }
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user