simultaneous multiplication for triplets based on Straus-Shamir Trick
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
2b3eb14012
commit
901e969877
54
README.md
54
README.md
@ -32,30 +32,36 @@ For full background and protocol details see the [project wiki](https://git.ghos
|
|||||||
|
|
||||||
`Jacobian` is the original implementation used as a reference implementation, while `Projective` is optimized one.
|
`Jacobian` is the original implementation used as a reference implementation, while `Projective` is optimized one.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
╭----------------------------------------+-----------------+-------+--------+-------+---------╮
|
╭----------------------------------------+-----------------+---------+---------+---------+---------╮
|
||||||
| src/MathTester.sol:MathTester Contract | | | | | |
|
| src/MathTester.sol:MathTester Contract | | | | | |
|
||||||
+=============================================================================================+
|
+==================================================================================================+
|
||||||
| Deployment Cost | Deployment Size | | | | |
|
| Deployment Cost | Deployment Size | | | | |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| 1065323 | 4715 | | | | |
|
| 2011993 | 9094 | | | | |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| | | | | | |
|
| | | | | | |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| Function Name | Min | Avg | Median | Max | # Calls |
|
| Function Name | Min | Avg | Median | Max | # Calls |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| addJacobian | 1768 | 1768 | 1768 | 1768 | 44 |
|
| addJacobian | 1800 | 1800 | 1800 | 1800 | 88 |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| addProjective | 992 | 992 | 992 | 992 | 44 |
|
| addProjective | 1015 | 1015 | 1015 | 1015 | 44 |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| doubleJacobian | 777 | 777 | 777 | 777 | 45 |
|
| addProjectiveMixed | 967 | 967 | 967 | 967 | 44 |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| doubleProjective | 532 | 532 | 532 | 532 | 45 |
|
| doubleJacobian | 794 | 794 | 794 | 794 | 45 |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| toAffineJacobian | 30564 | 36206 | 36300 | 40841 | 89 |
|
| doubleProjective | 546 | 546 | 546 | 546 | 45 |
|
||||||
|----------------------------------------+-----------------+-------+--------+-------+---------|
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
| toAffineProjective | 11838 | 13792 | 13796 | 15155 | 89 |
|
| mulEcTriplet | 205548 | 1140310 | 1546125 | 1958623 | 43 |
|
||||||
╰----------------------------------------+-----------------+-------+--------+-------+---------╯
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
|
| mulProjectiveTriplet | 6436 | 193298 | 334976 | 385511 | 43 |
|
||||||
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
|
| toAffineJacobian | 40275 | 47740 | 47859 | 53863 | 133 |
|
||||||
|
|----------------------------------------+-----------------+---------+---------+---------+---------|
|
||||||
|
| toAffineProjective | 11056 | 13787 | 13904 | 16105 | 176 |
|
||||||
|
╰----------------------------------------+-----------------+---------+---------+---------+---------╯
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|||||||
@ -46,6 +46,41 @@ contract MathTester {
|
|||||||
return EllipticCurveProjective.projectiveAddMixed(x1, y1, 1, x2, y2);
|
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
|
||||||
|
) public pure returns(uint256, uint256) {
|
||||||
|
(x1, y1) = EllipticCurve.ecMul(k1, x1, y1, A, P);
|
||||||
|
(x2, y2) = EllipticCurve.ecMul(k2, x2, y2, A, P);
|
||||||
|
(x3, y3) = EllipticCurve.ecMul(k3, x3, y3, A, P);
|
||||||
|
|
||||||
|
(x1, y1) = EllipticCurve.ecAdd(x1, y1, x2, y2, A, P);
|
||||||
|
(x1, y1) = EllipticCurve.ecAdd(x1, y1, x3, y3, A, P);
|
||||||
|
|
||||||
|
return (x1, y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mulProjectiveTriplet(
|
||||||
|
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 toAffineJacobian(uint256 x, uint256 y, uint256 z) public pure returns (uint256, uint256) {
|
function toAffineJacobian(uint256 x, uint256 y, uint256 z) public pure returns (uint256, uint256) {
|
||||||
return EllipticCurve.toAffine(x, y, z, P);
|
return EllipticCurve.toAffine(x, y, z, P);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
// TODO: are there better algorithms than Straus-Shamir trick for
|
||||||
|
// k1*P1 + k2*P2 + k3*P3
|
||||||
|
|
||||||
// Vectors for secp256k1 are difficult to find. These are the vectors from:
|
// 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
|
// https://web.archive.org/web/20190724010836/https://chuckbatson.wordpress.com/2014/11/26/secp256k1-test-vectors
|
||||||
|
|
||||||
@ -14,6 +17,132 @@ library EllipticCurveProjective {
|
|||||||
|
|
||||||
uint256 private constant B3 = 21;
|
uint256 private constant B3 = 21;
|
||||||
|
|
||||||
|
function findMaxBitLength(
|
||||||
|
uint256 k1,
|
||||||
|
uint256 k2,
|
||||||
|
uint256 k3
|
||||||
|
) internal pure returns (uint256 bits) {
|
||||||
|
assembly {
|
||||||
|
// Find maximum of the three scalars
|
||||||
|
let max := k1
|
||||||
|
if gt(k2, max) { max := k2 }
|
||||||
|
if gt(k3, max) { max := k3 }
|
||||||
|
|
||||||
|
// 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 mulAddProjectiveTriplet(
|
||||||
|
uint256 x1, uint256 y1, uint256 k1,
|
||||||
|
uint256 x2, uint256 y2, uint256 k2,
|
||||||
|
uint256 x3, uint256 y3, uint256 k3
|
||||||
|
) internal pure returns (uint256 x4, uint256 y4, uint256 z4) {
|
||||||
|
// 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, k3);
|
||||||
|
|
||||||
|
uint256[8] memory precomputedXs;
|
||||||
|
uint256[8] memory precomputedYs;
|
||||||
|
uint256[8] memory precomputedZs;
|
||||||
|
|
||||||
|
precomputedXs[1] = x3; precomputedYs[1] = y3; precomputedZs[1] = 1; // 001: P3
|
||||||
|
precomputedXs[2] = x2; precomputedYs[2] = y2; precomputedZs[2] = 1; // 010: P2
|
||||||
|
precomputedXs[4] = x1; precomputedYs[4] = y1; precomputedZs[4] = 1; // 100: P1
|
||||||
|
|
||||||
|
(precomputedXs[6], precomputedYs[6], precomputedZs[6]) = projectiveAddMixed(x1, y1, 1, x2, y2); // 110: P1+P2
|
||||||
|
(precomputedXs[5], precomputedYs[5], precomputedZs[5]) = projectiveAddMixed(x1, y1, 1, x3, y3); // 101: P1+P3
|
||||||
|
|
||||||
|
(x4, y4, z4) = projectiveAddMixed(x2, y2, 1, x3, y3); // 011: P2+P3
|
||||||
|
precomputedXs[3] = x4;
|
||||||
|
precomputedYs[3] = y4;
|
||||||
|
precomputedZs[3] = z4;
|
||||||
|
|
||||||
|
(x4, y4, z4) = projectiveAddMixed(x4, y4, z4, x1, y1); // 111: P1+P2+P3
|
||||||
|
precomputedXs[7] = x4;
|
||||||
|
precomputedYs[7] = y4;
|
||||||
|
precomputedZs[7] = z4;
|
||||||
|
|
||||||
|
x4 = 0;
|
||||||
|
y4 = 1;
|
||||||
|
z4 = 0;
|
||||||
|
|
||||||
|
for (; bits > 0;) {
|
||||||
|
unchecked { --bits; }
|
||||||
|
|
||||||
|
(x4, y4, z4) = projectiveDouble(x4, y4, z4);
|
||||||
|
|
||||||
|
uint8 mask;
|
||||||
|
uint8 bitmask;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
mask := or(
|
||||||
|
shl(2, and(shr(bits, k1), 1)),
|
||||||
|
or(
|
||||||
|
shl(1, and(shr(bits, k2), 1)),
|
||||||
|
and(shr(bits, k3), 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// bitmask for 1,2,4 is 22 = 0b00010110
|
||||||
|
bitmask := and(shl(22, mask), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitmask == 1) {
|
||||||
|
(x4, y4, z4) = projectiveAddMixed(
|
||||||
|
x4, y4, z4, precomputedXs[mask], precomputedYs[mask]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
(x4, y4, z4) = projectiveAdd(
|
||||||
|
x4, y4, z4, precomputedXs[mask], precomputedYs[mask], precomputedZs[mask]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function projectiveAddMixed(
|
function projectiveAddMixed(
|
||||||
uint256 X1,
|
uint256 X1,
|
||||||
uint256 Y1,
|
uint256 Y1,
|
||||||
|
|||||||
@ -22,7 +22,43 @@ contract MathTesterTest is Test {
|
|||||||
points = abi.decode(data, (Point[]));
|
points = abi.decode(data, (Point[]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_projectiveAddition() public view {
|
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(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),
|
||||||
|
uint256(points[i+2].x),
|
||||||
|
uint256(points[i+2].y),
|
||||||
|
uint256(points[i+2].k)
|
||||||
|
);
|
||||||
|
(x_p, y_p) = math.toAffineProjective(x_p, y_p, z_p);
|
||||||
|
|
||||||
|
(uint256 x_j, uint256 y_j) = math.mulEcTriplet(
|
||||||
|
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),
|
||||||
|
uint256(points[i+2].x),
|
||||||
|
uint256(points[i+2].y),
|
||||||
|
uint256(points[i+2].k)
|
||||||
|
);
|
||||||
|
// (x_j, y_j) = math.toAffineJacobian(x_j, y_j, z_j);
|
||||||
|
|
||||||
|
assertEq(x_p, x_j);
|
||||||
|
assertEq(y_p, y_j);
|
||||||
|
|
||||||
|
unchecked { ++i; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_addition() public view {
|
||||||
uint256 len = points.length - 1;
|
uint256 len = points.length - 1;
|
||||||
for (uint256 i; i < len;) {
|
for (uint256 i; i < len;) {
|
||||||
(uint256 x_p, uint256 y_p, uint256 z_p) = math.addProjective(
|
(uint256 x_p, uint256 y_p, uint256 z_p) = math.addProjective(
|
||||||
@ -48,7 +84,7 @@ contract MathTesterTest is Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_projectiveAdditionMixed() public view {
|
function test_mixedAddition() public view {
|
||||||
uint256 len = points.length - 1;
|
uint256 len = points.length - 1;
|
||||||
for (uint256 i; i < len;) {
|
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.addProjectiveMixed(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user