diff --git a/contracts/algebra/bls12381.sol b/contracts/algebra/bls12381.sol new file mode 100644 index 0000000..2441297 --- /dev/null +++ b/contracts/algebra/bls12381.sol @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//---------------------------------------------------------------------------// +pragma solidity >=0.8.4; + +import { uint512 } from "./uint512.sol"; + +/** + * @title BLS12-381 elliptic curve crypto + * @dev Provides some basic methods to compute bilinear pairings, construct group elements and misc numerical methods + */ +library bls12_381_crypto { + + /* @notice Jacobian coordinates (x/z^2, y/z^3) + */ + struct g1_point { + uint512.uint512_t x; + uint512.uint512_t y; + uint512.uint512_t z; + } + + struct g2_point { + uint512.uint512_t x0; + uint512.uint512_t x1; + uint512.uint512_t y0; + uint512.uint512_t y1; + } + + // Field prime, 381 bits long + uint256 constant ph_mod = 0x000000000000000000000000000000001A0111EA397FE69A4B1BA7B6434BACD7; + uint256 constant pl_mod = 0x64774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAB; + + function g1_to_affine(g1_point memory point, uint512.uint512_t memory p) internal pure returns (g1_point memory r) + { + /* + uint512.uint512_t memory z = uint512.mulmodp(point.z, point.z, p); + r.x = uint512.divmodp(point.x, z, p); + z = uint512.mulmodp(z, point.z, p); + r.y = uint512.divmodp(point.y, z, p); + */ + } + + /** @notice Checks whether point (x,y) is on curve. xy is in compressed form, little-endian + * array should contain only x coordinate (with flags). If point is on curve, + * the y coordinate is recovered */ + function g1_is_on_curve(uint256[4] memory xy) internal pure returns (bool result) + { + + } + + + /** @notice adds two points on G1 + * https://hyperelliptic.org/EFD/g1p/data/shortw/jacobian-0/addition/add-1998-cmo + */ + function g1_add(uint256[6] memory a, uint256[6] memory b, uint256[6] memory r) internal pure + { + /* layout for temporary vars: + v[ 0], v[ 1] = U1 + v[ 2], v[ 3] = U2 + v[ 4], v[ 5] = S1 + v[ 6], v[ 7] = S2 + v[ 8], v[ 9] = H + v[10], v[11] = r + */ + uint256[12] memory v; + + /* there is a room for optimization - store intermediate Zi^2 in H, r? */ + + /* U1 = X1*Z2^2 */ + (v[ 0], v[ 1]) = field3.mulmod_p381(a[ 0], a[ 1], b[ 4], b[ 5]); + (v[ 0], v[ 1]) = field3.mulmod_p381(v[ 0], v[ 1], b[ 4], b[ 5]); + /* U2 = X2*Z1^2 */ + (v[ 2], v[ 3]) = field3.mulmod_p381(b[ 0], b[ 0], a[ 4], a[ 5]); + (v[ 2], v[ 3]) = field3.mulmod_p381(v[ 2], v[ 3], a[ 4], a[ 5]); + /* S1 = Y1*Z2^3 */ + (v[ 4], v[ 5]) = field3.mulmod_p381(a[ 2], a[ 3], b[ 4], b[ 5]); + (v[ 4], v[ 5]) = field3.mulmod_p381(v[ 4], v[ 5], b[ 4], b[ 5]); + (v[ 4], v[ 5]) = field3.mulmod_p381(v[ 4], v[ 5], b[ 4], b[ 5]); + /* S2 = Y2*Z1^3 */ + (v[ 6], v[ 7]) = field3.mulmod_p381(b[ 2], b[ 3], a[ 4], a[ 5]); + (v[ 6], v[ 7]) = field3.mulmod_p381(v[ 6], v[ 7], a[ 4], a[ 5]); + (v[ 6], v[ 7]) = field3.mulmod_p381(v[ 6], v[ 7], a[ 4], a[ 5]); + /* H = U2 - U1 */ + (v[ 8], v[ 9]) = field3.submod_p381(v[ 2], v[ 3], v[ 0], v[ 1]); + /* r = S2 - S1 */ + (v[10], v[11]) = field3.submod_p381(v[ 6], v[ 7], v[ 4], v[ 5]); + + /* reuse U2 as H^2 */ + (v[ 2], v[ 3]) = field3.mulmod_p381(v[ 8], v[ 9], v[ 8], v[ 9]); + /* reuse U1 as U1*H^2 */ + (v[ 0], v[ 1]) = field3.mulmod_p381(v[ 0], v[ 1], v[ 2], v[ 3]); + /* reuse S2 as H^3 */ + (v[ 6], v[ 7]) = field3.mulmod_p381(v[ 2], v[ 3], v[ 8], v[ 9]); + /* reuse S1 as S1*H^3 */ + (v[ 4], v[ 5]) = field3.mulmod_p381(v[ 4], v[ 5], v[ 6], v[ 7]); + + /* X3 = r^2-H^3-2*U1*H^2 */ + (r[ 0], r[ 1]) = field3.mulmod_p381(v[10], v[11], v[10], v[11]); + (r[ 0], r[ 1]) = field3.submod_p381(r[ 0], r[ 1], v[ 6], v[ 7]); + (r[ 0], r[ 1]) = field3.submod_p381(r[ 0], r[ 1], v[ 0], v[ 1]); + (r[ 0], r[ 1]) = field3.submod_p381(r[ 0], r[ 1], v[ 0], v[ 1]); + + /* Y3 = r (U1 H^2-X3)-S1 H^3 */ + (r[ 2], r[ 3]) = field3.submod_p381(v[ 0], v[ 1], r[ 0], r[ 1]); + (r[ 2], r[ 3]) = field3.mulmod_p381(r[ 2], r[ 3], v[10], v[11]); + (r[ 2], r[ 3]) = field3.submod_p381(r[ 2], r[ 3], v[ 4], v[ 5]); + + /* Z3 = Z1*Z2*H */ + (r[ 4], p[ 5]) = field3.mulmod_p381(a[ 4], a[ 5], b[ 4], b[ 5]); + (r[ 4], p[ 5]) = field3.mulmod_p381(r[ 4], r[ 5], v[ 8], v[ 9]); + } + + /** @notice doubles a point on G1 + * https://hyperelliptic.org/EFD/g1p/data/shortw/jacobian-0/doubling/dbl-2009-l + */ + function g1_dbl(uint256[6] memory a, uint256[6] memory r) internal pure + { + /* layout for temporary vars: + v[ 0], v[ 1] = A + v[ 2], v[ 3] = B + v[ 4], v[ 5] = C + v[ 6], v[ 7] = D + v[ 8], v[ 9] = E + v[10], v[11] = F + */ + uint256[12] memory v; + + /* A = X1^2 */ + (v[ 0], v[ 1]) = field3.mulmod_p381(a[ 0], a[ 1], a[ 0], a[ 1]); + /* B = Y1^2 */ + (v[ 2], v[ 3]) = field3.mulmod_p381(a[ 2], a[ 3], a[ 2], a[ 3]); + /* C = B^2 */ + (v[ 4], v[ 5]) = field3.mulmod_p381(v[ 2], v[ 3], v[ 2], v[ 3]); + + /* D = 2 ((X1 + B)^2 - A - C) */ + (v[ 6], v[ 7]) = field3.addmod_p381(a[ 0], a[ 1], v[ 2], v[ 3]); /* X1+B */ + (v[ 6], v[ 7]) = field3.mulmod_p381(v[ 6], v[ 7], v[ 6], v[ 7]); /* ^2 */ + (v[ 6], v[ 7]) = field3.submod_p381(v[ 6], v[ 7], v[ 0], v[ 1]); /* -A */ + (v[ 6], v[ 7]) = field3.submod_p381(v[ 6], v[ 7], v[ 4], v[ 5]); /* -C */ + (v[ 6], v[ 7]) = field3.addmod_p381(v[ 6], v[ 7], v[ 6], v[ 7]); /* *2 */ + + /* reuse C as 8*C */ + (v[ 4], v[ 5]) = field3.addmod_p381(v[ 4], v[ 5], v[ 4], v[ 5]); /* 2*C */ + (v[ 4], v[ 5]) = field3.addmod_p381(v[ 4], v[ 5], v[ 4], v[ 5]); /* 4*C */ + (v[ 4], v[ 5]) = field3.addmod_p381(v[ 4], v[ 5], v[ 4], v[ 5]); /* 8*C */ + + /* E = 3*A */ + (v[ 8], v[ 9]) = field3.addmod_p381(v[ 0], v[ 1], v[ 0], v[ 1]); + (v[ 8], v[ 9]) = field3.addmod_p381(v[ 8], v[ 9], v[ 0], v[ 1]); + + /* F = E^2 */ + (v[10], v[11]) = field3.mulmod_p381(v[ 8], v[ 9], v[ 8], v[ 9]); + + /* X3 = F-2*D */ + (r[ 0], r[ 1]) = field3.submod_p381(v[10], v[11], v[ 6], v[ 7]); + (r[ 0], r[ 1]) = field3.submod_p381(r[ 0], r[ 1], v[ 6], v[ 7]); + + /* Y3 = E*(D - X3) - 8*C */ + (r[ 2], r[ 3]) = field3.submod_p381(v[ 6], v[ 7], a[ 0], a[ 1]); + (r[ 2], r[ 3]) = field3.mulmod_p381(r[ 2], r[ 3], v[ 8], v[ 9]); + (r[ 2], r[ 3]) = field3.submod_p381(r[ 2], r[ 3], v[ 4], v[ 5]); + + /* Z3 = 2*Y1*Z1 */ + (r[ 4], r[ 5]) = field3.mulmod_p381(a[ 2], a[ 3], a[ 4], a[ 5]); + (r[ 4], r[ 5]) = field3.addmod_p381(r[ 4], r[ 5], r[ 4], r[ 5]); + + } + + + +} diff --git a/contracts/algebra/field3.sol b/contracts/algebra/field3.sol new file mode 100644 index 0000000..4dd29f2 --- /dev/null +++ b/contracts/algebra/field3.sol @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//---------------------------------------------------------------------------// +pragma solidity ^0.8.0; + +/** Library for three-limbs arithmetic + * References: HAC - Handbook of applied cryptography, specifically 14th chapter - + * Efficient implementations + */ +import 'hardhat/console.sol'; +library field3 { + + uint256 constant not0 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + /** @notice empty function, for testing purposes and measurements */ + function empty(uint256 al, uint256 am, uint256 ah, + uint256 bl, uint256 bm, uint256 bh) internal pure returns + (uint256 rl, uint256 rm, uint256 rh) + { + return (al,am,ah); + } + + /** @notice r = a + b */ + function add(uint256 al, uint256 am, uint256 ah, + uint256 bl, uint256 bm, uint256 bh) internal pure returns + (uint256 rl, uint256 rm, uint256 rh) + { + assembly { + rl := add(al, bl) + rm := add(am, bm) + rh := add(ah, bh) + // carry from low to mid + rm := add(rm, lt(rl, al)) + // carry from mid to hi + rh := add(rh, or(lt(rm, am), lt(rm,bm))) + } + } + + /** @notice r = a - b */ + function sub(uint256 al, uint256 am, uint256 ah, + uint256 bl, uint256 bm, uint256 bh) internal pure returns + (uint256 rl, uint256 rm, uint256 rh) + { + assembly { + rh := sub(ah, bh) + rm := sub(am, bm) + rl := sub(al, bl) + // borrow from mid to low + rm := sub(rm, lt(al, bl)) + // borrow from hi to mid + rh := sub(rh, or(lt(am, bm), gt(rm, am))) + } + } + + /** @notice Less-than */ + function lt(uint256 al, uint256 am, uint256 ah, + uint256 bl, uint256 bm, uint256 bh) internal pure returns (bool r) + { + if (ah > bh) return false; + if (ah < bh) return true; + if (am > bm) return false; + if (am < bm) return true; + return (al < bl); + } + + /** @notice Less-than, but b has two limbs */ + function lt(uint256 al, uint256 am, uint256 ah, + uint256 bl, uint256 bm) internal pure returns (bool r) + { + if (ah > 0) return false; + if (am > bm) return false; + if (am < bm) return true; + return (al < bl); + } + + + /** @notice Compare, -1 - a less than b, 0 - a == b , 1 - a greater than b */ + function cmp(uint256 al, uint256 am, uint256 ah, + uint256 bl, uint256 bm, uint256 bh) internal pure returns (int r) + { + if (ah > bh) return 1; + if (ah < bh) return -1; + if (am > bm) return 1; + if (am < bm) return -1; + if (al > bl) return 1; + if (al < bl) return -1; + return 0; + } + + /** @notice Same as compare, but b has two limbs */ + function cmp(uint256 al, uint256 am, uint256 ah, + uint256 bl, uint256 bm) internal pure returns (int r) + { + if (ah > 0) return 1; + if (am > bm) return 1; + if (am < bm) return -1; + if (al > bl) return 1; + if (al < bl) return -1; + return 0; + } + + /** @notice Same as compare, but b has two high limbs */ + function cmph(uint256 al, uint256 am, uint256 ah, + uint256 bm, uint256 bh) internal pure returns (int r) + { + if (ah > bh) return 1; + if (ah < bh) return -1; + if (am > bm) return 1; + if (am < bm) return -1; + if (al > 0) return 1; + return 0; + } + + + /** @notice a == 1 ? */ + function is_one(uint256 al, uint256 am, uint256 ah) internal pure returns (bool r) + { + return (al == 1) && (am == 0) && (ah == 0); + } + + /** @notice a != 0 */ + function nz(uint256 al, uint256 am, uint256 ah) internal pure returns (bool r) + { + return (al != 0) || (am != 0) || (ah != 0); + } + + /** @notice Multiply words, result is two words */ + function mul256x256(uint256 a, uint256 b) internal pure returns (uint256 l, uint256 h) + { + assembly { + let mm := mulmod(a, b, not(0)) + l := mul(a, b) + h := sub(sub(mm, l), lt(mm, l)) + } + } + + /** @notice Multiply 3x3 limbs. The result is 6 digits, high 3 are discarded, + * because common use case involves multiplication of 384 bits integers */ + function mul3x3(uint256 al, uint256 am, uint256 ah, + uint256 bl, uint256 bm, uint256 bh) internal pure returns + (uint256 rl, uint256 rm, uint256 rh) + { + /* @dev TODO: separate optimized version for 2x2 -> 3 digits? 381x381 bits -> 762 bits*/ + /* @dev TODO: this uses three temporaries, rethink to reuse parameters and return values */ + assembly { + let mm := mulmod(al, bl, not(0)) + rl := mul(al, bl) + rm := sub(sub(mm, rl), lt(mm, rl)) + + mm := mulmod(am, bl, not(0)) + let t1 := mul(am, bl) + rh := sub(sub(mm, t1), lt(mm, t1)) + + let t2 := mul(ah, bl) + rh := add(rh, t2) + rm := add(rm, t1) + rh := add(rh, lt(rm, t1)) + + mm := mulmod(al, bm, not(0)) + t1 := mul(al, bm) + t2 := sub(sub(mm, t1), lt(mm, t1)) + + rm := add(rm, t1) + rh := add(rh, t2) + rh := add(rh, mul(am,bm)) + rh := add(rh, lt(rm, t1)) + rh := add(rh, mul(al,bh)) + } + } + + /** @notice divide three limbs x by two limb y + * @dev see HAC 14.20, n = 2, t = 1, b=2^256 */ + function divrem(uint256 xl, uint256 xm, uint256 xh, + uint256 yl, uint256 ym) internal view returns + (uint256 ql, uint256 qm, uint256 rl, uint256 rm) + { + unchecked { + + uint256 tl; + uint256 tm; + uint256 th; + + require(ym > 0, "division by short y"); + + /* 2. while (x>= y*2^256) do { qm+=1, x -= y*2^256;} */ + + /* This will be at most once for normalized version, + but we want to keep it in three limbs, as normalization for p381 + implies shift left for 131 bits */ + + console.log("need to divide: ", xm, xh); + console.log("by : ", yl, ym); + qm = xh / ym; + console.log("first approach: ", qm); + + (tm, th) = mul256x256(qm, yl); + console.log("qm * xl", tm, th); + if (th>0) { + console.log("adj, over by : ", th); + console.log("adj, sub by : ", th/ym); + qm -= th/ym ; + (tm, th) = mul256x256(qm, yl); + } + + (xl, xm, xh) = sub(xl, xm, xh, 0, tm, th); + + + } /* unchecked */ + } + + /* div256, mod256, add512, sub512, div512 - https://2π.com/17/512-bit-division/ License: MIT + */ + + /** @notice Divide 2^256 by a */ + function div256(uint256 a) internal pure returns (uint256 r) { + require(a > 1); + assembly { + r := add(div(sub(0, a), a), 1) + } + } + + /** @notice 2^256 mod a */ + function mod256(uint256 a) internal pure returns (uint256 r) { + require(a != 0); + assembly { + r := mod(sub(0, a), a) + } + } + + /** @notice add two-word numbers */ + function add512(uint256 a0, uint256 a1, uint256 b0, uint256 b1) + internal pure returns (uint256 r0, uint256 r1) { + assembly { + r0 := add(a0, b0) + r1 := add(add(a1, b1), lt(r0, a0)) + } + } + + /** @notice subtract two-word numbers */ + function sub512(uint256 a0, uint256 a1, uint256 b0, uint256 b1) + internal pure returns (uint256 r0, uint256 r1) { + assembly { + r0 := sub(a0, b0) + r1 := sub(sub(a1, b1), lt(a0, b0)) + } + } + + /** @notice divide two-word a by one-word b */ + function div512(uint256 a0, uint256 a1, uint256 b) internal pure returns (uint256 x0, uint256 x1) + { + uint256 q = div256(b); + uint256 r = mod256(b); + uint256 t0; + uint256 t1; + + while (a1 != 0) { + (t0, t1) = mul256x256(a1, q); + (x0, x1) = add512(x0, x1, t0, t1); + (t0, t1) = mul256x256(a1, r); + (a0, a1) = add512(t0, t1, a0, 0); + } + (x0, x1) = add512(x0, x1, a0 / b, 0); + } + /* https://2π.com/17/512-bit-division/ - Code ends */ + + /** @notice Multiplies (al,ah)*b */ + function mul512x256(uint256 xl, uint256 xh, uint256 y) internal pure returns(uint256 rl, uint256 rm, uint256 rh) + { + /* + (xl,xh) * y + + (xl*y)_L (xl*y)_H + (xh*y)_L (xh*yl)_H + ========== ======== ========== + rl rh (discarded) + */ + + assembly { + let mm := mulmod(xh, y, not(0)) + rm := mul(xh, y) + rh := sub(sub(mm, rm), lt(mm, rm)) + + mm := mulmod(xl, y, not(0)) + rl := mul(xl, y) + let t := sub(sub(mm, rl), lt(mm, rl)) + + rm := add(rm, t) + rh := add(rh, lt(rm, t)) + } + } + + /* normalized p381 = p381 << 131 */ + uint256 constant norm_p_381_h = 0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120; + uint256 constant norm_p_381_l = 0xf55ffff58a9ffffdcff7fffffffd555800000000000000000000000000000000; + + /** @notice multiply (xl,xh) * (yl,yh) mod (p381) */ + function mulmod_p381(uint256 xl, uint256 xh, uint256 yl, uint256 yh) internal pure returns + (uint256 rl, uint256 rh) + { + + unchecked { + + uint256 tl; + uint256 tm; + uint256 th; + + uint256 xyl; + + /* First, compute x*y + + (xl,xh) * (yl,yh) + + (xl*yl)_L (xl*yl)_H + (xl*yh+xh*yl)_L (xl*yh+xh*yl)_H + (xh*yh)_L (no xh*yh_H) + ========== ============== =============== ============ + tl tm th + + */ + + assembly { + let mm := mulmod(xl, yl, not(0)) + tl := mul(xl, yl) + tm := sub(sub(mm, tl), lt(mm, tl)) + + mm := mulmod(xl, yh, not(0)) + xyl := mul(xl, yh) + th := sub(sub(mm, xyl), lt(mm, xyl)) + + tm := add(tm, xyl) + th := add(th, lt(tm, xyl)) + + mm := mulmod(xh, yl, not(0)) + xyl := mul(xh, yl) + th := add(th, sub(sub(mm, xyl), lt(mm, xyl))) + + tm := add(tm, xyl) + th := add(th, lt(tm, xyl)) + + th := add(th, mul(xh,yh)) + } + + /* Second, normalize x*y (shift left by 512-381=131) */ + assembly { + xyl := shr(125,th) + th := or(shl(131,th), shr(125,tm)) + tm := or(shl(131,tm), shr(125,tl)) + tl := shl(131,tl) + } + + /* HAC 14.20 Multiple-precision division + Here we have n = 3, t = 1, b=2^256 */ + + /* 1. q_i = 0 */ + uint256 q2; + uint256 q1; + uint256 q0; + uint256 rtop; + + /* 2. while (x>= y*2^256) do { q2 += 1, x -= y*2^256;} */ + /* This will be at most once for normalized numbers */ + if (xyl >= norm_p_381_h) { + q2 = 1; + (tm, th, xyl) = sub(tm, th, xyl, 0, norm_p_381_l, norm_p_381_h); + } + + /* 3. i = 3 */ + + /* 3.1 */ + if (xyl == norm_p_381_h) { + q1 = not0; + } else { + /* The hardest at EVM */ + (q1, rtop /*high part discarded, should be zero*/) = div512(th, xyl, norm_p_381_h); + } + + /* 3.2 while q*(y_t*b + y_{t-1}) > x... q-= 1, at most two times */ + (rl, rh, rtop) = mul512x256(norm_p_381_l, norm_p_381_h, q1); + if (cmp(rl, rh, rtop, tm, th, xyl) > 0) { + q1--; + (rl, rh, rtop) = sub(rl, rh, rtop, norm_p_381_l, norm_p_381_h, 0); /* room for optimization */ + } + if (cmp(rl, rh, rtop, tm, th, xyl) > 0) { + q1--; + (rl, rh, rtop) = sub(rl, rh, rtop, norm_p_381_l, norm_p_381_h, 0); /* room for optimization */ + } + + /* 3.3 + 3.4 */ + if (lt(tm, th, xyl, rl, rh, rtop)) { + q1--; + (tm, th, xyl) = sub(tm, th, xyl, rl, rh, rtop); + (tm, th, xyl) = add(tm, th, xyl, norm_p_381_l, norm_p_381_h, 0); + + } else { + (tm, th, xyl) = sub(tm, th, xyl, rl, rh, rtop); + } + + /* repeat step 3 for i = 1 */ + /* 3.1 */ + if (th == norm_p_381_h) { + q0 = not0; + } else { + /* The hardest at EVM */ + (q0, rtop/*high part discarded, should be zero*/) = div512(tm, th, norm_p_381_h); + } + + /* 3.2 while q*(y_t*b + y_{t-1}) > x... q-= 1, at most two times */ + (rl, rh, rtop) = mul512x256(norm_p_381_l, norm_p_381_h, q0); + if (cmp(rl, rh, rtop, tl, tm, th) > 0) { + q0--; + (rl, rh, rtop) = sub(rl, rh, rtop, norm_p_381_l, norm_p_381_h, 0); /* room for optimization */ + } + if (cmp(rl, rh, rtop, tl, tm, th) > 0) { + q0--; + (rl, rh, rtop) = sub(rl, rh, rtop, norm_p_381_l, norm_p_381_h, 0); /* room for optimization */ + } + + /* 3.3 + 3.4 */ + if (lt(tl, tm, th, rl, rh, rtop)) { + q0--; + (tl, tm, th) = sub(tl, tm, th, rl, rh, rtop); + (tl, tm, th) = add(tl, tm, th, norm_p_381_l, norm_p_381_h, 0); + + } else { + (tl, tm, th) = sub(tl, tm, th, rl, rh, rtop); + } + + /* renormalization, shr 131 */ + assembly { + rh := shr(131, tm) + rl := or(shl(125, tm), shr(131, tl)) + } + + } /* unchecked */ + } + +} + diff --git a/contracts/algebra/uint512.sol b/contracts/algebra/uint512.sol new file mode 100644 index 0000000..b553671 --- /dev/null +++ b/contracts/algebra/uint512.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//---------------------------------------------------------------------------// +pragma solidity ^0.8.0; + +/** Library for two-limbs arithmetic + * References: HAC - Handbook of applied cryptography, specifically 14th chapter - + * Efficient implementations + */ +import 'hardhat/console.sol'; +library uint512 { + + + + struct uint512_t { + uint256 l; + uint256 h; + } + + + /// @notice addition + function add(uint512_t memory a, uint512_t memory b) internal pure returns (uint512_t memory r) + { + assembly { + let rl := add( mload(a), mload(b) ) + let rh := add( add( mload( add(a, 0x20)), mload(add(b, 0x20))), lt(rl, mload(a) ) ) + mstore( r, rl ) + mstore( add(r,0x20), rh ) + } + } + + /// @notice substraction, r = a - b + function sub(uint512_t memory a, uint512_t memory b) internal pure returns (uint512_t memory r) + { + assembly { + let rl := sub(mload(a), mload(b)) + let rh := sub(sub( mload(add(a,0x20)), mload(add(b,0x20))), lt(mload(a), mload(b))) + mstore( r, rl ) + mstore( add(r,0x20), rh ) + } + } + + /// @notice Product of two uint256 + function mul(uint256 a, uint256 b) internal pure returns (uint512_t memory r) + { + assembly { + let mm := mulmod(a, b, not(0)) + let rl := mul(a, b) + let rh := sub(sub(mm, rl), lt(mm, rl)) + mstore( r, rl ) + mstore( add(r,0x20), rh ) + } + } + + /// @notice Product of uint512_t and uint256, with wrapping + function mul(uint512_t memory a, uint256 b) internal pure returns (uint512_t memory r) + { + assembly { + let al := mload(a) + let ah := mload(add(a,0x20)) + let mm := mulmod(al, b, not(0)) + let rl := mul(al, b) + let rh := sub(sub(mm, rl), lt(mm, rl)) + rh := add(rh, mul(ah, b)) + mstore( r, rl ) + mstore( add(r,0x20), rh ) + } + } + + /// @notice Product of uint512_t and uint512, with wrapping + function mul(uint512_t memory a, uint512_t memory b) internal pure returns (uint512_t memory r) + { + r = mul(a, b.l); + uint512_t memory rh = mul(a, b.h); + rh = uint512_t(0, rh.l); + r = add(r, rh); + } + + function mulmodp(uint512_t memory a, uint512_t memory b) internal pure returns (uint512_t memory r) + { + } + +} + diff --git a/contracts/test/algebra/field3.sol b/contracts/test/algebra/field3.sol new file mode 100644 index 0000000..6d07aa3 --- /dev/null +++ b/contracts/test/algebra/field3.sol @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//---------------------------------------------------------------------------// +pragma solidity ^0.8.0; + +import '../../../contracts/algebra/field3.sol'; +import '../../../contracts/algebra/bls12381.sol'; +import 'hardhat/console.sol'; + +contract test_field3 { + + uint256 constant neg0 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + function test_add() public pure { + uint256[3] memory a = [uint256(1),0,0]; + uint256[3] memory b = [neg0, neg0, neg0]; + (b[0], b[1], b[2]) = field3.add(a[0], a[1], a[2], b[0], b[1], b[2]); + require(b[0] == 0 && b[1] == 0 && b[2] == 0, "-1+1 = 0 failed"); + } + + function test_sub() public pure { + uint256[3] memory a = [uint256(0),0,0]; + uint256[3] memory b = [uint256(1),0,0]; + (b[0], b[1], b[2]) = field3.sub(a[0], a[1], a[2], b[0], b[1], b[2]); + require(b[0] == neg0 && b[1] == neg0 && b[2] == neg0, "0-1 = -1 failed"); + } + + function test_mul() public view + { + uint256[3] memory a = [uint256(1),0,0]; + uint256[3] memory b = [uint256(0x12345678),0,0]; + uint256 gas = gasleft(); + require( + (a[0] == 0x0000000000000000000000000000000000000000000000000000000000000001) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 1'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + console.log(a[0], a[1], a[2]); + require( + (a[0] == 0x0000000000000000000000000000000000000000000000000000000012345678) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 2'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x000000000000000000000000000000000000000000000000014b66dc1df4d840) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 3'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x0000000000000000000000000000000000000000001790fc4eb7762eb86ade00) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 4'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x000000000000000000000000000000000001ad0326899c49d228112b70ac1000) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 5'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x00000000000000000000000000001e81ee41a95d3ac7908c834ba049de078000) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 6'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x00000000000000000000022b5f72f630b2beae25d5e0f29ab3e993bc98840000) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 7'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x00000000000000277e410f0ecb0d79d9e09b9a55fb04a8ca1226de73d5e00000) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 8'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x00000002cef38f3322d63873c74ce00af3e68b71b9356c5ae58461a581000000) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 9'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x33201c61831c7127e6e93e9786ac73b41908f1ff2a0b4177878b92ea78000000) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 10'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x5f9cb17ccdafc00df69bb96f3a0c73a9d6467c9f3114303ec0d8023840000000) && + (a[1] == 0x0000000000000000000000000000000000000000000000000000000003a2b5c9) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 11'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x48e342cd7313c20f65f6f44cd441c0aca0b14899fffa0e3b5decefde00000000) && + (a[1] == 0x00000000000000000000000000000000000000000000000000422f0f72f74cd2) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 12'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x86cb189f14c036cfc85c1038dbe96a0474c557f90ea85a5ff4bd041000000000) && + (a[1] == 0x00000000000000000000000000000000000000000004b4d7000dfd7f5b916fea) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 13'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x993eb6700aceaa8c239dc9655824c99669adb83887f999e02b37478000000000) && + (a[1] == 0x00000000000000000000000000000000000055ad70a1fd772c066fdf00e2e833) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 14'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0xd633e21efd2359ddbc66f0c332f2b43f4a5c83b29b4419da59ee840000000000) && + (a[1] == 0x00000000000000000000000000000617b616b1dd14623dd333f57d3f6f9fb73b) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 15'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0xf2604f10610c1f11865dd8967a675cb33ece0f884348824b1825e00000000000) && + (a[1] == 0x00000000000000000000006ee9ad6766a6bab0c7f75fbf20b9e1ec8f67a32671) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 16'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x2ddb20ed1b2057a515ec352b886aea116637d25da4b5dec18b01000000000000) && + (a[1] == 0x0000000000000007e31b1efc0297f27ad6e905b93ece3a4f29c995e135944df5) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 17'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x77d8defcfdad9a777d04c668a178409ffb704fd5292bbd9f7e78000000000000) && + (a[1] == 0x000000008f94b3ad947e64ea26bef78335322bec214696a1965b392c02bba128) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 18'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x1dcb3fef025446eddd7f6842764101e2cba877f7b97e979f9840000000000000) && + (a[1] == 0x0a35cf55f5c39605076f42f7d10e6ea75093a2cb4515d8cdd51954fde4bcbaf6) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 19'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x0976a8ec89391b1e7a8fca37997e4961d8a208cf7f661cf4de00000000000000) && + (a[1] == 0x059dbd21bb9f5829e3b01f1b1596d2eeba0a2a2c3bddd7318414dd55e75ca928) && + (a[2] == 0x0000000000000000000000000000000000000000000000000000000000b9def5), 'fails at 20'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x3e10887b28aff4e2bd6b867028f4aa2c5c04e4d71ef0ed5c1000000000000000) && + (a[1] == 0x69854d6854d408161e06ab6a6b21936e4c206309c79fcd61ebda1eb4c10f01ed) && + (a[2] == 0x000000000000000000000000000000000000000000000000000d37ad4c310e14), 'fails at 21'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x0779fc5bde1eff361b3a37ff1355b4ca630ac2dc207f70878000000000000000) && + (a[1] == 0x4da5790a30b5c9dc9ddc390f3305e6d15e797e8bdc606998c6d962c49c3c5f03) && + (a[2] == 0x00000000000000000000000000000000000000000000f09df5753dc87d4a4389), 'fails at 22'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x343c73ec2d2b6e45a145e42ecefb9c071980d9e66e1044840000000000000000) && + (a[1] == 0x49b8d6426af04930a312a1b1817a4374a35dd899e1ae7f8420005397075ba674) && + (a[2] == 0x000000000000000000000000000000000000111c4c9df4a9173efbe70cd93091), 'fails at 23'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0xf26de8750ee2c10eb77a6b8dbd97c363b4250d1c417475e00000000000000000) && + (a[1] == 0xd7a15d03229114bc06365247ab2695d8ebe1565b99b8aea0b56ddb963827ec72) && + (a[2] == 0x000000000000000000000000000001377cea324a9036a39377fb942710d18b7a), 'fails at 24'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x026f4181670950f6f1a336b28db247b38a1d1f2d7fb081000000000000000000) && + (a[1] == 0x65f3e0025f99f70ae9b21f76e112dec4400d765edea26c091db99c03d88a6bfe) && + (a[2] == 0x00000000000000000000001626770d14aff5c1ff8cf446f398d0b8553f31cb56), 'fails at 25'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0xebafc2db3cb777edc31cf08d7fd606aa0e583f255a1278000000000000000000) && + (a[1] == 0x62c60bdf7bcf76042209bad4e40c4e9142ffffd9825ecaee3f6c2441ccee4516) && + (a[2] == 0x0000000000000001933baa6822c61a5e6a3097bfac90cee12f856199ba5e3209), 'fails at 26'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x84a638dd2c4dc1c9fe1b4f37221d5ee5d27fbdf4ccf840000000000000000000) && + (a[1] == 0xf4ec60c9fe204e7c249a486d34613c96ab4c2546ed75a5800ca57af1c0205152) && + (a[2] == 0x000000001caca24cfecfda96d85d107866d2c1c76475a779b7a3df51d3d098a4), 'fails at 27'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x1f0f6bfa74723b2a4102a5f14b216dbf61d45e8879de00000000000000000000) && + (a[1] == 0x14371e22a54cc6eb60841ccc1e952a426f51ce8d73a4cc877d7c24253c8e7901) && + (a[2] == 0x020a0029d100514846b7a1747ed7df6a61747ee3310c37bc3f59dc9df7c9566c), 'fails at 28'); + (a[0],a[1],a[2]) = field3.mul3x3(a[0],a[1],a[2], b[0],b[1],b[2]); + require( + (a[0] == 0x48906fe1f29351da094874da324a8a5bff988601b41000000000000000000000) && + (a[1] == 0x8c9ebd5a3b69359dd336c539b3707dad90996b02a44d9b6c7c27c53ee7d87daf) && + (a[2] == 0x49ee9997ab1ba849d2e5c2c6d6b4733090d49f5fa7248d49cdbef842f0c8ccc4), 'fails at 29'); + gas = gas-gasleft(); + console.log("mul test passed, gas consumed:", gas, " per test: ", gas/28); + } + + + function test_divrem() public view + { + uint256 x = 0x6cd4f254f107fac863848ce8b56db5a13f66ce14a2632dbb0b3aa41dd9319ce1; + uint256 y = 0x0a35cf0f4a940d8fc49e4b88fd39ff84f54ef7f887c63173fde3d95c939bb9e9; + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0xf0602e9c05a41bf8ad732463b9cab2d82291ba58b16c61f40fabf3b0d31e3b42) && + (y == 0x000000008f94afcbcd31d4fa0d199883d859299e689c60d96e844ea74db8bd33), 'fails at 1'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x8d2ded57144ef0d63bf2c87604b2d7a58ca29bfe91877a731403b24a46817e5d) && + (y == 0x0000000000000007e31ae864fff17a56d7bc80a03c281e959e6c15e964bb4c50), 'fails at 2'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x6a6c3d6f49ede3ca81fc2afdfbe5906a7f32441142d9f43e96bdc6064ac10e69) && + (y == 0x00000000000000000000006ee9aa67bb115c3a1b4d5abeb55f77831daf47ce9e), 'fails at 3'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x1b4d4baea0a32339550bfeb716d011a7e834d4ecd273ef0fb8f26ab5cbd93bc1) && + (y == 0x00000000000000000000000000000617b5ec86802e7b68617ae51113231bb4f4), 'fails at 4'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0xc60031d6d2f09fe9b3f36e98b58dda00b803b54f4d867764627548a25db5e600) && + (y == 0x00000000000000000000000000000000000055ad6e50fbacb757c716e69f5c5f), 'fails at 5'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x98bf9bc962bf690cbdb2c5b2befed3b2d090d11a088317087a8d32bbba417ca6) && + (y == 0x00000000000000000000000000000000000000000004b4d6df7ad44f3dd66c53), 'fails at 6'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x71170c92554a699bd19c36efa151cf167916e1419a36f72bb686a8dacada34b3) && + (y == 0x00000000000000000000000000000000000000000000000000422f0da8e1d98e), 'fails at 7'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x35cf0bbcdfb4048f3ce093e0b919ad7ff1e8b2eb8f0bb07cc8884ffb5f4c4e5d) && + (y == 0x0000000000000000000000000000000000000000000000000000000003a2b5b0), 'fails at 8'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x33201affa7407a8d177791cd8f8e76db6f340a206b12aa2419b3080d9e6ea932) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 9'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000002cef37bc2ff105759920c58d40c2c5a783344cf65957f5b7b20202ad6) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 10'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000000000000277e3ffdb5d3f5b8e51f7644c1769d00a6c12e2a0df0575be9) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 11'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000000000000000000022b5f63f23d9f810321d637c18030d9c5ea9bd441ae) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 12'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000000000000000000000000001e81ed6e81d2f5318b8cf8953348efeaa31f) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 13'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x000000000000000000000000000000000001ad031af04040fd02dc96ce4a7433) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 14'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x0000000000000000000000000000000000000000001790fbab9ad7ed189c6968) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 15'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x000000000000000000000000000000000000000000000000014b66d328326678) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 16'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000000000000000000000000000000000000000000000000000000123455fa) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 17'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 18'); + } + + function test_mulmod_p381() public pure { + uint256 x = 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1; + uint256 y = 0x0000000000000000000000000000000000000000000000000000000087654321; + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000000); + + require( + (x == 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 1'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x0000000000000000000000000000000000000000000000000000000087654321, + 0x0000000000000000000000000000000000000000000000000000000000000000); + require( + (x == 0x0000000000000125bb0144149e4753e79ca098893db2c00a9500063b3b703cc1) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 2'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x000000000000000000000000000000000000000000000000479bf4dad7a44a41, + 0x0000000000000000000000000000000000000000000000000000000000000000); + require( + (x == 0x0000009b59cd78352b953b785020eaa5c64e2fa2150d52a7e858f182c88357e1) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 3'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x000000000000000000000000000000000000000025df916e0e3e8d9f5e419561, + 0x0000000000000000000000000000000000000000000000000000000000000000); + require( + (x == 0x29ce84fbd0e84d7f95862034f0a21a3c4a97e43f5b1f2be77b02bb7f8bb33701) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000052), 'fails at 4'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x000000000000000000000000000000001407e0d3ced028471fb5bcdc66d1a481, + 0x0000000000000000000000000000000000000000000000000000000000000000); + require( + (x == 0xb2f0f8a7956a1a7b00806d9796e2b095d45d64774bb436fcce6a2cb723e45a21) && + (y == 0x0000000000000000000000000000000000000000000000000000002b748bf223), 'fails at 5'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x0000000000000000000000000a9813ec0a7bc7cb892827c3640825c80cf8f7a1, + 0x0000000000000000000000000000000000000000000000000000000000000000); + require( + (x == 0x9c0cd08857668772da9cdf96758a02b85d521349ef36ae149d92469f5a0b4141) && + (y == 0x000000000000000000000000000000000000000000000016fba62d0201dc3aa1), 'fails at 6'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x0000000000000000059a63488078c15de7e04ff1ea6c06816db4a2fe6e6c0ec1, + 0x0000000000000000000000000000000000000000000000000000000000000000); + require( + (x == 0xa41c1d541b437ed1cb155a0a9173b3be1bf20c82b2d623d97db144f5932c6c61) && + (y == 0x000000000000000000000000000000000000000c27cbf11ebef9328d3bc92db2), 'fails at 7'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x0000000002f6a1bc883d2394f9804672b5e712964f8b409de58e261c1cef69e1, + 0x0000000000000000000000000000000000000000000000000000000000000000); + require( + (x == 0xb6808ec3eb0205ff782cf0407d26050643054d5d4b679d4d88a4a6da82715b6c) && + (y == 0x00000000000000000000000000000000078806f4b2c222f2c6cbe278084aae87), 'fails at 8'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x01913b5f29592ed8298aa8430527b95c85e68d3575362e9ca7a5f07cd0578901, + 0x0000000000000000000000000000000000000000000000000000000000000000); + require( + (x == 0xd1979e9b3412050795ba667cb98e3662896c665c9d3d65f1f0e5fd4fa9bf2c96) && + (y == 0x0000000000000000000000000000000004b8c83fa1c6195d82e49a1aa60ea2ba), 'fails at 9'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0xd19be6fa24f21f2c5f123668fbbf8465809402864a65a85bbd7106665888ec21, + 0x0000000000000000000000000000000000000000000000000000000000d43504); + require( + (x == 0x975190b7c1b3d2d01967187439c5cc71158cbd59c59ed477193f51d37dc016e2) && + (y == 0x00000000000000000000000000000000002d1bf03f3ba3763a6c8d8684c60a84), 'fails at 10'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0xdcb87fe0d402be7ad9c3cecc563069761ff46eabff430e5f7f4fbee7cf781341, + 0x00000000000000000000000000000000000000000000000000703be61aab01ca); + require( + (x == 0x4719b65d4242ef35f556dcd16c90eed920e7ad0db54222d751cccf54fac3cefa) && + (y == 0x000000000000000000000000000000000d1fcdaef1f5c2413a1f15cddc26220f), 'fails at 11'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0xc0c16afffb82e122a96eab9cae4c046374f15a037c3820c3d81b73b50b297e61, + 0x0000000000000000000000000000000000000000003b5bfb67fda50de0f1b324); + require( + (x == 0x2750d4792b0a409d8c1912b29c499793619fa9bc5346c02a50133fc31f7af703) && + (y == 0x00000000000000000000000000000000080c30a87a4c9ea34a171578106e28d5), 'fails at 12'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0xac90092ec6d14cf922e8fb1e8adb021d978d1f61e80a37a72a2295464fb1ad81, + 0x00000000000000000000000000000000001f64fc6ec070bc39fe1d7d5030c1eb); + require( + (x == 0xcd745dd9a178b7ad720d73e3c09e6e51d1c78e035802d9b1282a9b21ceda5f95) && + (y == 0x00000000000000000000000000000000159af635e386455b738d9ea277cb061b), 'fails at 13'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x3b453d3b27d3c298a807a3cca8613ab47b659abab20dc8f0744ac884413520a1, + 0x00000000000000000000000000109aac317942e66127ae3fb333f2a3e304b736); + require( + (x == 0x0082026b5b0abcb0ff969fca9e3cf70d698757b85e55a0820961c28d4768a8eb) && + (y == 0x0000000000000000000000000000000016d9f0e230f7ed1eab2752cc59e4b15b), 'fails at 14'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0x1d4ac236d5105c0c356461c47d2d113dff255311a38f26ad6aaf344815e857c1, + 0x00000000000000000008c8222ea57843ff442cab3f5a3a8fd99339046135b51c); + require( + (x == 0x2fcba4b950f3c6c99f6999740a776ba6650715494b3d05172db892edf15b58b1) && + (y == 0x0000000000000000000000000000000011e50e8fea4bd8e59f8323062b767522), 'fails at 15'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0xe17512d3802c0fb5580f2df66d54cb4cbbc357f44240daf83f42c5f8080fd2e1, + 0x000000000004a50349986851a03ef606ad732722be72aca361e42bb3dddcaf33); + require( + (x == 0xf3aaf943e1e1b48e822797b1d6d7390ca585f2b4b14a85a3216bbab4ef6f627a) && + (y == 0x000000000000000000000000000000001767d51aeaed9a0f35750d6eb55e1234), 'fails at 16'); + + (x,y) = field3.mulmod_p381( + 0x00000000000000000000022b5f6df4dfa0c8a0e35eb9cb284d233c383da565a1, + 0x0000000000000000000000000000000000000000000000000000000000000000, + 0xaa0d9579e1c63ba668dd450af56d617bd6819ae2d0f85e4c8886f58808001201, + 0x000274db0de489d393a83c0ab89b9e534239bf9ad50bf5bf6dcf9db5d6a9d9cf); + require( + (x == 0xc88992d202827106967936e1916e7e415e22cb44eda3fb3988f6c87c3d29c38a) && + (y == 0x000000000000000000000000000000000b53eb88fb2dd4dc0a424c007c5ba394), 'fails at 17');} + +} diff --git a/contracts/test/algebra/field3_gas_estimation.sol b/contracts/test/algebra/field3_gas_estimation.sol new file mode 100644 index 0000000..444ae24 --- /dev/null +++ b/contracts/test/algebra/field3_gas_estimation.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//---------------------------------------------------------------------------// +pragma solidity ^0.8.0; + +import '../../../contracts/algebra/field3.sol'; +import 'hardhat/console.sol'; + +contract field3_gas_estimation { + + function test_add(uint256 runs) public view returns (uint256[3] memory r) { + + uint256[3] memory a = [uint256(1),0,0]; + uint256[3] memory b = [0, runs, 0]; + uint256 gas = gasleft(); + uint256 save_runs = runs; + unchecked { + for(;runs>0;runs--) { + (b[0], b[1], b[2]) = field3.empty(a[0], a[1], a[2], b[0], b[1], b[2]); + } + } + gas = gas-gasleft(); + uint256 per_one = gas / save_runs; + console.log("Gas used: ", gas, " per one empty call:", per_one); + + a = [uint256(1),0,0]; + b = [0, save_runs, 0]; + runs = save_runs; + + gas = gasleft(); + unchecked { + for(;runs>0;runs--) { + (b[0], b[1], b[2]) = field3.add(a[0], a[1], a[2], b[0], b[1], b[2]); + } + } + gas = gas-gasleft(); + require(b[0] == save_runs, "add test failed"); + per_one = gas / save_runs; + console.log("Gas used: ", gas, " per one call:", per_one); + console.log("Runs: ", save_runs); + return b; + + /* + uint256[3] memory a = field3.create(1); + uint256[3] memory b = field3.create(0, runs); + uint256 gas = gasleft(); + unchecked { + for(;runs>0;runs--) { + b = field3.add(a, b); + } + } + gas = gas-gasleft(); + require(b[0] == b[1], "add test failed"); + uint256 per_one = gas/ b[0]; + console.log("Gas used: ", gas, "per one call:", per_one); + return b; + */ + } + + function test_sub(uint256 runs) public view { + uint256[3] memory b = [runs, runs, 0]; + uint256[3] memory a = [uint256(1),0,0]; + uint256 gas = gasleft(); + unchecked { + for(;runs>0;runs--) { + (b[0], b[1], b[2]) = field3.sub(b[0], b[1], b[2], a[0], a[1], a[2]); + } + } + gas = gas-gasleft(); + require(b[0] == 0 && b[2] == 0, "sub test failed"); + uint256 per_one = gas / b[1]; + console.log("Gas used: ", gas, " per one call:", per_one); + console.log("Runs: ", b[1]); + } + +} diff --git a/contracts/test/algebra/test_2pi.sol b/contracts/test/algebra/test_2pi.sol new file mode 100644 index 0000000..4804c11 --- /dev/null +++ b/contracts/test/algebra/test_2pi.sol @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//---------------------------------------------------------------------------// +pragma solidity ^0.8.0; + +import '../../../contracts/algebra/field3.sol'; +import 'hardhat/console.sol'; + +contract test_2pi { + + uint256 constant neg0 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + function test_div256() public pure { + uint256 x; + + x = field3.div256(0x0000000000000000000000000000000000000000000000000000000000000011); + require(x == 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f, 'test fails at 1'); + x = field3.div256(0x0000000000000000000000000000000000000000000000000000000000000121); + require(x == 0x00e2c4a6886a4c2e0ff1d3b597795b3d1f00e2c4a6886a4c2e0ff1d3b597795b, 'test fails at 2'); + x = field3.div256(0x0000000000000000000000000000000000000000000000000000000000001331); + require(x == 0x000d56dc9e9cd74e00f01b832707237c10e1ef38be807eb92fe2d1fd6508e905, 'test fails at 3'); + x = field3.div256(0x0000000000000000000000000000000000000000000000000000000000014641); + require(x == 0x0000c8dfcd1848e678869834e42d98acf1ef2c3083ad34a17b4993e1baa62bd3, 'test fails at 4'); + x = field3.div256(0x000000000000000000000000000000000000000000000000000000000015aa51); + require(x == 0x00000bd0edf25ea4253518031c7b27193b686c02da91b7cd437ccc76b0a05cee, 'test fails at 5'); + x = field3.div256(0x0000000000000000000000000000000000000000000000000000000001704f61); + require(x == 0x000000b1efe114a03e6c88f11fcb024cc74260b4dfae37fd03f848433790f668, 'test fails at 6'); + x = field3.div256(0x0000000000000000000000000000000000000000000000000000000018754571); + require(x == 0x0000000a7785b5eb4ef753597a573c5ede8b6f19b2ce034b1e59e6221253d242, 'test fails at 7'); + x = field3.div256(0x000000000000000000000000000000000000000000000000000000019fc99c81); + require(x == 0x000000009d9e741ce68704e725506cf6858fbb3dbf394b7ce3aaef6b6a7d66b8, 'test fails at 8'); + x = field3.div256(0x0000000000000000000000000000000000000000000000000000001b9c636491); + require(x == 0x0000000009458e5c0d8f78c24d7d339607db473fde126dda2b82868dd9166f74, 'test fails at 9'); + x = field3.div256(0x000000000000000000000000000000000000000000000000000001d56299ada1); + require(x == 0x00000000008b9ef65b268ea2048ee4eab52b0430eef206763ecb7153a35bac34, 'test fails at 10'); + x = field3.div256(0x00000000000000000000000000000000000000000000000000001f2b8c3487b1); + require(x == 0x0000000000083686f64d8feb69ae0d7737d55a997777a606f4a28e3218ab0a21, 'test fails at 11'); + x = field3.div256(0x000000000000000000000000000000000000000000000000000211e44f7d02c1); + require(x == 0x0000000000007bad96048ffec9fb2df7f439ba09070709c42c82085d4cbec45c, 'test fails at 12'); + x = field3.div256(0x00000000000000000000000000000000000000000000000000233029474d2ed1); + require(x == 0x0000000000000746723c80f0deb46c1da4f4563cc42d9729a843e25fd7568405, 'test fails at 13'); + x = field3.div256(0x000000000000000000000000000000000000000000000000025632bdbc201be1); + require(x == 0x000000000000006d8e3fcb597683156b27d22330c03eeac637130d50ee8c9e5a, 'test fails at 14'); + x = field3.div256(0x00000000000000000000000000000000000000000000000027b95e997e21d9f1); + require(x == 0x000000000000000671c7846eac9e4c8dd52a7a8a65a9591ab7f20fd7958fcd14, 'test fails at 15'); + x = field3.div256(0x000000000000000000000000000000000000000000000002a34f4831603f7901); + require(x == 0x0000000000000000610bbc7efb185edb2aa825536f64508919e10ffd9f62cfd4, 'test fails at 16'); + x = field3.div256(0x00000000000000000000000000000000000000000000002cd843cb4764370911); + require(x == 0x000000000000000005b56570e19805946bebc5f5d96040f90185b5a581d8a2d0, 'test fails at 17'); + x = field3.div256(0x0000000000000000000000000000000000000000000002fa5c807fbda7a79a21); + require(x == 0x00000000000000000055f6e885bda5f9abfecf68d08d30ff96ad8327da7627b1, 'test fails at 18'); + x = field3.div256(0x0000000000000000000000000000000000000000000032a024887b9822213c31); + require(x == 0x000000000000000000050e8625fc18d27387759cc0f93f1e17ec16c61be8d528, 'test fails at 19'); + x = field3.div256(0x000000000000000000000000000000000000000000035ca26d10351a4434ff41); + require(x == 0x000000000000000000004c26023c0175ca8f7f6392e17c2ef2592e841fc266e4, 'test fails at 20'); + x = field3.div256(0x0000000000000000000000000000000000000000003926c93e1386be8784f351); + require(x == 0x00000000000000000000047ab4d65a7057359e14ea85bc02c2f62fe9a783e7ef, 'test fails at 21'); + x = field3.div256(0x000000000000000000000000000000000000000003cb935d1f4bf2a6ffd42861); + require(x == 0x000000000000000000000043740c9be87d99be013af8ced2fc68d5a45525e077, 'test fails at 22'); + x = field3.div256(0x00000000000000000000000000000000000000004084c92f140b1d16fd16ae71); + require(x == 0x000000000000000000000003f7c481a4439fa1c3d64adefd5a2448cd6e6ba3ca, 'test fails at 23'); + x = field3.div256(0x000000000000000000000000000000000000000448d15c2054bcee86ce819581); + require(x == 0x0000000000000000000000003bc043dc7c72cd47c14fb2c39be4044851ca18b1, 'test fails at 24'); + x = field3.div256(0x0000000000000000000000000000000000000048d5e71e25a08bd6f3b69aed91); + require(x == 0x0000000000000000000000000383c7c1acf7b1b8ed40ec65dbfe5a9ad7a279ec, 'test fails at 25'); + x = field3.div256(0x00000000000000000000000000000000000004d63459007fa949462f2049c6a1); + require(x == 0x0000000000000000000000000034eda1fb1da10ae0c7956f674b32819436bbe0, 'test fails at 26'); + x = field3.div256(0x000000000000000000000000000000000000523979e9087a3ddda92124e630b1); + require(x == 0x00000000000000000000000000031d09873dfa6a0d38eaac3340a89e35e51a1c, 'test fails at 27'); + x = field3.div256(0x00000000000000000000000000000000000575d11879901e1bb83b3373493bc1); + require(x == 0x00000000000000000000000000002ee2715dffabe2a8febed5d6a081c6ef5be3, 'test fails at 28'); + x = field3.div256(0x00000000000000000000000000000000005cd2e2a01291ffd73bee6aa7dcf7d1); + require(x == 0x000000000000000000000000000002c206ab2d283a826956850ca007a24a50b3, 'test fails at 29'); + x = field3.div256(0x00000000000000000000000000000000062a010ca13bb1fd4afad51525ac74e1); + require(x == 0x0000000000000000000000000000002987ebf398f4620632440fcd2da0227d37, 'test fails at 30'); + x = field3.div256(0x0000000000000000000000000000000068ca11d6b4f6d1d1faa826678073c2f1); + require(x == 0x0000000000000000000000000000000271683b8177c987e4d6d3c0c672d4da30, 'test fails at 31'); + x = field3.div256(0x00000000000000000000000000000006f56b2f420463eef1a52a8cdf87aff201); + require(x == 0x0000000000000000000000000000000024c9e561f7fccbc22ac1297515d04911, 'test fails at 32'); + x = field3.div256(0x000000000000000000000000000000764c1e23624aa2de0bf7d35ad802af1211); + require(x == 0x000000000000000000000000000000000229fe6f2cb48474d556a815f2396db5, 'test fails at 33'); + x = field3.div256(0x000000000000000000000000000007db0e005986f4d0becb750908582da03321); + require(x == 0x000000000000000000000000000000000020967f02a134f7d050643d86b81583, 'test fails at 34'); + x = field3.div256(0x0000000000000000000000000000858bee05f1f641dcab82c5998ddb07a36531); + require(x == 0x000000000000000000000000000000000001eabc2d54c6e1669b5130cbb079bc, 'test fails at 35'); + x = field3.div256(0x0000000000000000000000000008de4ace65115a5fa763af1f326b8b81d9b841); + require(x == 0x0000000000000000000000000000000000001cdde48c842b60637d3f1b0a6183, 'test fails at 36'); + x = field3.div256(0x0000000000000000000000000096c2f7b4b627005a1d9ea1125924439f753c51); + require(x == 0x00000000000000000000000000000000000001b2b31753119c42166d1fb55107, 'test fails at 37'); + x = field3.div256(0x0000000000000000000000000a02f27300189705fbf788b237eb687d96c90161); + require(x == 0x000000000000000000000000000000000000001992106e4c547c5bac10ec8c4b, 'test fails at 38'); + x = field3.div256(0x000000000000000000000000aa3219a301a20765bb7013d5b6a1f05703591771); + require(x == 0x00000000000000000000000000000000000000018110067cf5e93291a6a480b9, 'test fails at 39'); + x = field3.div256(0x00000000000000000000000b4d53b3d31bc27dc17271513120c0f5c738ea8e81); + require(x == 0x000000000000000000000000000000000000000016a696f84ab35d53dca043ce, 'test fails at 40'); + x = field3.div256(0x0000000000000000000000c0228ef104d7ea59d8998664432cd0523ac7937691); + require(x == 0x0000000000000000000000000000000000000000015517f07cdd5fd7c1af130c, 'test fails at 41'); + x = field3.div256(0x000000000000000000000cc24b7e0152568ff76231eca875f9d575e740cadfa1); + require(x == 0x0000000000000000000000000000000000000000001410778edfd8761a73b5d3, 'test fails at 42'); + x = field3.div256(0x00000000000000000000d8e7035e1677bf8f6d8550b72fd5972cd45b4d78d9b1); + require(x == 0x000000000000000000000000000000000000000000012e252685a3523dca9239, 'test fails at 43'); + x = field3.div256(0x0000000000000000000e6757393f7df3b88645da5c2a2d2f09fa1a10250674c1); + require(x == 0x0000000000000000000000000000000000000000000011c5f335099b6d0bea7b, 'test fails at 44'); + x = field3.div256(0x000000000000000000f4dccacd375d2f40eaa3801ecd001fa99bbb12756dc0d1); + require(x == 0x00000000000000000000000000000000000000000000010ba4e50090ac0fc27f, 'test fails at 45'); + x = field3.div256(0x00000000000000001042a977a0ad30234f94db820b9d021a43576c39cc49cde1); + require(x == 0x00000000000000000000000000000000000000000000000fbe67d2db556a56bc, 'test fails at 46'); + x = field3.div256(0x0000000000000001146d40f1ab80325848e293a2c56d23be78ce2fd690e6abf1); + require(x == 0x000000000000000000000000000000000000000000000000ed152a855f609bb0, 'test fails at 47'); + x = field3.div256(0x00000000000000125b41500c638357dcd70bcdcf1c3f5fa605b12d3f9f516b01); + require(x == 0x0000000000000000000000000000000000000000000000000df22fad7e14bddd, 'test fails at 48'); + x = field3.div256(0x00000000000001380f5650d29bb8d5aa47c8aac0e0355a0660c4013994681b11); + require(x == 0x00000000000000000000000000000000000000000000000000d202cdf85b92b2, 'test fails at 49'); + x = field3.div256(0x00000000000014b904bb5dfc5746304ec45356cee38afa6c6d0414d2dae9cc21); + require(x == 0x000000000000000000000000000000000000000000000000000c5a8496238119, 'test fails at 50'); + x = field3.div256(0x000000000001604950713dc1cba9353b0988c3bd1c3aa1333d45620089868e31); + require(x == 0x0000000000000000000000000000000000000000000000000000ba07cc98ad3d, 'test fails at 51'); + x = field3.div256(0x00000000001764de578519de863c88eba214ff8edfe4b467119b820921ef7141); + require(x == 0x00000000000000000000000000000000000000000000000000000af16663557c, 'test fails at 52'); + x = field3.div256(0x00000000018db2c3cfd6b7c6ea0517a5c364f87cde2ffad82b53a29b40e68551); + require(x == 0x000000000000000000000000000000000000000000000000000000a4c9c99b9d, 'test fails at 53'); + x = field3.div256(0x000000001a68df00cd4234358a569201f9b4804ac12fa85ae08dcc4f4f4eda61); + require(x == 0x00000000000000000000000000000000000000000000000000000009b1845472, 'test fails at 54'); + x = field3.div256(0x00000001c0f6cf0da165778e2fbfb22194fc84f6d42a2e08e96a9144443c8071); + require(x == 0x0000000000000000000000000000000000000000000000000000000091f8b9ac, 'test fails at 55'); + x = field3.div256(0x0000001dd063bfe7b7bcf0712bbad43ae4c4d46416cd0e978013a58888048781); + require(x == 0x000000000000000000000000000000000000000000000000000000000896290a, 'test fails at 56'); + x = field3.div256(0x000001fad69fbe63338bf783e76817e931121aa5839df80f814dfe11084cff91); + require(x == 0x0000000000000000000000000000000000000000000000000000000000814db5, 'test fails at 57'); + x = field3.div256(0x000021a8409ba4966c4b6fc25de9967c4233c4fdbd7d7907962ddf218d1cf8a1); + require(x == 0x0000000000000000000000000000000000000000000000000000000000079b28, 'test fails at 58'); + x = field3.div256(0x00023c2c4a55edfd31026be83c82fe40657014d995550980f90bd13a5eec82b1); + require(x == 0x0000000000000000000000000000000000000000000000000000000000007289, 'test fails at 59'); + x = field3.div256(0x0025fef0efb4cdd041292a6c04b2e246bc716272eaa5a19089c8e4e04db4adc1); + require(x == 0x00000000000000000000000000000000000000000000000000000000000006bc, 'test fails at 60'); + x = field3.div256(0x0285edffeb01aad453bbd12c4fe106b2838789a194ffba99265732e528ff89d1); + require(x == 0x0000000000000000000000000000000000000000000000000000000000000065, 'test fails at 61'); + x = field3.div256(0x2ae4cdfe9b1c58198f78e3f14df171dabc0023bae4fb642b8bca6137b8f826e1); + require(x == 0x0000000000000000000000000000000000000000000000000000000000000005, 'test fails at 62'); + } + + function test_mod256() public pure { + uint256 x; + + x = field3.mod256(0x0000000000000000000000000000000000000000000000000000000000000011); + require(x == 0x0000000000000000000000000000000000000000000000000000000000000001, 'test fails at 1'); + x = field3.mod256(0x0000000000000000000000000000000000000000000000000000000000000121); + require(x == 0x0000000000000000000000000000000000000000000000000000000000000045, 'test fails at 2'); + x = field3.mod256(0x0000000000000000000000000000000000000000000000000000000000001331); + require(x == 0x000000000000000000000000000000000000000000000000000000000000070b, 'test fails at 3'); + x = field3.mod256(0x0000000000000000000000000000000000000000000000000000000000014641); + require(x == 0x0000000000000000000000000000000000000000000000000000000000002d6d, 'test fails at 4'); + x = field3.mod256(0x000000000000000000000000000000000000000000000000000000000015aa51); + require(x == 0x0000000000000000000000000000000000000000000000000000000000068cb2, 'test fails at 5'); + x = field3.mod256(0x0000000000000000000000000000000000000000000000000000000001704f61); + require(x == 0x0000000000000000000000000000000000000000000000000000000000888a98, 'test fails at 6'); + x = field3.mod256(0x0000000000000000000000000000000000000000000000000000000018754571); + require(x == 0x00000000000000000000000000000000000000000000000000000000092a66de, 'test fails at 7'); + x = field3.mod256(0x000000000000000000000000000000000000000000000000000000019fc99c81); + require(x == 0x00000000000000000000000000000000000000000000000000000000fdbf1d48, 'test fails at 8'); + x = field3.mod256(0x0000000000000000000000000000000000000000000000000000001b9c636491); + require(x == 0x000000000000000000000000000000000000000000000000000000077ce58f4c, 'test fails at 9'); + x = field3.mod256(0x000000000000000000000000000000000000000000000000000001d56299ada1); + require(x == 0x000000000000000000000000000000000000000000000000000000077ce58f4c, 'test fails at 10'); + x = field3.mod256(0x00000000000000000000000000000000000000000000000000001f2b8c3487b1); + require(x == 0x00000000000000000000000000000000000000000000000000000587a4b2982f, 'test fails at 11'); + x = field3.mod256(0x000000000000000000000000000000000000000000000000000211e44f7d02c1); + require(x == 0x0000000000000000000000000000000000000000000000000000a16161b93ea4, 'test fails at 12'); + x = field3.mod256(0x00000000000000000000000000000000000000000000000000233029474d2ed1); + require(x == 0x000000000000000000000000000000000000000000000000000f1e9f8e2451eb, 'test fails at 13'); + x = field3.mod256(0x000000000000000000000000000000000000000000000000025632bdbc201be1); + require(x == 0x000000000000000000000000000000000000000000000000019230659e7554e6, 'test fails at 14'); + x = field3.mod256(0x00000000000000000000000000000000000000000000000027b95e997e21d9f1); + require(x == 0x0000000000000000000000000000000000000000000000000f9760d80735fc2c, 'test fails at 15'); + x = field3.mod256(0x000000000000000000000000000000000000000000000002a34f4831603f7901); + require(x == 0x0000000000000000000000000000000000000000000000000f9760d80735fc2c, 'test fails at 16'); + x = field3.mod256(0x00000000000000000000000000000000000000000000002cd843cb4764370911); + require(x == 0x00000000000000000000000000000000000000000000000a9cd4819d8833e030, 'test fails at 17'); + x = field3.mod256(0x0000000000000000000000000000000000000000000002fa5c807fbda7a79a21); + require(x == 0x0000000000000000000000000000000000000000000002ab48cd6acc676d682f, 'test fails at 18'); + x = field3.mod256(0x0000000000000000000000000000000000000000000032a024887b9822213c31); + require(x == 0x000000000000000000000000000000000000000000001d788951e8774c51d358, 'test fails at 19'); + x = field3.mod256(0x000000000000000000000000000000000000000000035ca26d10351a4434ff41); + require(x == 0x00000000000000000000000000000000000000000000e7f91b73d6d7d4d6c41c, 'test fails at 20'); + x = field3.mod256(0x0000000000000000000000000000000000000000003926c93e1386be8784f351); + require(x == 0x00000000000000000000000000000000000000000011b7253cc4e05b29dfc061, 'test fails at 21'); + x = field3.mod256(0x000000000000000000000000000000000000000003cb935d1f4bf2a6ffd42861); + require(x == 0x000000000000000000000000000000000000000001daed6f2d61164f66075ae9, 'test fails at 22'); + x = field3.mod256(0x00000000000000000000000000000000000000004084c92f140b1d16fd16ae71); + require(x == 0x000000000000000000000000000000000000000033316929c43c68ca63cd67d6, 'test fails at 23'); + x = field3.mod256(0x000000000000000000000000000000000000000448d15c2054bcee86ce819581); + require(x == 0x000000000000000000000000000000000000000277dc7bd178a06e99499989cf, 'test fails at 24'); + x = field3.mod256(0x0000000000000000000000000000000000000048d5e71e25a08bd6f3b69aed91); + require(x == 0x0000000000000000000000000000000000000017e3f348732051173b52217554, 'test fails at 25'); + x = field3.mod256(0x00000000000000000000000000000000000004d63459007fa949462f2049c6a1); + require(x == 0x0000000000000000000000000000000000000381eac8b236a6df2aa7e1649820, 'test fails at 26'); + x = field3.mod256(0x000000000000000000000000000000000000523979e9087a3ddda92124e630b1); + require(x == 0x00000000000000000000000000000000000016dabc2cb4354c044364628bb2a4, 'test fails at 27'); + x = field3.mod256(0x00000000000000000000000000000000000575d11879901e1bb83b3373493bc1); + require(x == 0x000000000000000000000000000000000002fae0055e008178cf358eaea368dd, 'test fails at 28'); + x = field3.mod256(0x00000000000000000000000000000000005cd2e2a01291ffd73bee6aa7dcf7d1); + require(x == 0x000000000000000000000000000000000002fae0055e008178cf358eaea368dd, 'test fails at 29'); + x = field3.mod256(0x00000000000000000000000000000000062a010ca13bb1fd4afad51525ac74e1); + require(x == 0x00000000000000000000000000000000045cdd7f863cd87f8f9e628e8cff06a9, 'test fails at 30'); + x = field3.mod256(0x0000000000000000000000000000000068ca11d6b4f6d1d1faa826678073c2f1); + require(x == 0x000000000000000000000000000000002f82e4d7eedeb66c9c7a362294b638d0, 'test fails at 31'); + x = field3.mod256(0x00000000000000000000000000000006f56b2f420463eef1a52a8cdf87aff201); + require(x == 0x000000000000000000000000000000065359f06c895501ba4c5476331b7ea4ef, 'test fails at 32'); + x = field3.mod256(0x000000000000000000000000000000764c1e23624aa2de0bf7d35ad802af1211); + require(x == 0x00000000000000000000000000000059d4602784be04350e0a5310ad77bdfcfb, 'test fails at 33'); + x = field3.mod256(0x000000000000000000000000000007db0e005986f4d0becb750908582da03321); + require(x == 0x000000000000000000000000000001466c9c6e495349f125f9f9c65d7d1c211d, 'test fails at 34'); + x = field3.mod256(0x0000000000000000000000000000858bee05f1f641dcab82c5998ddb07a36531); + require(x == 0x00000000000000000000000000003843ce9ee0fa04ff28b62d3900c6bc7d8704, 'test fails at 35'); + x = field3.mod256(0x0000000000000000000000000008de4ace65115a5fa763af1f326b8b81d9b841); + require(x == 0x0000000000000000000000000004ea2f2cd462a255c1304f1f9efd7a013c15bd, 'test fails at 36'); + x = field3.mod256(0x0000000000000000000000000096c2f7b4b627005a1d9ea1125924439f753c51); + require(x == 0x000000000000000000000000006f55b0d99132ded199dc8495fc08041770b8c9, 'test fails at 37'); + x = field3.mod256(0x0000000000000000000000000a02f27300189705fbf788b237eb687d96c90161); + require(x == 0x0000000000000000000000000780794d521b06e30afd4c117229bb2f90ef8c95, 'test fails at 38'); + x = field3.mod256(0x000000000000000000000000aa3219a301a20765bb7013d5b6a1f05703591771); + require(x == 0x0000000000000000000000001b865e33524c34ef02ec5d75e2008c2abe818f57, 'test fails at 39'); + x = field3.mod256(0x00000000000000000000000b4d53b3d31bc27dc17271513120c0f5c738ea8e81); + require(x == 0x0000000000000000000000076bad78346442864e10bd37a4baf5dfe7e3559132, 'test fails at 40'); + x = field3.mod256(0x0000000000000000000000c0228ef104d7ea59d8998664432cd0523ac7937691); + require(x == 0x00000000000000000000001e0654dfda9bc781d0f59fda06fc77cb76552aae34, 'test fails at 41'); + x = field3.mod256(0x000000000000000000000cc24b7e0152568ff76231eca875f9d575e740cadfa1); + require(x == 0x0000000000000000000006df3d5b59063304aa6e5b5960638fcaaf875959d94d, 'test fails at 42'); + x = field3.mod256(0x00000000000000000000d8e7035e1677bf8f6d8550b72fd5972cd45b4d78d9b1); + require(x == 0x0000000000000000000086763047663d94a454444e97f4ff52214a8fe1469597, 'test fails at 43'); + x = field3.mod256(0x0000000000000000000e6757393f7df3b88645da5c2a2d2f09fa1a10250674c1); + require(x == 0x0000000000000000000c63185f6ca0ca0e7c518eb89c92ad9694e78e1de27d45, 'test fails at 44'); + x = field3.mod256(0x000000000000000000f4dccacd375d2f40eaa3801ecd001fa99bbb12756dc0d1); + require(x == 0x000000000000000000b93b2f0e668836b4c797cb0a96b0e20e4e204fda2ff651, 'test fails at 45'); + x = field3.mod256(0x00000000000000001042a977a0ad30234f94db820b9d021a43576c39cc49cde1); + require(x == 0x00000000000000000397d18f760c9fc47787824b66fdb1410b2151873a7938c4, 'test fails at 46'); + x = field3.mod256(0x0000000000000001146d40f1ab80325848e293a2c56d23be78ce2fd690e6abf1); + require(x == 0x0000000000000000c6b7c32afe2ae16c3281cc63f259ca7c333a643ccdeedf50, 'test fails at 47'); + x = field3.mod256(0x00000000000000125b41500c638357dcd70bcdcf1c3f5fa605b12d3f9f516b01); + require(x == 0x000000000000000403ff860000ab78750d29874c42a135b79da4f3c080a2e323, 'test fails at 48'); + x = field3.mod256(0x00000000000001380f5650d29bb8d5aa47c8aac0e0355a0660c4013994681b11); + require(x == 0x00000000000000cdefcdf68847503ef24aab5f32795a51d9dc41e57c59227c2e, 'test fails at 49'); + x = field3.mod256(0x00000000000014b904bb5dfc5746304ec45356cee38afa6c6d0414d2dae9cc21); + require(x == 0x0000000000000bc679d6cdefc0cfc1eed0b95ffa5b3a7c134325f08290cb6fc7, 'test fails at 50'); + x = field3.mod256(0x000000000001604950713dc1cba9353b0988c3bd1c3aa1333d45620089868e31); + require(x == 0x0000000000010472b29f35c3d81a05a004a171ad05be39285f56ea64d3c10153, 'test fails at 51'); + x = field3.mod256(0x00000000001764de578519de863c88eba214ff8edfe4b467119b820921ef7141); + require(x == 0x00000000000264bc03107385a3c33adb0e2a356a21f8da5b9c9c4c655d478f84, 'test fails at 52'); + x = field3.mod256(0x00000000018db2c3cfd6b7c6ea0517a5c364f87cde2ffad82b53a29b40e68551); + require(x == 0x0000000001614dc323dcf78f814f40a98d652ec9405f6c65a4b8eaee5a4f3253, 'test fails at 53'); + x = field3.mod256(0x000000001a68df00cd4234358a569201f9b4804ac12fa85ae08dcc4f4f4eda61); + require(x == 0x000000001277fc2d1216dd1b8f8744c8f2bbdc26cc6f33af8150e79a2436ecce, 'test fails at 54'); + x = field3.mod256(0x00000001c0f6cf0da165778e2fbfb22194fc84f6d42a2e08e96a9144443c8071); + require(x == 0x00000000b0ed3631e1a4165ccd8eb0d4ccf6dde7538d25d0c4a3b17600100b14, 'test fails at 55'); + x = field3.mod256(0x0000001dd063bfe7b7bcf0712bbad43ae4c4d46416cd0e978013a58888048781); + require(x == 0x0000000432dad44d246f05792d0e1517f6efe7d4fbe181e29778d3fe88890bf6, 'test fails at 56'); + x = field3.mod256(0x000001fad69fbe63338bf783e76817e931121aa5839df80f814dfe11084cff91); + require(x == 0x0000009944cd93d3bb1fb7af07b43a3e6ec80dc96de2cad817db0fa9309fb17b, 'test fails at 57'); + x = field3.mod256(0x000021a8409ba4966c4b6fc25de9967c4233c4fdbd7d7907962ddf218d1cf8a1); + require(x == 0x00001a562aea3edd593b4961c7fd7115ecb368311ce863a1a8d0f6869c88abd8, 'test fails at 58'); + x = field3.mod256(0x00023c2c4a55edfd31026be83c82fe40657014d995550980f90bd13a5eec82b1); + require(x == 0x00021331f408e3adb1a6d5c548ad425dcdbbf30f37427b137581097de13b3d47, 'test fails at 59'); + x = field3.mod256(0x0025fef0efb4cdd041292a6c04b2e246bc716272eaa5a19089c8e4e04db4adc1); + require(x == 0x001f2171ba65f9892ec650905b542ba2f46d021bcc93f6a01b1aa974b33de044, 'test fails at 60'); + x = field3.mod256(0x0285edffeb01aad453bbd12c4fe106b2838789a194ffba99265732e528ff89d1); + require(x == 0x01291a0848579a3af6e679847c385b921b86b340371b6193df98eb96d32ea08b, 'test fails at 61'); + x = field3.mod256(0x2ae4cdfe9b1c58198f78e3f14df171dabc0023bae4fb642b8bca6137b8f826e1); + require(x == 0x2987fa06f872478032a38c497a48c6ba53ff4d5987170b26450c19e963273d9b, 'test fails at 62'); + } + + function test_div512() public pure { + uint256 x = 0x6cd4f254f107fac863848ce8b56db5a13f66ce14a2632dbb0b3aa41dd9319ce1; + uint256 y = 0x0a35cf0f4a940d8fc49e4b88fd39ff84f54ef7f887c63173fde3d95c939bb9e9; + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0xf0602e9c05a41bf8ad732463b9cab2d82291ba58b16c61f40fabf3b0d31e3b42) && + (y == 0x000000008f94afcbcd31d4fa0d199883d859299e689c60d96e844ea74db8bd33), 'fails at 1'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x8d2ded57144ef0d63bf2c87604b2d7a58ca29bfe91877a731403b24a46817e5d) && + (y == 0x0000000000000007e31ae864fff17a56d7bc80a03c281e959e6c15e964bb4c50), 'fails at 2'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x6a6c3d6f49ede3ca81fc2afdfbe5906a7f32441142d9f43e96bdc6064ac10e69) && + (y == 0x00000000000000000000006ee9aa67bb115c3a1b4d5abeb55f77831daf47ce9e), 'fails at 3'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x1b4d4baea0a32339550bfeb716d011a7e834d4ecd273ef0fb8f26ab5cbd93bc1) && + (y == 0x00000000000000000000000000000617b5ec86802e7b68617ae51113231bb4f4), 'fails at 4'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0xc60031d6d2f09fe9b3f36e98b58dda00b803b54f4d867764627548a25db5e600) && + (y == 0x00000000000000000000000000000000000055ad6e50fbacb757c716e69f5c5f), 'fails at 5'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x98bf9bc962bf690cbdb2c5b2befed3b2d090d11a088317087a8d32bbba417ca6) && + (y == 0x00000000000000000000000000000000000000000004b4d6df7ad44f3dd66c53), 'fails at 6'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x71170c92554a699bd19c36efa151cf167916e1419a36f72bb686a8dacada34b3) && + (y == 0x00000000000000000000000000000000000000000000000000422f0da8e1d98e), 'fails at 7'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x35cf0bbcdfb4048f3ce093e0b919ad7ff1e8b2eb8f0bb07cc8884ffb5f4c4e5d) && + (y == 0x0000000000000000000000000000000000000000000000000000000003a2b5b0), 'fails at 8'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x33201affa7407a8d177791cd8f8e76db6f340a206b12aa2419b3080d9e6ea932) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 9'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000002cef37bc2ff105759920c58d40c2c5a783344cf65957f5b7b20202ad6) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 10'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000000000000277e3ffdb5d3f5b8e51f7644c1769d00a6c12e2a0df0575be9) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 11'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000000000000000000022b5f63f23d9f810321d637c18030d9c5ea9bd441ae) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 12'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000000000000000000000000001e81ed6e81d2f5318b8cf8953348efeaa31f) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 13'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x000000000000000000000000000000000001ad031af04040fd02dc96ce4a7433) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 14'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x0000000000000000000000000000000000000000001790fbab9ad7ed189c6968) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 15'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x000000000000000000000000000000000000000000000000014b66d328326678) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 16'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x00000000000000000000000000000000000000000000000000000000123455fa) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 17'); + (x,y) = field3.div512(x,y,0x12345678); + require( + (x == 0x0000000000000000000000000000000000000000000000000000000000000000) && + (y == 0x0000000000000000000000000000000000000000000000000000000000000000), 'fails at 18'); + } + +} diff --git a/contracts/test/algebra/uint512.sol b/contracts/test/algebra/uint512.sol new file mode 100644 index 0000000..5936ff0 --- /dev/null +++ b/contracts/test/algebra/uint512.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//---------------------------------------------------------------------------// +pragma solidity ^0.8.0; + +import '../../../contracts/algebra/uint512.sol'; +import 'hardhat/console.sol'; + +contract test_uint512 { + + uint256 constant not0 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + function test_create() public pure { + uint512.uint512_t memory a = uint512.uint512_t(0x1, 0); + require(a.l == 0x1 && a.h == 0, "Creation from single uint256 failed"); + a = uint512.uint512_t(0x2, 0x3); + require(a.l == 0x2 && a.h == 0x3, "Creation from two uint256 failed"); + } + + function test_add() public pure { + uint512.uint512_t memory a = uint512.uint512_t(0x12345, 0); + uint512.uint512_t memory b = uint512.uint512_t(0x23456, 0); + a = uint512.add(a, b); + require(a.l == 0x3579b && a.h == 0, "Addition does not work"); + + a = uint512.uint512_t(0, 0x12345); + b = uint512.uint512_t(0, 0x23456); + a = uint512.add(a, b); + require(a.h == 0x3579b && a.l == 0, "Addition does not work"); + + a = uint512.uint512_t(not0, 0x12345); + b = uint512.uint512_t(1, 0); + a = uint512.add(a, b); + require(a.l == 0 && a.h == 0x12346, "Addition with wrapping does not work"); + + a = uint512.uint512_t(not0, not0); + b = uint512.uint512_t(1, 0); + a = uint512.add(a, b); + require(a.l == 0 && a.h == 0, "Addition with overflow does not work"); + + a = uint512.uint512_t(not0, not0); + b = uint512.uint512_t(not0, not0); + a = uint512.add(a, b); + b = uint512.uint512_t(2, 0); + a = uint512.add(a, b); + require(a.l == 0 && a.h == 0, "Addition with overflow does not work"); + } + + function test_sub() public pure { + uint512.uint512_t memory a = uint512.uint512_t(0x12345, 0); + uint512.uint512_t memory b = uint512.uint512_t(0x23456, 0); + a = uint512.sub(b, a); + require(a.l == 0x11111 && a.h == 0, "Subtraction does not work"); + + a = uint512.uint512_t(0, 0x12345); + b = uint512.uint512_t(0, 0x23456); + a = uint512.sub(b, a); + require(a.h == 0x11111 && a.l == 0, "Subtraction does not work"); + + a = uint512.uint512_t(0, 0x12345); + b = uint512.uint512_t(1, 0); + a = uint512.sub(a, b); + require(a.l == not0 && a.h == 0x12344, "Subtraction with wrapping does not work"); + + a = uint512.uint512_t(1, 0); + b = uint512.uint512_t(not0, not0); + a = uint512.sub(a, b); + require(a.l == 2 && a.h == 0, "Subtraction with borrow does not work"); + + a = uint512.uint512_t(0, 0); + b = uint512.uint512_t(1, 0); + a = uint512.sub(a, b); + require(a.l == not0 && a.h == not0, "Subtraction with borrow does not work"); + } + + function test_mul() public pure { + + uint512.uint512_t memory a = uint512.mul(0x12345, 0x23456); + require(a.l == 0x28215dd2e && a.h == 0, "Multiplication does not work"); + + a = uint512.mul(not0, not0); + require(a.l == 1 && a.h == not0-1, "Multiplication does not work"); + + a = uint512.uint512_t(not0, not0); + a = uint512.mul(a, not0); + require(a.l == 1 && a.h == not0, "Multiplication does not work"); + + uint512.uint512_t memory b = uint512.uint512_t(not0, not0); + a = uint512.uint512_t(not0, not0); + a = uint512.mul(a, b); + require(a.l == 1 && a.h == 0, "Multiplication does not work"); + } +} diff --git a/deploy/test-algebra.js b/deploy/test-algebra.js index a068340..8ddf1af 100644 --- a/deploy/test-algebra.js +++ b/deploy/test-algebra.js @@ -6,6 +6,26 @@ module.exports = async function() { const {deploy} = deployments; const {deployer, tokenOwner} = await getNamedAccounts(); + await deploy('test_2pi', { + from: deployer, + log : true, + }); + + await deploy('test_field3', { + from: deployer, + log : true, + }); + + await deploy('field3_gas_estimation', { + from: deployer, + log : true, + }); + + await deploy('test_uint512', { + from: deployer, + log : true, + }); + await deploy('TestFieldMath', { from: deployer, log : true, diff --git a/hardhat.config.ts b/hardhat.config.ts index af6619a..ee1b1a9 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -5,6 +5,8 @@ require("hardhat-deploy"); require('hardhat-deploy-ethers'); require('hardhat-contract-sizer'); +import "hardhat-gas-reporter" + import './tasks/modular-test' const SEPOLIA_PRIVATE_KEY="SEPOLIA_PRIVATE_KEY" @@ -41,4 +43,7 @@ module.exports = { apiKey: ETHERSCAN_KEY, }, allowUnlimitedContractSize: true, + gasReporter : { + enabled: true + } }; diff --git a/package.json b/package.json index 4bd6868..902c546 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "hardhat": "^2.14.0", "hardhat-deploy": "^0.11.25", "hardhat-deploy-ethers": "0.4.0-next.1", + "hardhat-gas-reporter": "^1.0.9", "json5": "^2.2.1", "lossless-json": "^2.0.9", "solpp": "^0.11.5", diff --git a/test/algebra-test.ts b/test/algebra-test.ts new file mode 100644 index 0000000..48006e7 --- /dev/null +++ b/test/algebra-test.ts @@ -0,0 +1,100 @@ +const { + time, + loadFixture, +} = require("@nomicfoundation/hardhat-network-helpers"); +const {anyValue} = require("@nomicfoundation/hardhat-chai-matchers/withArgs"); +const {expect} = require("chai"); +const hre = require('hardhat') +const fs = require("fs"); +const path = require("path"); +const {BigNumber} = require("ethers"); +const {getNamedAccounts} = hre +const losslessJSON = require("lossless-json") + +import { Field3_gas_estiamtion, Field3_gas_estimation__factory } from "../typechain-types"; + +/* global BigInt */ + +describe('Algebra test', async function () { + const {deployments, getNamedAccounts} = hre; + const {deploy} = deployments; + + let fge : Field3_gas_estimation; + let owner; + + before(async () => { + const signers = await ethers.getSigners(); + owner = signers[0]; + fge = await new Field3_gas_estimation__factory().connect(owner).deploy(); + }); + + it("field3 gas measure", async function () { + //await deployments.fixture(['testFieldMathFixture']); + //const field3 = await ethers.getContract('field3_gas_estimation'); + //console.log(field3); + const runs = 20_000; + await fge.test_add(runs, {gasLimit: 30_500_000}); + await fge.test_sub(runs, {gasLimit: 30_500_000}); + //console.log("⛽Gas used: ", receipt.gasUsed.toNumber()); + /* + await field3.test_sub(runs, {gasLimit: 30_500_000}); + await field3.test_mul({gasLimit: 30_500_000}); + */ + }); + + it("field3 test", async function () { + await deployments.fixture(['testFieldMathFixture']); + const field3 = await ethers.getContract('test_field3'); + /* + await field3.test_add({gasLimit: 30_500_000}); + await field3.test_sub({gasLimit: 30_500_000}); + await field3.test_mul({gasLimit: 30_500_000}); + */ + await field3.test_mulmod_p381({gasLimit: 30_500_000}); + console.log("field3 tests passed"); + }); + + it("2pi test", async function () { + await deployments.fixture(['testFieldMathFixture']); + const field3 = await ethers.getContract('test_2pi'); + await field3.test_div256({gasLimit: 30_500_000}); + await field3.test_mod256({gasLimit: 30_500_000}); + await field3.test_div512({gasLimit: 30_500_000}); + }); + + + it("uint512 test", async function () { + await deployments.fixture(['testFieldMathFixture']); + const test_uint512 = await ethers.getContract('test_uint512'); + await test_uint512.test_create({gasLimit: 30_500_000}); + await test_uint512.test_add({gasLimit: 30_500_000}); + await test_uint512.test_sub({gasLimit: 30_500_000}); + await test_uint512.test_mul({gasLimit: 30_500_000}); + }); + + it("Field math", async function () { + await deployments.fixture(['testFieldMathFixture']); + let fieldMath = await ethers.getContract('TestFieldMath'); + await fieldMath.test_log2_ceil({gasLimit: 30_500_000}); + }); + + it("Transcript", async function () { + await deployments.fixture(['testTranscriptFixture']); + let testTranscript = await ethers.getContract('TestTranscript'); + await testTranscript.test_transcript({gasLimit: 30_500_000}); + }); + + it("Polynomial", async function () { + await deployments.fixture(['testFieldMathFixture']); + let testPolynomial = await ethers.getContract('TestPolynomial'); + await testPolynomial.test_polynomial_evaluation_aDeg15_bDeg20({gasLimit: 30_500_000}); + await testPolynomial.test_polynomial_addition_aDeg15_bDeg20({gasLimit: 30_500_000}); + await testPolynomial.test_polynomial_multiplication_aDeg15_bDeg20({gasLimit: 30_500_000}); + await testPolynomial.test_lagrange_interpolation_by_2_points_neg_x({gasLimit: 30_500_000}); + await testPolynomial.test_lagrange_interpolation_by_2_points1({gasLimit: 30_500_000}); + await testPolynomial.test_lagrange_interpolation_by_2_points2({gasLimit: 30_500_000}); + await testPolynomial.test_lagrange_interpolate_then_evaluate_by_2_points({gasLimit: 30_500_000}); + // This test goes out of gas / times out + //await testPolynomial.test_lagrange_interpolation({gasLimit: 80_500_000}); + }); +})