From df9386b487aceaef9049c255e7d046cfd60be839 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Feb 2017 20:09:24 +0100 Subject: [PATCH 01/10] Add snark primitives and pairing support --- CMakeLists.txt | 3 +- eth/main.cpp | 2 + libdevcrypto/CMakeLists.txt | 2 +- libdevcrypto/Exceptions.h | 1 + libdevcrypto/LibSnark.cpp | 222 +++++++++++ libdevcrypto/LibSnark.h | 37 ++ libethashseal/genesis/mainNetwork.cpp | 3 + libethashseal/genesis/metropolisTest.cpp | 6 +- libethcore/Precompiled.cpp | 46 +-- test/libdevcrypto/LibSnark.cpp | 470 +++++++++++++++++++++++ 10 files changed, 749 insertions(+), 43 deletions(-) create mode 100644 libdevcrypto/LibSnark.cpp create mode 100644 libdevcrypto/LibSnark.h create mode 100644 test/libdevcrypto/LibSnark.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4596748bf7d..aa380ee873b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,8 +60,7 @@ include(ProjectCryptopp) include(ProjectJsonCpp) include(ProjectJsonRpcCpp) include(ProjectSecp256k1) - - +include(deps/snark.cmake) configure_project() diff --git a/eth/main.cpp b/eth/main.cpp index 049766fd61c..8c4bc2eb140 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -43,6 +43,8 @@ #include #include +#include + #include #include #include diff --git a/libdevcrypto/CMakeLists.txt b/libdevcrypto/CMakeLists.txt index a9f74928203..425937f709d 100644 --- a/libdevcrypto/CMakeLists.txt +++ b/libdevcrypto/CMakeLists.txt @@ -6,4 +6,4 @@ add_library(devcrypto ${SOURCES} ${HEADERS}) find_package(Utils) target_include_directories(devcrypto PRIVATE ..) target_include_directories(devcrypto PRIVATE ../utils) -target_link_libraries(devcrypto Secp256k1 Cryptopp ${Utils_SCRYPT_LIBRARIES} devcore) +target_link_libraries(devcrypto Secp256k1 Cryptopp snark ${Utils_SCRYPT_LIBRARIES} devcore) diff --git a/libdevcrypto/Exceptions.h b/libdevcrypto/Exceptions.h index 858374bda8a..bece7c51574 100644 --- a/libdevcrypto/Exceptions.h +++ b/libdevcrypto/Exceptions.h @@ -30,6 +30,7 @@ namespace crypto /// Rare malfunction of cryptographic functions. DEV_SIMPLE_EXCEPTION(CryptoException); +DEV_SIMPLE_EXCEPTION(InvalidEncoding); } } diff --git a/libdevcrypto/LibSnark.cpp b/libdevcrypto/LibSnark.cpp new file mode 100644 index 00000000000..5b23876e31e --- /dev/null +++ b/libdevcrypto/LibSnark.cpp @@ -0,0 +1,222 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** + * @file LibSnark.cpp + */ + +#include +// libsnark has to be compiled with exactly the same switches: +// sudo PREFIX=/usr/local make NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 CURVE=ALT_BN128 FEATUREFLAGS="-DBINARY_OUTPUT=1 -DMONTGOMERY_OUTPUT=1 -DNO_PT_COMPRESSION=1" lib install +#define BINARY_OUTPUT 1 +#define MONTGOMERY_OUTPUT 1 +#define NO_PT_COMPRESSION 1 + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::crypto; + +namespace +{ + +void initLibSnark() +{ + static bool initialized = 0; + if (!initialized) + { + libsnark::alt_bn128_pp::init_public_params(); + // Otherwise the library would output profiling info for each run. + libsnark::inhibit_profiling_counters = true; + libsnark::inhibit_profiling_info = true; + initialized = true; + } +} + +libsnark::bigint toLibsnarkBigint(h256 const& _x) +{ + libsnark::bigint x; + for (unsigned i = 0; i < 4; i++) + for (unsigned j = 0; j < 8; j++) + x.data[3 - i] |= uint64_t(_x[i * 8 + j]) << (8 * (7 - j)); + return x; +} + +h256 fromLibsnarkBigint(libsnark::bigint _x) +{ + h256 x; + for (unsigned i = 0; i < 4; i++) + for (unsigned j = 0; j < 8; j++) + x[i * 8 + j] = uint8_t(uint64_t(_x.data[3 - i]) >> (8 * (7 - j))); + return x; +} + +libsnark::alt_bn128_Fq decodeFqElement(dev::bytesConstRef _data) +{ + // h256::AlignLeft ensures that the h256 is zero-filled on the right if _data + // is too short. + h256 xbin(_data, h256::AlignLeft); + if (u256(xbin) >= u256(fromLibsnarkBigint(libsnark::alt_bn128_Fq::mod))) + BOOST_THROW_EXCEPTION(InvalidEncoding()); + return toLibsnarkBigint(xbin); +} + +libsnark::alt_bn128_G1 decodePointG1(dev::bytesConstRef _data) +{ + libsnark::alt_bn128_Fq X = decodeFqElement(_data.cropped(0)); + libsnark::alt_bn128_Fq Y = decodeFqElement(_data.cropped(32)); + if (X == libsnark::alt_bn128_Fq::zero() && Y == libsnark::alt_bn128_Fq::zero()) + return libsnark::alt_bn128_G1::zero(); + libsnark::alt_bn128_G1 p(X, Y, libsnark::alt_bn128_Fq::one()); + if (!p.is_well_formed()) + BOOST_THROW_EXCEPTION(InvalidEncoding()); + return p; +} + +bytes encodePointG1(libsnark::alt_bn128_G1 _p) +{ + if (_p.is_zero()) + return h256().asBytes() + h256().asBytes(); + _p.to_affine_coordinates(); + return + fromLibsnarkBigint(_p.X.as_bigint()).asBytes() + + fromLibsnarkBigint(_p.Y.as_bigint()).asBytes(); +} + +libsnark::alt_bn128_Fq2 decodeFq2Element(dev::bytesConstRef _data) +{ + // Encoding: c1 (256 bits) c0 (256 bits) + // "Big endian", just like the numbers + return libsnark::alt_bn128_Fq2( + decodeFqElement(_data.cropped(32)), + decodeFqElement(_data.cropped(0)) + ); +} + + +libsnark::alt_bn128_G2 decodePointG2(dev::bytesConstRef _data) +{ + libsnark::alt_bn128_Fq2 X = decodeFq2Element(_data); + libsnark::alt_bn128_Fq2 Y = decodeFq2Element(_data.cropped(64)); + if (X == libsnark::alt_bn128_Fq2::zero() && Y == libsnark::alt_bn128_Fq2::zero()) + return libsnark::alt_bn128_G2::zero(); + libsnark::alt_bn128_G2 p(X, Y, libsnark::alt_bn128_Fq2::one()); + if (!p.is_well_formed()) + BOOST_THROW_EXCEPTION(InvalidEncoding()); + return p; +} + +} + +pair dev::crypto::alt_bn128_pairing_product(dev::bytesConstRef _in) +{ + // Input: list of pairs of G1 and G2 points + // Output: 1 if pairing evaluates to 1, 0 otherwise (left-padded to 32 bytes) + + bool result = true; + size_t const pairSize = 2 * 32 + 2 * 64; + size_t const pairs = _in.size() / pairSize; + if (pairs * pairSize != _in.size()) + { + // Invalid length. + return make_pair(false, bytes()); + } + try + { + initLibSnark(); + libsnark::alt_bn128_Fq12 x = libsnark::alt_bn128_Fq12::one(); + for (size_t i = 0; i < pairs; ++i) + { + dev::bytesConstRef pair = _in.cropped(i * pairSize, pairSize); + libsnark::alt_bn128_G2 p = decodePointG2(pair.cropped(2 * 32)); + if (-libsnark::alt_bn128_G2::scalar_field::one() * p + p != libsnark::alt_bn128_G2::zero()) + // p is not an element of the group (has wrong order) + return {false, bytes()}; + x = x * libsnark::alt_bn128_miller_loop( + libsnark::alt_bn128_precompute_G1(decodePointG1(pair)), + libsnark::alt_bn128_precompute_G2(p) + ); + } + result = libsnark::alt_bn128_final_exponentiation(x) == libsnark::alt_bn128_GT::one(); + } + catch (InvalidEncoding const&) + { + return make_pair(false, bytes()); + } + catch (...) + { + cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; + return make_pair(false, bytes()); + } + + bytes res(32, 0); + res[31] = unsigned(result); + return {true, res}; +} + +pair dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in) +{ + try + { + initLibSnark(); + libsnark::alt_bn128_G1 p1 = decodePointG1(_in); + libsnark::alt_bn128_G1 p2 = decodePointG1(_in.cropped(32 * 2)); + + return {true, encodePointG1(p1 + p2)}; + } + catch (InvalidEncoding const&) + { + } + catch (...) + { + cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; + } + return make_pair(false, bytes()); +} + +pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) +{ + try + { + initLibSnark(); + libsnark::alt_bn128_G1 p = decodePointG1(_in.cropped(0)); + + libsnark::alt_bn128_G1 result = toLibsnarkBigint(h256(_in.cropped(64), h256::AlignLeft)) * p; + + return {true, encodePointG1(result)}; + } + catch (InvalidEncoding const&) + { + } + catch (...) + { + cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; + } + return make_pair(false, bytes()); +} diff --git a/libdevcrypto/LibSnark.h b/libdevcrypto/LibSnark.h new file mode 100644 index 00000000000..92c2f721706 --- /dev/null +++ b/libdevcrypto/LibSnark.h @@ -0,0 +1,37 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** + * @file LibSnark.h + */ + +#pragma once + +#include + +namespace dev +{ +namespace crypto +{ + +std::pair alt_bn128_pairing_product(bytesConstRef _in); +std::pair alt_bn128_G1_add(bytesConstRef _in); +std::pair alt_bn128_G1_mul(bytesConstRef _in); + +void exportVK(std::string const& _VKFilename); + +} +} diff --git a/libethashseal/genesis/mainNetwork.cpp b/libethashseal/genesis/mainNetwork.cpp index 074698290d8..b16cdc15b0a 100644 --- a/libethashseal/genesis/mainNetwork.cpp +++ b/libethashseal/genesis/mainNetwork.cpp @@ -55,6 +55,9 @@ R"E( "0000000000000000000000000000000000000002": { "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, "0000000000000000000000000000000000000003": { "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, "0000000000000000000000000000000000000004": { "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000006": { "precompiled": { "name": "alt_bn128_G1_add", "startingBlock": "0xffffffffffffffffff", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000007": { "precompiled": { "name": "alt_bn128_G1_mul", "startingBlock": "0xffffffffffffffffff", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000008": { "precompiled": { "name": "alt_bn128_pairing_product", "startingBlock": "0xffffffffffffffffff", "linear": { "base": 15, "word": 3 } } }, "3282791d6fd713f1e94f4bfd565eaa78b3a0599d": { "balance": "1337000000000000000000" }, diff --git a/libethashseal/genesis/metropolisTest.cpp b/libethashseal/genesis/metropolisTest.cpp index 1f42c7b8391..3f6dc3216f5 100644 --- a/libethashseal/genesis/metropolisTest.cpp +++ b/libethashseal/genesis/metropolisTest.cpp @@ -56,7 +56,9 @@ R"E( "0000000000000000000000000000000000000002": { "wei": "1", "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, "0000000000000000000000000000000000000003": { "wei": "1", "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, "0000000000000000000000000000000000000004": { "wei": "1", "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, - "0000000000000000000000000000000000000005": { "wei": "1", "precompiled": { "name": "modexp" } } - } + "0000000000000000000000000000000000000006": { "wei": "1", "precompiled": { "name": "alt_bn128_G1_add", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000007": { "wei": "1", "precompiled": { "name": "alt_bn128_G1_mul", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000008": { "wei": "1", "precompiled": { "name": "alt_bn128_pairing_product", "linear": { "base": 15, "word": 3 } } }, + } } )E"; diff --git a/libethcore/Precompiled.cpp b/libethcore/Precompiled.cpp index c9a1c5735e7..61c8e6e9e32 100644 --- a/libethcore/Precompiled.cpp +++ b/libethcore/Precompiled.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include using namespace std; using namespace dev; @@ -97,50 +98,19 @@ ETH_REGISTER_PRECOMPILED(identity)(bytesConstRef _in) return {true, _in.toBytes()}; } -// Parse _count bytes of _in starting with _begin offset as big endian int. -// If there's not enough bytes in _in, consider it infinitely right-padded with zeroes. -bigint parseBigEndianRightPadded(bytesConstRef _in, size_t _begin, size_t _count) +ETH_REGISTER_PRECOMPILED(alt_bn128_pairing_product)(bytesConstRef _in) { - if (_begin > _in.count()) - return 0; - - // crop _in, not going beyond its size - bytesConstRef cropped = _in.cropped(_begin, min(_count, _in.count() - _begin)); - - bigint ret = fromBigEndian(cropped); - // shift as if we had right-padding zeroes - ret <<= 8 * (_count - cropped.count()); - - return ret; + return dev::crypto::alt_bn128_pairing_product(_in); } -ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) +ETH_REGISTER_PRECOMPILED(alt_bn128_G1_add)(bytesConstRef _in) { - size_t const baseLength(parseBigEndianRightPadded(_in, 0, 32)); - size_t const expLength(parseBigEndianRightPadded(_in, 32, 32)); - size_t const modLength(parseBigEndianRightPadded(_in, 64, 32)); - - bigint const base(parseBigEndianRightPadded(_in, 96, baseLength)); - bigint const exp(parseBigEndianRightPadded(_in, 96 + baseLength, expLength)); - bigint const mod(parseBigEndianRightPadded(_in, 96 + baseLength + expLength, modLength)); - - bigint const result = mod != 0 ? boost::multiprecision::powm(base, exp, mod) : bigint{0}; - - bytes ret(modLength); - toBigEndian(result, ret); - - return {true, ret}; + return dev::crypto::alt_bn128_G1_add(_in); } - -ETH_REGISTER_PRECOMPILED_PRICER(modexp)(bytesConstRef _in) +ETH_REGISTER_PRECOMPILED(alt_bn128_G1_mul)(bytesConstRef _in) { - bigint const baseLength(parseBigEndianRightPadded(_in, 0, 32)); - bigint const expLength(parseBigEndianRightPadded(_in, 32, 32)); - bigint const modLength(parseBigEndianRightPadded(_in, 64, 32)); - - bigint const maxLength = max(modLength, baseLength); - - return maxLength * maxLength * max(expLength, 1) / 20; + return dev::crypto::alt_bn128_G1_mul(_in); } + } diff --git a/test/libdevcrypto/LibSnark.cpp b/test/libdevcrypto/LibSnark.cpp new file mode 100644 index 00000000000..8b300ab6c83 --- /dev/null +++ b/test/libdevcrypto/LibSnark.cpp @@ -0,0 +1,470 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file LibSnark.cpp + * @date 2017 + * Tests for libsnark integration. + */ + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::crypto; + +BOOST_AUTO_TEST_SUITE(LibSnark) + +namespace dev +{ +namespace test +{ + +namespace +{ + +static u256 groupOrder = u256("21888242871839275222246405745257275088548364400416034343698204186575808495617"); + +pair ecmul_helper(bytes const& _a, u256 const& _scalar) +{ + bytes input = _a + toBigEndian(_scalar); + return alt_bn128_G1_mul(ref(input)); +} + +pair ecadd_helper(bytes const& _a, bytes const& _b) +{ + bytes input = _a + _b; + return alt_bn128_G1_add(ref(input)); +} + +pair pairingprod_helper(bytes const& _input) +{ + return alt_bn128_pairing_product(ref(_input)); +} + +bytes negateG1(bytes const& _input) +{ + auto ret = ecmul_helper(_input, groupOrder - 1); + BOOST_REQUIRE(ret.first); + return ret.second; +} + +bytes addG1(bytes const& _x, bytes const& _y) +{ + auto ret = ecadd_helper(_x, _y); + BOOST_REQUIRE(ret.first); + return ret.second; +} + +} + +BOOST_AUTO_TEST_CASE(ecadd) +{ + // "0 + 0 == 0" + bytes input(0x20 * 4, 0); + bytes expectation(0x20 * 2, 0); + auto result = alt_bn128_G1_add(ref(input)); + BOOST_CHECK(result.first); + BOOST_CHECK(result.second == expectation); + // The same, truncated. + bytes empty; + result = alt_bn128_G1_add(ref(empty)); + BOOST_CHECK(result.first); + BOOST_CHECK(result.second == expectation); +} + +BOOST_AUTO_TEST_CASE(invalid) +{ + bytes x = + toBigEndian(u256("6851077925310461602867742977619883934042581405263014789956638244065803308498")) + + toBigEndian(u256("10336382210592135525880811046708757754106524561907815205241508542912494488506")); + bytes invalid = x; + invalid[3] ^= 1; + + bytes input = x + invalid; + // This should fail because the point is not on the curve + BOOST_CHECK(!ecadd_helper(x, invalid).first); + BOOST_CHECK(!ecadd_helper(invalid, bytes()).first); + // truncated, but valid + BOOST_CHECK(ecadd_helper(x, bytes()).first); + BOOST_CHECK(ecadd_helper(x, bytes()).second == x); +} + +BOOST_AUTO_TEST_CASE(ecmul_add) +{ + bytes x = + toBigEndian(u256("6851077925310461602867742977619883934042581405263014789956638244065803308498")) + + toBigEndian(u256("10336382210592135525880811046708757754106524561907815205241508542912494488506")); + BOOST_CHECK(ecadd_helper(x, x).first); + BOOST_CHECK(ecmul_helper(x, u256(2)).first); + // x + x == x * 2 + BOOST_CHECK(ecadd_helper(x, x).second == ecmul_helper(x, u256(2)).second); + // x * -1 + x == 0 + BOOST_CHECK(ecmul_helper(x, groupOrder - 1).first); + BOOST_CHECK(ecadd_helper(ecmul_helper(x, groupOrder - 1).second, x).second == bytes(0x40, 0)); +} + +BOOST_AUTO_TEST_CASE(pairing) +{ + // This verifies a full zkSNARK proof. Let's see if this hocus-pocus actually works... + struct VK { + bytes A; + bytes B; + bytes C; + bytes gamma; + bytes gammaBeta1; + bytes gammaBeta2; + bytes Z; + vector IC; + } vk; + vk.A = + toBigEndian(u256("0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7")) + + toBigEndian(u256("0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678")) + + toBigEndian(u256("0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d")) + + toBigEndian(u256("0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550")); + vk.B = + toBigEndian(u256("0x2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc02")) + + toBigEndian(u256("0x03d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db84")); + vk.C = + toBigEndian(u256("0x2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb")) + + toBigEndian(u256("0x01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb3")) + + toBigEndian(u256("0x14a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713")) + + toBigEndian(u256("0x178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee24590")); + vk.gamma = + toBigEndian(u256("0x25f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb1")) + + toBigEndian(u256("0x22acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d")) + + toBigEndian(u256("0x065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf68")) + + toBigEndian(u256("0x06d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb")); + vk.gammaBeta1 = + toBigEndian(u256("0x15794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f21")) + + toBigEndian(u256("0x14db745c6780e9df549864cec19c2daf4531f6ec0c89cc1c7436cc4d8d300c6d")); + vk.gammaBeta2 = + toBigEndian(u256("0x1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e")) + + toBigEndian(u256("0x283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39")) + + toBigEndian(u256("0x140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e")) + + toBigEndian(u256("0x0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd4")); + vk.Z = + toBigEndian(u256("0x217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac29")) + + toBigEndian(u256("0x0a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c")) + + toBigEndian(u256("0x26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a9855")) + + toBigEndian(u256("0x2fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d7")); + vk.IC.push_back( + toBigEndian(u256("0x0aee46a7ea6e80a3675026dfa84019deee2a2dedb1bbe11d7fe124cb3efb4b5a")) + + toBigEndian(u256("0x044747b6e9176e13ede3a4dfd0d33ccca6321b9acd23bf3683a60adc0366ebaf")) + ); + vk.IC.push_back( + toBigEndian(u256("0x1e39e9f0f91fa7ff8047ffd90de08785777fe61c0e3434e728fce4cf35047ddc")) + + toBigEndian(u256("0x2e0b64d75ebfa86d7f8f8e08abbe2e7ae6e0a1c0b34d028f19fa56e9450527cb")) + ); + vk.IC.push_back( + toBigEndian(u256("0x1c36e713d4d54e3a9644dffca1fc524be4868f66572516025a61ca542539d43f")) + + toBigEndian(u256("0x042dcc4525b82dfb242b09cb21909d5c22643dcdbe98c4d082cc2877e96b24db")) + ); + vk.IC.push_back( + toBigEndian(u256("0x17d5d09b4146424bff7e6fb01487c477bbfcd0cdbbc92d5d6457aae0b6717cc5")) + + toBigEndian(u256("0x02b5636903efbf46db9235bbe74045d21c138897fda32e079040db1a16c1a7a1")) + ); + vk.IC.push_back( + toBigEndian(u256("0x0f103f14a584d4203c27c26155b2c955f8dfa816980b24ba824e1972d6486a5d")) + + toBigEndian(u256("0x0c4165133b9f5be17c804203af781bcf168da7386620479f9b885ecbcd27b17b")) + ); + vk.IC.push_back( + toBigEndian(u256("0x232063b584fb76c8d07995bee3a38fa7565405f3549c6a918ddaa90ab971e7f8")) + + toBigEndian(u256("0x2ac9b135a81d96425c92d02296322ad56ffb16299633233e4880f95aafa7fda7")) + ); + vk.IC.push_back( + toBigEndian(u256("0x09b54f111d3b2d1b2fe1ae9669b3db3d7bf93b70f00647e65c849275de6dc7fe")) + + toBigEndian(u256("0x18b2e77c63a3e400d6d1f1fbc6e1a1167bbca603d34d03edea231eb0ab7b14b4")) + ); + vk.IC.push_back( + toBigEndian(u256("0x0c54b42137b67cc268cbb53ac62b00ecead23984092b494a88befe58445a244a")) + + toBigEndian(u256("0x18e3723d37fae9262d58b548a0575f59d9c3266db7afb4d5739555837f6b8b3e")) + ); + vk.IC.push_back( + toBigEndian(u256("0x0a6de0e2240aa253f46ce0da883b61976e3588146e01c9d8976548c145fe6e4a")) + + toBigEndian(u256("0x04fbaa3a4aed4bb77f30ebb07a3ec1c7d77a7f2edd75636babfeff97b1ea686e")) + ); + vk.IC.push_back( + toBigEndian(u256("0x111e2e2a5f8828f80ddad08f9f74db56dac1cc16c1cb278036f79a84cf7a116f")) + + toBigEndian(u256("0x1d7d62e192b219b9808faa906c5ced871788f6339e8d91b83ac1343e20a16b30")) + ); + struct Proof { + bytes A; + bytes Ap; + bytes B; + bytes Bp; + bytes C; + bytes Cp; + bytes H; + bytes K; + } proof; + proof.A = + toBigEndian(u256("12873740738727497448187997291915224677121726020054032516825496230827252793177")) + + toBigEndian(u256("21804419174137094775122804775419507726154084057848719988004616848382402162497")); + proof.Ap = + toBigEndian(u256("7742452358972543465462254569134860944739929848367563713587808717088650354556")) + + toBigEndian(u256("7324522103398787664095385319014038380128814213034709026832529060148225837366")); + proof.B = + toBigEndian(u256("8176651290984905087450403379100573157708110416512446269839297438960217797614")) + + toBigEndian(u256("15588556568726919713003060429893850972163943674590384915350025440408631945055")) + + toBigEndian(u256("15347511022514187557142999444367533883366476794364262773195059233657571533367")) + + toBigEndian(u256("4265071979090628150845437155927259896060451682253086069461962693761322642015")); + proof.Bp = + toBigEndian(u256("2979746655438963305714517285593753729335852012083057917022078236006592638393")) + + toBigEndian(u256("6470627481646078059765266161088786576504622012540639992486470834383274712950")); + proof.C = + toBigEndian(u256("6851077925310461602867742977619883934042581405263014789956638244065803308498")) + + toBigEndian(u256("10336382210592135525880811046708757754106524561907815205241508542912494488506")); + proof.Cp = + toBigEndian(u256("12491625890066296859584468664467427202390981822868257437245835716136010795448")) + + toBigEndian(u256("13818492518017455361318553880921248537817650587494176379915981090396574171686")); + proof.H = + toBigEndian(u256("12091046215835229523641173286701717671667447745509192321596954139357866668225")) + + toBigEndian(u256("14446807589950902476683545679847436767890904443411534435294953056557941441758")); + proof.K = + toBigEndian(u256("21341087976609916409401737322664290631992568431163400450267978471171152600502")) + + toBigEndian(u256("2942165230690572858696920423896381470344658299915828986338281196715687693170")); + vector input; + input.push_back(u256("13986731495506593864492662381614386532349950841221768152838255933892789078521")); + input.push_back(u256("622860516154313070522697309645122400675542217310916019527100517240519630053")); + input.push_back(u256("11094488463398718754251685950409355128550342438297986977413505294941943071569")); + input.push_back(u256("6627643779954497813586310325594578844876646808666478625705401786271515864467")); + input.push_back(u256("2957286918163151606545409668133310005545945782087581890025685458369200827463")); + input.push_back(u256("1384290496819542862903939282897996566903332587607290986044945365745128311081")); + input.push_back(u256("5613571677741714971687805233468747950848449704454346829971683826953541367271")); + input.push_back(u256("9643208548031422463313148630985736896287522941726746581856185889848792022807")); + input.push_back(u256("18066496933330839731877828156604")); + + bytes P2 = + toBigEndian(u256("11559732032986387107991004021392285783925812861821192530917403151452391805634")) + + toBigEndian(u256("10857046999023057135944570762232829481370756359578518086990519993285655852781")) + + toBigEndian(u256("4082367875863433681332203403145435568316851327593401208105741076214120093531")) + + toBigEndian(u256("8495653923123431417604973247489272438418190587263600148770280649306958101930")); + + pair ret; + // Compute the linear combination vk_x + bytes vkx = toBigEndian(u256(0)) + toBigEndian(u256(0)); + for (size_t i = 0; i < input.size(); ++i) + { + ret = ecmul_helper(vk.IC[i + 1], input[i]); + BOOST_REQUIRE(ret.first); + ret = ecadd_helper(vkx, ret.second); + BOOST_REQUIRE(ret.first); + vkx = ret.second; + } + ret = ecadd_helper(vkx, vk.IC[0]); + BOOST_REQUIRE(ret.first); + vkx = ret.second; + + // Now run the pairing checks. + ret = pairingprod_helper(proof.A + vk.A + negateG1(proof.Ap) + P2); + BOOST_REQUIRE(ret.first); + BOOST_REQUIRE(ret.second == toBigEndian(u256(1))); + ret = pairingprod_helper(vk.B + proof.B + negateG1(proof.Bp) + P2); + BOOST_REQUIRE(ret.first); + BOOST_REQUIRE(ret.second == toBigEndian(u256(1))); + ret = pairingprod_helper(proof.C + vk.C + negateG1(proof.Cp) + P2); + BOOST_REQUIRE(ret.first); + BOOST_REQUIRE(ret.second == toBigEndian(u256(1))); + ret = pairingprod_helper( + proof.K + vk.gamma + + negateG1(addG1(vkx, addG1(proof.A, proof.C))) + vk.gammaBeta2 + + negateG1(vk.gammaBeta1) + proof.B + ); + BOOST_REQUIRE(ret.first); + BOOST_REQUIRE(ret.second == toBigEndian(u256(1))); + ret = pairingprod_helper( + addG1(vkx, proof.A) + proof.B + + negateG1(proof.H) + vk.Z + + negateG1(proof.C) + P2 + ); + BOOST_REQUIRE(ret.first); + BOOST_REQUIRE(ret.second == toBigEndian(u256(1))); + + // Just for the fun of it, try a wrong check. + ret = pairingprod_helper(proof.A + vk.A + proof.Ap + P2); + BOOST_REQUIRE(ret.first); + BOOST_REQUIRE(ret.second == toBigEndian(u256(0))); +} + +BOOST_AUTO_TEST_SUITE_END() + +#if 0 + +std::string outputPointG1Affine(libsnark::alt_bn128_G1 _p) +{ + libsnark::alt_bn128_G1 aff = _p; + aff.to_affine_coordinates(); + return + "Pairing.g1FromAffine(0x" + + fromLibsnarkBigint(aff.X.as_bigint()).hex() + + ", 0x" + + fromLibsnarkBigint(aff.Y.as_bigint()).hex() + + ")"; +} + +std::string outputPointG2Affine(libsnark::alt_bn128_G2 _p) +{ + libsnark::alt_bn128_G2 aff = _p; + aff.to_affine_coordinates(); + return + "Pairing.g2FromAffine([0x" + + fromLibsnarkBigint(aff.X.c1.as_bigint()).hex() + ", 0x" + + fromLibsnarkBigint(aff.X.c0.as_bigint()).hex() + "], [0x" + + fromLibsnarkBigint(aff.Y.c1.as_bigint()).hex() + ", 0x" + + fromLibsnarkBigint(aff.Y.c0.as_bigint()).hex() + "])"; +} + + +void testProof(libsnark::r1cs_ppzksnark_verification_key const& _vk) +{ + using ppT = libsnark::alt_bn128_pp; + using Fq = libsnark::alt_bn128_Fq; + using Fq2 = libsnark::alt_bn128_Fq2; + using G1 = libsnark::alt_bn128_G1; + using G2 = libsnark::alt_bn128_G2; + libsnark::r1cs_ppzksnark_primary_input primary_input; + libsnark::r1cs_ppzksnark_proof proof; + + primary_input.emplace_back("13986731495506593864492662381614386532349950841221768152838255933892789078521"); + primary_input.emplace_back("622860516154313070522697309645122400675542217310916019527100517240519630053"); + primary_input.emplace_back("11094488463398718754251685950409355128550342438297986977413505294941943071569"); + primary_input.emplace_back("6627643779954497813586310325594578844876646808666478625705401786271515864467"); + primary_input.emplace_back("2957286918163151606545409668133310005545945782087581890025685458369200827463"); + primary_input.emplace_back("1384290496819542862903939282897996566903332587607290986044945365745128311081"); + primary_input.emplace_back("5613571677741714971687805233468747950848449704454346829971683826953541367271"); + primary_input.emplace_back("9643208548031422463313148630985736896287522941726746581856185889848792022807"); + primary_input.emplace_back("18066496933330839731877828156604"); + + proof.g_A.g = G1( + Fq("12873740738727497448187997291915224677121726020054032516825496230827252793177"), + Fq("21804419174137094775122804775419507726154084057848719988004616848382402162497"), + Fq::one() + ); + proof.g_A.h = G1( + Fq("7742452358972543465462254569134860944739929848367563713587808717088650354556"), + Fq("7324522103398787664095385319014038380128814213034709026832529060148225837366"), + Fq::one() + ); + proof.g_B.g = G2( + Fq2( + Fq("15588556568726919713003060429893850972163943674590384915350025440408631945055"), + Fq("8176651290984905087450403379100573157708110416512446269839297438960217797614") + ), + Fq2( + Fq("4265071979090628150845437155927259896060451682253086069461962693761322642015"), + Fq("15347511022514187557142999444367533883366476794364262773195059233657571533367") + ), + Fq2::one() + ); + proof.g_B.h = G1( + Fq("2979746655438963305714517285593753729335852012083057917022078236006592638393"), + Fq("6470627481646078059765266161088786576504622012540639992486470834383274712950"), + Fq::one() + ); + proof.g_C.g = G1( + Fq("6851077925310461602867742977619883934042581405263014789956638244065803308498"), + Fq("10336382210592135525880811046708757754106524561907815205241508542912494488506"), + Fq::one() + ); + proof.g_C.h = G1( + Fq("12491625890066296859584468664467427202390981822868257437245835716136010795448"), + Fq("13818492518017455361318553880921248537817650587494176379915981090396574171686"), + Fq::one() + ); + proof.g_H = G1( + Fq("12091046215835229523641173286701717671667447745509192321596954139357866668225"), + Fq("14446807589950902476683545679847436767890904443411534435294953056557941441758"), + Fq::one() + ); + proof.g_K = G1( + Fq("21341087976609916409401737322664290631992568431163400450267978471171152600502"), + Fq("2942165230690572858696920423896381470344658299915828986338281196715687693170"), + Fq::one() + ); + + + cout << "Verifying..." << endl; + bool verified = libsnark::r1cs_ppzksnark_verifier_strong_IC(_vk, primary_input, proof); + cout << "Verified: " << verified << endl; +} + +void dev::crypto::exportVK(string const& _VKFilename) +{ + initLibSnark(); + + std::stringstream ss; + std::ifstream fh(_VKFilename, std::ios::binary); + + if (!fh.is_open()) + throw std::runtime_error("could not load param file at " + _VKFilename); + + ss << fh.rdbuf(); + fh.close(); + + ss.rdbuf()->pubseekpos(0, std::ios_base::in); + + libsnark::r1cs_ppzksnark_verification_key verificationKey; + ss >> verificationKey; + +// testProof(verificationKey); +// return; + + unsigned icLength = verificationKey.encoded_IC_query.rest.indices.size() + 1; + + + cout << "\tfunction verifyingKey() internal returns (VerifyingKey vk) {" << endl; + cout << "\t\tvk.A = " << outputPointG2Affine(verificationKey.alphaA_g2) << ";" << endl; + cout << "\t\tvk.B = " << outputPointG1Affine(verificationKey.alphaB_g1) << ";" << endl; + cout << "\t\tvk.C = " << outputPointG2Affine(verificationKey.alphaC_g2) << ";" << endl; + cout << "\t\tvk.gamma = " << outputPointG2Affine(verificationKey.gamma_g2) << ";" << endl; + cout << "\t\tvk.gammaBeta1 = " << outputPointG1Affine(verificationKey.gamma_beta_g1) << ";" << endl; + cout << "\t\tvk.gammaBeta2 = " << outputPointG2Affine(verificationKey.gamma_beta_g2) << ";" << endl; + cout << "\t\tvk.Z = " << outputPointG2Affine(verificationKey.rC_Z_g2) << ";" << endl; + cout << "\t\tvk.IC = new Pairing.G1Point[](" << icLength << ");" << endl; + cout << "\t\tvk.IC[0] = " << outputPointG1Affine(verificationKey.encoded_IC_query.first) << ";" << endl; + for (size_t i = 1; i < icLength; ++i) + { + auto vkICi = outputPointG1Affine(verificationKey.encoded_IC_query.rest.values[i - 1]); + cout << "\t\tvk.IC[" << i << "] = " << vkICi << ";" << endl; + } + cout << "\t\t}" << endl; + cout << "\tfunction verify(uint[] input, Proof proof) internal returns (bool) {" << endl; + cout << "\t\tVerifyingKey memory vk = verifyingKey();" << endl; + cout << "\t\tif (input.length + 1 != vk.IC.length) throw;" << endl; + cout << "\t\t// Compute the linear combination vk_x" << endl; + cout << "\t\tPairing.G1Point memory vk_x = vk.IC[0];" << endl; + for (size_t i = 0; i < verificationKey.encoded_IC_query.rest.indices.size(); ++i) + cout << "\t\tvk_x = Pairing.add(vk_x, Pairing.mul(vk.IC[" << (i + 1) << "], input[" << i << "]));" << endl; + cout << "\t\tif (!Pairing.pairingProd2(proof.A, vk.A, Pairing.negate(proof.A_p), Pairing.P2())) return false;" << endl; + cout << "\t\tif (!Pairing.pairingProd2(vk.B, proof.B, Pairing.negate(proof.B_p), Pairing.P2())) return false;" << endl; + cout << "\t\tif (!Pairing.pairingProd2(proof.C, vk.C, Pairing.negate(proof.C_p), Pairing.P2())) return false;" << endl; + cout << "\t\tif (!Pairing.pairingProd3(proof.K, vk.gamma, Pairing.negate(Pairing.add(vk_x, Pairing.add(proof.A, proof.C))), vk.gammaBeta2, Pairing.negate(vk.gammaBeta1), proof.B)) return false;" << endl; + cout << "\t\tif (!Pairing.pairingProd3(" << endl; + cout << "\t\t\t\tPairing.add(vk_x, proof.A), proof.B," << endl; + cout << "\t\t\t\tPairing.negate(proof.H), vk.Z," << endl; + cout << "\t\t\t\tPairing.negate(proof.C), Pairing.P2()" << endl; + cout << "\t\t)) return false;" << endl; + cout << "\t\treturn true;" << endl; + cout << "\t}" << endl; +} +#endif + +} +} From 89918282f76dd03881becadd47608a0258c1d1e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Mar 2017 14:44:18 +0100 Subject: [PATCH 02/10] ProjectSnark: switch to libff --- CMakeLists.txt | 2 +- cmake/ProjectBoost.cmake | 19 ++++---- cmake/ProjectSnark.cmake | 31 +++++++++++++ libdevcrypto/CMakeLists.txt | 2 +- libdevcrypto/LibSnark.cpp | 86 ++++++++++++++++--------------------- 5 files changed, 81 insertions(+), 59 deletions(-) create mode 100644 cmake/ProjectSnark.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index aa380ee873b..7f13b871013 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ include(ProjectCryptopp) include(ProjectJsonCpp) include(ProjectJsonRpcCpp) include(ProjectSecp256k1) -include(deps/snark.cmake) +include(ProjectSnark) configure_project() diff --git a/cmake/ProjectBoost.cmake b/cmake/ProjectBoost.cmake index 822333b46c8..5ce0ac8857a 100644 --- a/cmake/ProjectBoost.cmake +++ b/cmake/ProjectBoost.cmake @@ -36,45 +36,46 @@ ExternalProject_Add(boost --with-regex --with-test --with-thread + --with-program_options # libff wants it, we may need it in future. LOG_BUILD 1 INSTALL_COMMAND "" ) ExternalProject_Get_Property(boost SOURCE_DIR) set(BOOST_INCLUDE_DIR ${SOURCE_DIR}) -set(BOOST_LIB_DIR ${SOURCE_DIR}/stage/lib) +set(BOOST_LIBRARY_DIR ${SOURCE_DIR}/stage/lib) unset(BUILD_DIR) add_library(Boost::Chrono STATIC IMPORTED) -set_property(TARGET Boost::Chrono PROPERTY IMPORTED_LOCATION ${BOOST_LIB_DIR}/libboost_chrono${BOOST_LIBRARY_SUFFIX}) +set_property(TARGET Boost::Chrono PROPERTY IMPORTED_LOCATION ${BOOST_LIBRARY_DIR}/libboost_chrono${BOOST_LIBRARY_SUFFIX}) add_dependencies(Boost::Chrono boost) add_library(Boost::DataTime STATIC IMPORTED) -set_property(TARGET Boost::DataTime PROPERTY IMPORTED_LOCATION ${BOOST_LIB_DIR}/libboost_date_time${BOOST_LIBRARY_SUFFIX}) +set_property(TARGET Boost::DataTime PROPERTY IMPORTED_LOCATION ${BOOST_LIBRARY_DIR}/libboost_date_time${BOOST_LIBRARY_SUFFIX}) add_dependencies(Boost::DataTime boost) add_library(Boost::Regex STATIC IMPORTED) -set_property(TARGET Boost::Regex PROPERTY IMPORTED_LOCATION ${BOOST_LIB_DIR}/libboost_regex${BOOST_LIBRARY_SUFFIX}) +set_property(TARGET Boost::Regex PROPERTY IMPORTED_LOCATION ${BOOST_LIBRARY_DIR}/libboost_regex${BOOST_LIBRARY_SUFFIX}) add_dependencies(Boost::Regex boost) add_library(Boost::System STATIC IMPORTED) -set_property(TARGET Boost::System PROPERTY IMPORTED_LOCATION ${BOOST_LIB_DIR}/libboost_system${BOOST_LIBRARY_SUFFIX}) +set_property(TARGET Boost::System PROPERTY IMPORTED_LOCATION ${BOOST_LIBRARY_DIR}/libboost_system${BOOST_LIBRARY_SUFFIX}) add_dependencies(Boost::System boost) add_library(Boost::Filesystem STATIC IMPORTED) -set_property(TARGET Boost::Filesystem PROPERTY IMPORTED_LOCATION ${BOOST_LIB_DIR}/libboost_filesystem${BOOST_LIBRARY_SUFFIX}) +set_property(TARGET Boost::Filesystem PROPERTY IMPORTED_LOCATION ${BOOST_LIBRARY_DIR}/libboost_filesystem${BOOST_LIBRARY_SUFFIX}) set_property(TARGET Boost::Filesystem PROPERTY INTERFACE_LINK_LIBRARIES Boost::System) add_dependencies(Boost::Filesystem boost) add_library(Boost::Random STATIC IMPORTED) -set_property(TARGET Boost::Random PROPERTY IMPORTED_LOCATION ${BOOST_LIB_DIR}/libboost_random${BOOST_LIBRARY_SUFFIX}) +set_property(TARGET Boost::Random PROPERTY IMPORTED_LOCATION ${BOOST_LIBRARY_DIR}/libboost_random${BOOST_LIBRARY_SUFFIX}) add_dependencies(Boost::Random boost) add_library(Boost::UnitTestFramework STATIC IMPORTED) -set_property(TARGET Boost::UnitTestFramework PROPERTY IMPORTED_LOCATION ${BOOST_LIB_DIR}/libboost_unit_test_framework${BOOST_LIBRARY_SUFFIX}) +set_property(TARGET Boost::UnitTestFramework PROPERTY IMPORTED_LOCATION ${BOOST_LIBRARY_DIR}/libboost_unit_test_framework${BOOST_LIBRARY_SUFFIX}) add_dependencies(Boost::UnitTestFramework boost) add_library(Boost::Thread STATIC IMPORTED) -set_property(TARGET Boost::Thread PROPERTY IMPORTED_LOCATION ${BOOST_LIB_DIR}/libboost_thread${BOOST_LIBRARY_SUFFIX}) +set_property(TARGET Boost::Thread PROPERTY IMPORTED_LOCATION ${BOOST_LIBRARY_DIR}/libboost_thread${BOOST_LIBRARY_SUFFIX}) set_property(TARGET Boost::Thread PROPERTY INTERFACE_LINK_LIBRARIES Boost::Chrono Boost::DataTime Boost::Regex) add_dependencies(Boost::Thread boost) diff --git a/cmake/ProjectSnark.cmake b/cmake/ProjectSnark.cmake new file mode 100644 index 00000000000..3cfad54cfd9 --- /dev/null +++ b/cmake/ProjectSnark.cmake @@ -0,0 +1,31 @@ +# Depends on ProjectBoost.cmake + +# FIXME: Rename to LibFF as that's the name of the library. + +ExternalProject_Add(snark + PREFIX ${CMAKE_SOURCE_DIR}/deps + DOWNLOAD_NAME libff-2fa434b3.tar.gz + DOWNLOAD_NO_PROGRESS TRUE + URL https://github.com/scipr-lab/libff/archive/2fa434b3d6e9163beedaefffa85043a1180a05e6.tar.gz + URL_HASH SHA256=d3266f95a86bbfc908a7a9531df08a47cdd026e76b6290465fb8dfa203606a21 + + PATCH_COMMAND ${CMAKE_COMMAND} -E touch /third_party/gtest/CMakeLists.txt + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= + -DBOOST_INCLUDEDIR=${BOOST_INCLUDE_DIR} + -DBOOST_LIBRARYDIR=${BOOST_LIBRARY_DIR} + -DCURVE=ALT_BN128 -DPERFORMANCE=Off -DWITH_PROCPS=Off + -DUSE_PT_COMPRESSION=Off +) +add_dependencies(snark boost) + +# Create snark imported library +ExternalProject_Get_Property(snark INSTALL_DIR) +add_library(Snark STATIC IMPORTED) +set(SNARK_LIBRARY ${INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}ff${CMAKE_STATIC_LIBRARY_SUFFIX}) +set(SNARK_INCLUDE_DIR ${INSTALL_DIR}/include/libff) +file(MAKE_DIRECTORY ${SNARK_INCLUDE_DIR}) +set_property(TARGET Snark PROPERTY IMPORTED_LOCATION ${SNARK_LIBRARY}) +set_property(TARGET Snark PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SNARK_INCLUDE_DIR}) +set_property(TARGET Snark PROPERTY INTERFACE_LINK_LIBRARIES gmp) +add_dependencies(Snark snark) +unset(INSTALL_DIR) diff --git a/libdevcrypto/CMakeLists.txt b/libdevcrypto/CMakeLists.txt index 425937f709d..b352970805a 100644 --- a/libdevcrypto/CMakeLists.txt +++ b/libdevcrypto/CMakeLists.txt @@ -6,4 +6,4 @@ add_library(devcrypto ${SOURCES} ${HEADERS}) find_package(Utils) target_include_directories(devcrypto PRIVATE ..) target_include_directories(devcrypto PRIVATE ../utils) -target_link_libraries(devcrypto Secp256k1 Cryptopp snark ${Utils_SCRYPT_LIBRARIES} devcore) +target_link_libraries(devcrypto Secp256k1 Snark Cryptopp ${Utils_SCRYPT_LIBRARIES} devcore) diff --git a/libdevcrypto/LibSnark.cpp b/libdevcrypto/LibSnark.cpp index 5b23876e31e..4ee78b226cc 100644 --- a/libdevcrypto/LibSnark.cpp +++ b/libdevcrypto/LibSnark.cpp @@ -19,25 +19,16 @@ */ #include -// libsnark has to be compiled with exactly the same switches: -// sudo PREFIX=/usr/local make NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 CURVE=ALT_BN128 FEATUREFLAGS="-DBINARY_OUTPUT=1 -DMONTGOMERY_OUTPUT=1 -DNO_PT_COMPRESSION=1" lib install -#define BINARY_OUTPUT 1 -#define MONTGOMERY_OUTPUT 1 -#define NO_PT_COMPRESSION 1 - -#include -#include -#include -#include -#include + +#include +#include +#include +#include #include #include #include -#include - -#include using namespace std; using namespace dev; @@ -51,24 +42,23 @@ void initLibSnark() static bool initialized = 0; if (!initialized) { - libsnark::alt_bn128_pp::init_public_params(); + // FIXME: This is race condition!!! + libff::alt_bn128_pp::init_public_params(); // Otherwise the library would output profiling info for each run. - libsnark::inhibit_profiling_counters = true; - libsnark::inhibit_profiling_info = true; initialized = true; } } -libsnark::bigint toLibsnarkBigint(h256 const& _x) +libff::bigint toLibsnarkBigint(h256 const& _x) { - libsnark::bigint x; + libff::bigint x; for (unsigned i = 0; i < 4; i++) for (unsigned j = 0; j < 8; j++) x.data[3 - i] |= uint64_t(_x[i * 8 + j]) << (8 * (7 - j)); return x; } -h256 fromLibsnarkBigint(libsnark::bigint _x) +h256 fromLibsnarkBigint(libff::bigint _x) { h256 x; for (unsigned i = 0; i < 4; i++) @@ -77,29 +67,29 @@ h256 fromLibsnarkBigint(libsnark::bigint _x) return x; } -libsnark::alt_bn128_Fq decodeFqElement(dev::bytesConstRef _data) +libff::alt_bn128_Fq decodeFqElement(dev::bytesConstRef _data) { // h256::AlignLeft ensures that the h256 is zero-filled on the right if _data // is too short. h256 xbin(_data, h256::AlignLeft); - if (u256(xbin) >= u256(fromLibsnarkBigint(libsnark::alt_bn128_Fq::mod))) + if (u256(xbin) >= u256(fromLibsnarkBigint(libff::alt_bn128_Fq::mod))) BOOST_THROW_EXCEPTION(InvalidEncoding()); return toLibsnarkBigint(xbin); } -libsnark::alt_bn128_G1 decodePointG1(dev::bytesConstRef _data) +libff::alt_bn128_G1 decodePointG1(dev::bytesConstRef _data) { - libsnark::alt_bn128_Fq X = decodeFqElement(_data.cropped(0)); - libsnark::alt_bn128_Fq Y = decodeFqElement(_data.cropped(32)); - if (X == libsnark::alt_bn128_Fq::zero() && Y == libsnark::alt_bn128_Fq::zero()) - return libsnark::alt_bn128_G1::zero(); - libsnark::alt_bn128_G1 p(X, Y, libsnark::alt_bn128_Fq::one()); + libff::alt_bn128_Fq X = decodeFqElement(_data.cropped(0)); + libff::alt_bn128_Fq Y = decodeFqElement(_data.cropped(32)); + if (X == libff::alt_bn128_Fq::zero() && Y == libff::alt_bn128_Fq::zero()) + return libff::alt_bn128_G1::zero(); + libff::alt_bn128_G1 p(X, Y, libff::alt_bn128_Fq::one()); if (!p.is_well_formed()) BOOST_THROW_EXCEPTION(InvalidEncoding()); return p; } -bytes encodePointG1(libsnark::alt_bn128_G1 _p) +bytes encodePointG1(libff::alt_bn128_G1 _p) { if (_p.is_zero()) return h256().asBytes() + h256().asBytes(); @@ -109,24 +99,24 @@ bytes encodePointG1(libsnark::alt_bn128_G1 _p) fromLibsnarkBigint(_p.Y.as_bigint()).asBytes(); } -libsnark::alt_bn128_Fq2 decodeFq2Element(dev::bytesConstRef _data) +libff::alt_bn128_Fq2 decodeFq2Element(dev::bytesConstRef _data) { // Encoding: c1 (256 bits) c0 (256 bits) // "Big endian", just like the numbers - return libsnark::alt_bn128_Fq2( + return libff::alt_bn128_Fq2( decodeFqElement(_data.cropped(32)), decodeFqElement(_data.cropped(0)) ); } -libsnark::alt_bn128_G2 decodePointG2(dev::bytesConstRef _data) +libff::alt_bn128_G2 decodePointG2(dev::bytesConstRef _data) { - libsnark::alt_bn128_Fq2 X = decodeFq2Element(_data); - libsnark::alt_bn128_Fq2 Y = decodeFq2Element(_data.cropped(64)); - if (X == libsnark::alt_bn128_Fq2::zero() && Y == libsnark::alt_bn128_Fq2::zero()) - return libsnark::alt_bn128_G2::zero(); - libsnark::alt_bn128_G2 p(X, Y, libsnark::alt_bn128_Fq2::one()); + libff::alt_bn128_Fq2 X = decodeFq2Element(_data); + libff::alt_bn128_Fq2 Y = decodeFq2Element(_data.cropped(64)); + if (X == libff::alt_bn128_Fq2::zero() && Y == libff::alt_bn128_Fq2::zero()) + return libff::alt_bn128_G2::zero(); + libff::alt_bn128_G2 p(X, Y, libff::alt_bn128_Fq2::one()); if (!p.is_well_formed()) BOOST_THROW_EXCEPTION(InvalidEncoding()); return p; @@ -150,20 +140,20 @@ pair dev::crypto::alt_bn128_pairing_product(dev::bytesConstRef _in) try { initLibSnark(); - libsnark::alt_bn128_Fq12 x = libsnark::alt_bn128_Fq12::one(); + libff::alt_bn128_Fq12 x = libff::alt_bn128_Fq12::one(); for (size_t i = 0; i < pairs; ++i) { dev::bytesConstRef pair = _in.cropped(i * pairSize, pairSize); - libsnark::alt_bn128_G2 p = decodePointG2(pair.cropped(2 * 32)); - if (-libsnark::alt_bn128_G2::scalar_field::one() * p + p != libsnark::alt_bn128_G2::zero()) + libff::alt_bn128_G2 p = decodePointG2(pair.cropped(2 * 32)); + if (-libff::alt_bn128_G2::scalar_field::one() * p + p != libff::alt_bn128_G2::zero()) // p is not an element of the group (has wrong order) return {false, bytes()}; - x = x * libsnark::alt_bn128_miller_loop( - libsnark::alt_bn128_precompute_G1(decodePointG1(pair)), - libsnark::alt_bn128_precompute_G2(p) + x = x * libff::alt_bn128_miller_loop( + libff::alt_bn128_precompute_G1(decodePointG1(pair)), + libff::alt_bn128_precompute_G2(p) ); } - result = libsnark::alt_bn128_final_exponentiation(x) == libsnark::alt_bn128_GT::one(); + result = libff::alt_bn128_final_exponentiation(x) == libff::alt_bn128_GT::one(); } catch (InvalidEncoding const&) { @@ -185,8 +175,8 @@ pair dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in) try { initLibSnark(); - libsnark::alt_bn128_G1 p1 = decodePointG1(_in); - libsnark::alt_bn128_G1 p2 = decodePointG1(_in.cropped(32 * 2)); + libff::alt_bn128_G1 p1 = decodePointG1(_in); + libff::alt_bn128_G1 p2 = decodePointG1(_in.cropped(32 * 2)); return {true, encodePointG1(p1 + p2)}; } @@ -205,9 +195,9 @@ pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) try { initLibSnark(); - libsnark::alt_bn128_G1 p = decodePointG1(_in.cropped(0)); + libff::alt_bn128_G1 p = decodePointG1(_in.cropped(0)); - libsnark::alt_bn128_G1 result = toLibsnarkBigint(h256(_in.cropped(64), h256::AlignLeft)) * p; + libff::alt_bn128_G1 result = toLibsnarkBigint(h256(_in.cropped(64), h256::AlignLeft)) * p; return {true, encodePointG1(result)}; } From 90ad447e3493e5ca5a7c2a4a4db27819ea39a7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 19 Apr 2017 18:21:29 +0200 Subject: [PATCH 03/10] testeth: save one thread --- test/tools/libtesteth/boostTest.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/tools/libtesteth/boostTest.cpp b/test/tools/libtesteth/boostTest.cpp index 7255c4e103c..684b500fd30 100644 --- a/test/tools/libtesteth/boostTest.cpp +++ b/test/tools/libtesteth/boostTest.cpp @@ -154,9 +154,8 @@ int main( int argc, char* argv[] ) parameters.push_back(argv[i]); stopTravisOut = false; - std::future ret = std::async(unit_test_main, fake_init_func, argc, argv); std::thread outputThread(travisOut); - int result = ret.get(); + int result = unit_test_main(fake_init_func, argc, argv); stopTravisOut = true; outputThread.join(); dev::test::TestOutputHelper::printTestExecStats(); From 8d1726cd258304c7767a34e31f9ba85fdd551bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 17 Mar 2017 08:33:04 +0100 Subject: [PATCH 04/10] Integrate MPIR for libff --- cmake/ProjectMPIR.cmake | 18 ++++++++++++++++++ cmake/ProjectSnark.cmake | 15 ++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 cmake/ProjectMPIR.cmake diff --git a/cmake/ProjectMPIR.cmake b/cmake/ProjectMPIR.cmake new file mode 100644 index 00000000000..d848a601803 --- /dev/null +++ b/cmake/ProjectMPIR.cmake @@ -0,0 +1,18 @@ +ExternalProject_Add(mpir + PREFIX ${CMAKE_SOURCE_DIR}/deps + DOWNLOAD_NAME mpir-master.tar.gz + DOWNLOAD_NO_PROGRESS TRUE + URL https://github.com/chfast/mpir/archive/master.tar.gz + URL_HASH SHA256=ed52442ab345c9dbb463d9c4bee4436785dd6a3ab6d24e461302dd3f93bedbb4 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= + -DCMAKE_BUILD_TYPE=Release +) + +ExternalProject_Get_Property(mpir INSTALL_DIR) +add_library(MPIR::mpz STATIC IMPORTED) +set(MPIR_LIBRARY ${INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}mpz${CMAKE_STATIC_LIBRARY_SUFFIX}) +set(MPIR_INCLUDE_DIR ${INSTALL_DIR}/include) +set_property(TARGET MPIR::mpz PROPERTY IMPORTED_LOCATION ${MPIR_LIBRARY}) +set_property(TARGET MPIR::mpz PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MPIR_INCLUDE_DIR}) +add_dependencies(MPIR::mpz mpir) +unset(INSTALL_DIR) \ No newline at end of file diff --git a/cmake/ProjectSnark.cmake b/cmake/ProjectSnark.cmake index 3cfad54cfd9..d31045cdb8f 100644 --- a/cmake/ProjectSnark.cmake +++ b/cmake/ProjectSnark.cmake @@ -1,18 +1,15 @@ -# Depends on ProjectBoost.cmake +include(ProjectMPIR) # FIXME: Rename to LibFF as that's the name of the library. ExternalProject_Add(snark PREFIX ${CMAKE_SOURCE_DIR}/deps - DOWNLOAD_NAME libff-2fa434b3.tar.gz + DOWNLOAD_NAME libff-a1021d20.tar.gz DOWNLOAD_NO_PROGRESS TRUE - URL https://github.com/scipr-lab/libff/archive/2fa434b3d6e9163beedaefffa85043a1180a05e6.tar.gz - URL_HASH SHA256=d3266f95a86bbfc908a7a9531df08a47cdd026e76b6290465fb8dfa203606a21 - - PATCH_COMMAND ${CMAKE_COMMAND} -E touch /third_party/gtest/CMakeLists.txt + URL https://github.com/chfast/libff/archive/a1021d2066d1b73fe701fdb60b2e521134b126bf.tar.gz + URL_HASH SHA256=f71052cd97e55898853f44434dbf3a804d3b66afd950a81093a277bcc9e002f6 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= - -DBOOST_INCLUDEDIR=${BOOST_INCLUDE_DIR} - -DBOOST_LIBRARYDIR=${BOOST_LIBRARY_DIR} + -DGMP_INCLUDE_DIR=${MPIR_INCLUDE_DIR} -DCURVE=ALT_BN128 -DPERFORMANCE=Off -DWITH_PROCPS=Off -DUSE_PT_COMPRESSION=Off ) @@ -26,6 +23,6 @@ set(SNARK_INCLUDE_DIR ${INSTALL_DIR}/include/libff) file(MAKE_DIRECTORY ${SNARK_INCLUDE_DIR}) set_property(TARGET Snark PROPERTY IMPORTED_LOCATION ${SNARK_LIBRARY}) set_property(TARGET Snark PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SNARK_INCLUDE_DIR}) -set_property(TARGET Snark PROPERTY INTERFACE_LINK_LIBRARIES gmp) +set_property(TARGET Snark PROPERTY INTERFACE_LINK_LIBRARIES MPIR::mpz) add_dependencies(Snark snark) unset(INSTALL_DIR) From 88a0786cd6d76f065d3120935bfb7fea115a6971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 8 May 2017 22:04:06 +0200 Subject: [PATCH 05/10] libff: Fix configuration --- .travis.yml | 1 + cmake/ProjectMPIR.cmake | 19 ++++++++++--------- cmake/ProjectSnark.cmake | 12 ++++++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 509faf0fffd..723888932cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -107,6 +107,7 @@ cache: # build external dependencies next build. - $TRAVIS_BUILD_DIR/deps install: + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then clang --version; brew install llvm; $(brew --prefix llvm)/bin/clang --version; export PATH=$(brew --prefix llvm)/bin:$PATH; export CC=$(brew --prefix llvm)/bin/clang; export CXX=$(brew --prefix llvm)/bin/clang++; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./scripts/install_cmake.sh; fi - ./scripts/install_deps.sh before_script: diff --git a/cmake/ProjectMPIR.cmake b/cmake/ProjectMPIR.cmake index d848a601803..a3e7be9b9c9 100644 --- a/cmake/ProjectMPIR.cmake +++ b/cmake/ProjectMPIR.cmake @@ -1,18 +1,19 @@ ExternalProject_Add(mpir PREFIX ${CMAKE_SOURCE_DIR}/deps - DOWNLOAD_NAME mpir-master.tar.gz + DOWNLOAD_NAME mpir-cmake.tar.gz DOWNLOAD_NO_PROGRESS TRUE - URL https://github.com/chfast/mpir/archive/master.tar.gz - URL_HASH SHA256=ed52442ab345c9dbb463d9c4bee4436785dd6a3ab6d24e461302dd3f93bedbb4 + URL https://github.com/chfast/mpir/archive/cmake.tar.gz + URL_HASH SHA256=2bf35ad7aa2b61bf4c2dcdab9aab48b2b4f9d6db8b937be4b0b5fe3b5efbdfa6 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= - -DCMAKE_BUILD_TYPE=Release + -DCMAKE_BUILD_TYPE=Debug + -DMPIR_GMP=On ) ExternalProject_Get_Property(mpir INSTALL_DIR) -add_library(MPIR::mpz STATIC IMPORTED) -set(MPIR_LIBRARY ${INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}mpz${CMAKE_STATIC_LIBRARY_SUFFIX}) +add_library(MPIR::mpir STATIC IMPORTED) +set(MPIR_LIBRARY ${INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}mpir${CMAKE_STATIC_LIBRARY_SUFFIX}) set(MPIR_INCLUDE_DIR ${INSTALL_DIR}/include) -set_property(TARGET MPIR::mpz PROPERTY IMPORTED_LOCATION ${MPIR_LIBRARY}) -set_property(TARGET MPIR::mpz PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MPIR_INCLUDE_DIR}) -add_dependencies(MPIR::mpz mpir) +set_property(TARGET MPIR::mpir PROPERTY IMPORTED_LOCATION ${MPIR_LIBRARY}) +set_property(TARGET MPIR::mpir PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MPIR_INCLUDE_DIR}) +add_dependencies(MPIR::mpir mpir) unset(INSTALL_DIR) \ No newline at end of file diff --git a/cmake/ProjectSnark.cmake b/cmake/ProjectSnark.cmake index d31045cdb8f..6c047130fd6 100644 --- a/cmake/ProjectSnark.cmake +++ b/cmake/ProjectSnark.cmake @@ -4,14 +4,18 @@ include(ProjectMPIR) ExternalProject_Add(snark PREFIX ${CMAKE_SOURCE_DIR}/deps - DOWNLOAD_NAME libff-a1021d20.tar.gz + DOWNLOAD_NAME libff-97d3fa6c.tar.gz DOWNLOAD_NO_PROGRESS TRUE - URL https://github.com/chfast/libff/archive/a1021d2066d1b73fe701fdb60b2e521134b126bf.tar.gz - URL_HASH SHA256=f71052cd97e55898853f44434dbf3a804d3b66afd950a81093a277bcc9e002f6 + URL https://github.com/chfast/libff/archive/97d3fa6cdbd4b7549c7a8a179dc97308dbfd044d.tar.gz + URL_HASH SHA256=f102f3ee43c96c9a81c20d8c0446c805c6b8c0e3121518b3625f08e2c230096e CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= -DGMP_INCLUDE_DIR=${MPIR_INCLUDE_DIR} + -DGMP_LIBRARIES=${MPIR_LIBRARY} + -DGMPXX_LIBRARIES=${MPIR_LIBRARY} -DCURVE=ALT_BN128 -DPERFORMANCE=Off -DWITH_PROCPS=Off -DUSE_PT_COMPRESSION=Off + BUILD_COMMAND ${CMAKE_COMMAND} --build --config Release + INSTALL_COMMAND ${CMAKE_COMMAND} --build --config Release --target install ) add_dependencies(snark boost) @@ -23,6 +27,6 @@ set(SNARK_INCLUDE_DIR ${INSTALL_DIR}/include/libff) file(MAKE_DIRECTORY ${SNARK_INCLUDE_DIR}) set_property(TARGET Snark PROPERTY IMPORTED_LOCATION ${SNARK_LIBRARY}) set_property(TARGET Snark PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SNARK_INCLUDE_DIR}) -set_property(TARGET Snark PROPERTY INTERFACE_LINK_LIBRARIES MPIR::mpz) +set_property(TARGET Snark PROPERTY INTERFACE_LINK_LIBRARIES MPIR::mpir) add_dependencies(Snark snark) unset(INSTALL_DIR) From e924e18ef978607686cd03ae5a64d5668344bb12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 23 May 2017 12:56:27 +0200 Subject: [PATCH 06/10] snark: Fix conversions and static initialization --- libdevcrypto/LibSnark.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libdevcrypto/LibSnark.cpp b/libdevcrypto/LibSnark.cpp index 4ee78b226cc..4125488475c 100644 --- a/libdevcrypto/LibSnark.cpp +++ b/libdevcrypto/LibSnark.cpp @@ -39,31 +39,31 @@ namespace void initLibSnark() { - static bool initialized = 0; - if (!initialized) - { - // FIXME: This is race condition!!! - libff::alt_bn128_pp::init_public_params(); - // Otherwise the library would output profiling info for each run. - initialized = true; - } + // This is hackish, but we really want to use `static` variable for lock + // free thread-safe initialization. + static bool initialized = (libff::alt_bn128_pp::init_public_params(), true); + (void)initialized; } libff::bigint toLibsnarkBigint(h256 const& _x) { libff::bigint x; - for (unsigned i = 0; i < 4; i++) - for (unsigned j = 0; j < 8; j++) - x.data[3 - i] |= uint64_t(_x[i * 8 + j]) << (8 * (7 - j)); + constexpr auto N = x.N; + constexpr auto L = sizeof(x.data[0]); + for (unsigned i = 0; i < N; i++) + for (unsigned j = 0; j < L; j++) + x.data[N - 1 - i] |= uint64_t(_x[i * L + j]) << (8 * (L - 1 - j)); return x; } h256 fromLibsnarkBigint(libff::bigint _x) { + constexpr auto N = _x.N; + constexpr auto L = sizeof(_x.data[0]); h256 x; - for (unsigned i = 0; i < 4; i++) - for (unsigned j = 0; j < 8; j++) - x[i * 8 + j] = uint8_t(uint64_t(_x.data[3 - i]) >> (8 * (7 - j))); + for (unsigned i = 0; i < N; i++) + for (unsigned j = 0; j < L; j++) + x[i * L + j] = uint8_t(uint64_t(_x.data[N - 1 - i]) >> (8 * (L - 1 - j))); return x; } From 9cbc85de8541f517227bf7c2baf993c9e47b54e3 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 29 May 2017 12:13:00 +0200 Subject: [PATCH 07/10] snark: Fixes and cleanups --- cmake/ProjectSnark.cmake | 1 + libdevcrypto/LibSnark.cpp | 46 ++++--- libethashseal/genesis/mainNetwork.cpp | 6 +- libethashseal/genesis/metropolisTest.cpp | 9 +- libethcore/Precompiled.cpp | 55 +++++++- test/jsontests | 2 +- test/libdevcrypto/LibSnark.cpp | 161 ----------------------- test/tools/jsontests/StateTests.cpp | 1 + test/tools/libtesteth/ImportTest.cpp | 2 +- 9 files changed, 95 insertions(+), 188 deletions(-) diff --git a/cmake/ProjectSnark.cmake b/cmake/ProjectSnark.cmake index 6c047130fd6..abdd166dc3c 100644 --- a/cmake/ProjectSnark.cmake +++ b/cmake/ProjectSnark.cmake @@ -18,6 +18,7 @@ ExternalProject_Add(snark INSTALL_COMMAND ${CMAKE_COMMAND} --build --config Release --target install ) add_dependencies(snark boost) +add_dependencies(snark mpir) # Create snark imported library ExternalProject_Get_Property(snark INSTALL_DIR) diff --git a/libdevcrypto/LibSnark.cpp b/libdevcrypto/LibSnark.cpp index 4125488475c..976dc6396b0 100644 --- a/libdevcrypto/LibSnark.cpp +++ b/libdevcrypto/LibSnark.cpp @@ -24,12 +24,15 @@ #include #include #include +#include #include #include #include +#include + using namespace std; using namespace dev; using namespace dev::crypto; @@ -37,33 +40,40 @@ using namespace dev::crypto; namespace { +std::once_flag initLibSnarkFlag; + void initLibSnark() { - // This is hackish, but we really want to use `static` variable for lock - // free thread-safe initialization. - static bool initialized = (libff::alt_bn128_pp::init_public_params(), true); - (void)initialized; + call_once(initLibSnarkFlag, + [](){ + cnote << "initializing libsnark."; + libff::inhibit_profiling_info = true; + libff::inhibit_profiling_counters = true; + libff::alt_bn128_pp::init_public_params(); + }); } libff::bigint toLibsnarkBigint(h256 const& _x) { libff::bigint x; - constexpr auto N = x.N; - constexpr auto L = sizeof(x.data[0]); + auto const N = x.N; + constexpr size_t L = sizeof(x.data[0]); + static_assert(sizeof(mp_limb_t) == L, "Unexpected limb size in libff:bigint."); for (unsigned i = 0; i < N; i++) for (unsigned j = 0; j < L; j++) - x.data[N - 1 - i] |= uint64_t(_x[i * L + j]) << (8 * (L - 1 - j)); + x.data[N - 1 - i] |= (mp_limb_t)(_x[i * L + j]) << (8 * (L - 1 - j)); return x; } -h256 fromLibsnarkBigint(libff::bigint _x) +h256 fromLibsnarkBigint(libff::bigint const& _x) { - constexpr auto N = _x.N; - constexpr auto L = sizeof(_x.data[0]); + auto const N = _x.N; + constexpr size_t L = sizeof(_x.data[0]); + static_assert(sizeof(mp_limb_t) == L, "Unexpected limb size in libff:bigint."); h256 x; for (unsigned i = 0; i < N; i++) for (unsigned j = 0; j < L; j++) - x[i * L + j] = uint8_t(uint64_t(_x.data[N - 1 - i]) >> (8 * (L - 1 - j))); + x[i * L + j] = uint8_t((mp_limb_t)(_x.data[N - 1 - i]) >> (8 * (L - 1 - j))); return x; } @@ -130,7 +140,7 @@ pair dev::crypto::alt_bn128_pairing_product(dev::bytesConstRef _in) // Output: 1 if pairing evaluates to 1, 0 otherwise (left-padded to 32 bytes) bool result = true; - size_t const pairSize = 2 * 32 + 2 * 64; + size_t constexpr pairSize = 2 * 32 + 2 * 64; size_t const pairs = _in.size() / pairSize; if (pairs * pairSize != _in.size()) { @@ -148,6 +158,10 @@ pair dev::crypto::alt_bn128_pairing_product(dev::bytesConstRef _in) if (-libff::alt_bn128_G2::scalar_field::one() * p + p != libff::alt_bn128_G2::zero()) // p is not an element of the group (has wrong order) return {false, bytes()}; + if (p.is_zero()) + continue; // the pairing is one + if (decodePointG1(pair).is_zero()) + continue; // the pairing is one x = x * libff::alt_bn128_miller_loop( libff::alt_bn128_precompute_G1(decodePointG1(pair)), libff::alt_bn128_precompute_G2(p) @@ -175,8 +189,8 @@ pair dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in) try { initLibSnark(); - libff::alt_bn128_G1 p1 = decodePointG1(_in); - libff::alt_bn128_G1 p2 = decodePointG1(_in.cropped(32 * 2)); + libff::alt_bn128_G1 const p1 = decodePointG1(_in); + libff::alt_bn128_G1 const p2 = decodePointG1(_in.cropped(32 * 2)); return {true, encodePointG1(p1 + p2)}; } @@ -195,9 +209,9 @@ pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) try { initLibSnark(); - libff::alt_bn128_G1 p = decodePointG1(_in.cropped(0)); + libff::alt_bn128_G1 const p = decodePointG1(_in.cropped(0)); - libff::alt_bn128_G1 result = toLibsnarkBigint(h256(_in.cropped(64), h256::AlignLeft)) * p; + libff::alt_bn128_G1 const result = toLibsnarkBigint(h256(_in.cropped(64), h256::AlignLeft)) * p; return {true, encodePointG1(result)}; } diff --git a/libethashseal/genesis/mainNetwork.cpp b/libethashseal/genesis/mainNetwork.cpp index b16cdc15b0a..384e125d684 100644 --- a/libethashseal/genesis/mainNetwork.cpp +++ b/libethashseal/genesis/mainNetwork.cpp @@ -55,9 +55,9 @@ R"E( "0000000000000000000000000000000000000002": { "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, "0000000000000000000000000000000000000003": { "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, "0000000000000000000000000000000000000004": { "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, - "0000000000000000000000000000000000000006": { "precompiled": { "name": "alt_bn128_G1_add", "startingBlock": "0xffffffffffffffffff", "linear": { "base": 15, "word": 3 } } }, - "0000000000000000000000000000000000000007": { "precompiled": { "name": "alt_bn128_G1_mul", "startingBlock": "0xffffffffffffffffff", "linear": { "base": 15, "word": 3 } } }, - "0000000000000000000000000000000000000008": { "precompiled": { "name": "alt_bn128_pairing_product", "startingBlock": "0xffffffffffffffffff", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000006": { "precompiled": { "name": "alt_bn128_G1_add", "startingBlock": "0xffffffffffffffffff", "linear": { "base": 500, "word": 0 } } }, + "0000000000000000000000000000000000000007": { "precompiled": { "name": "alt_bn128_G1_mul", "startingBlock": "0xffffffffffffffffff", "linear": { "base": 2000, "word": 0 } } }, + "0000000000000000000000000000000000000008": { "precompiled": { "name": "alt_bn128_pairing_product", "startingBlock": "0xffffffffffffffffff" } }, "3282791d6fd713f1e94f4bfd565eaa78b3a0599d": { "balance": "1337000000000000000000" }, diff --git a/libethashseal/genesis/metropolisTest.cpp b/libethashseal/genesis/metropolisTest.cpp index 3f6dc3216f5..9bc50e2c8db 100644 --- a/libethashseal/genesis/metropolisTest.cpp +++ b/libethashseal/genesis/metropolisTest.cpp @@ -56,9 +56,10 @@ R"E( "0000000000000000000000000000000000000002": { "wei": "1", "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, "0000000000000000000000000000000000000003": { "wei": "1", "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, "0000000000000000000000000000000000000004": { "wei": "1", "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, - "0000000000000000000000000000000000000006": { "wei": "1", "precompiled": { "name": "alt_bn128_G1_add", "linear": { "base": 15, "word": 3 } } }, - "0000000000000000000000000000000000000007": { "wei": "1", "precompiled": { "name": "alt_bn128_G1_mul", "linear": { "base": 15, "word": 3 } } }, - "0000000000000000000000000000000000000008": { "wei": "1", "precompiled": { "name": "alt_bn128_pairing_product", "linear": { "base": 15, "word": 3 } } }, - } + "0000000000000000000000000000000000000005": { "wei": "1", "precompiled": { "name": "modexp" } }, + "0000000000000000000000000000000000000006": { "wei": "1", "precompiled": { "name": "alt_bn128_G1_add", "linear": { "base": 500, "word": 0 } } }, + "0000000000000000000000000000000000000007": { "wei": "1", "precompiled": { "name": "alt_bn128_G1_mul", "linear": { "base": 2000, "word": 0 } } }, + "0000000000000000000000000000000000000008": { "wei": "1", "precompiled": { "name": "alt_bn128_pairing_product" } } + } } )E"; diff --git a/libethcore/Precompiled.cpp b/libethcore/Precompiled.cpp index 61c8e6e9e32..cf7a94407a6 100644 --- a/libethcore/Precompiled.cpp +++ b/libethcore/Precompiled.cpp @@ -98,19 +98,70 @@ ETH_REGISTER_PRECOMPILED(identity)(bytesConstRef _in) return {true, _in.toBytes()}; } -ETH_REGISTER_PRECOMPILED(alt_bn128_pairing_product)(bytesConstRef _in) +// Parse _count bytes of _in starting with _begin offset as big endian int. +// If there's not enough bytes in _in, consider it infinitely right-padded with zeroes. +bigint parseBigEndianRightPadded(bytesConstRef _in, size_t _begin, size_t _count) { - return dev::crypto::alt_bn128_pairing_product(_in); + if (_begin > _in.count()) + return 0; + + // crop _in, not going beyond its size + bytesConstRef cropped = _in.cropped(_begin, min(_count, _in.count() - _begin)); + + bigint ret = fromBigEndian(cropped); + // shift as if we had right-padding zeroes + ret <<= 8 * (_count - cropped.count()); + + return ret; +} + +ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) +{ + size_t const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + size_t const expLength(parseBigEndianRightPadded(_in, 32, 32)); + size_t const modLength(parseBigEndianRightPadded(_in, 64, 32)); + + bigint const base(parseBigEndianRightPadded(_in, 96, baseLength)); + bigint const exp(parseBigEndianRightPadded(_in, 96 + baseLength, expLength)); + bigint const mod(parseBigEndianRightPadded(_in, 96 + baseLength + expLength, modLength)); + + bigint const result = mod != 0 ? boost::multiprecision::powm(base, exp, mod) : bigint{0}; + + bytes ret(modLength); + toBigEndian(result, ret); + + return {true, ret}; +} + +ETH_REGISTER_PRECOMPILED_PRICER(modexp)(bytesConstRef _in) +{ + bigint const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + bigint const expLength(parseBigEndianRightPadded(_in, 32, 32)); + bigint const modLength(parseBigEndianRightPadded(_in, 64, 32)); + + bigint const maxLength = max(modLength, baseLength); + + return maxLength * maxLength * max(expLength, 1) / 20; } ETH_REGISTER_PRECOMPILED(alt_bn128_G1_add)(bytesConstRef _in) { return dev::crypto::alt_bn128_G1_add(_in); } + ETH_REGISTER_PRECOMPILED(alt_bn128_G1_mul)(bytesConstRef _in) { return dev::crypto::alt_bn128_G1_mul(_in); } +ETH_REGISTER_PRECOMPILED(alt_bn128_pairing_product)(bytesConstRef _in) +{ + return dev::crypto::alt_bn128_pairing_product(_in); +} + +ETH_REGISTER_PRECOMPILED_PRICER(alt_bn128_pairing_product)(bytesConstRef _in) +{ + return 100000 + (_in.size() / 192) * 80000; +} } diff --git a/test/jsontests b/test/jsontests index dcbc5e771e9..b3dd02c7b02 160000 --- a/test/jsontests +++ b/test/jsontests @@ -1 +1 @@ -Subproject commit dcbc5e771e9e732ce369752707939649352965bd +Subproject commit b3dd02c7b0200551ccdf9972366e01bbbac4500d diff --git a/test/libdevcrypto/LibSnark.cpp b/test/libdevcrypto/LibSnark.cpp index 8b300ab6c83..7d721f4edeb 100644 --- a/test/libdevcrypto/LibSnark.cpp +++ b/test/libdevcrypto/LibSnark.cpp @@ -305,166 +305,5 @@ BOOST_AUTO_TEST_CASE(pairing) BOOST_AUTO_TEST_SUITE_END() -#if 0 - -std::string outputPointG1Affine(libsnark::alt_bn128_G1 _p) -{ - libsnark::alt_bn128_G1 aff = _p; - aff.to_affine_coordinates(); - return - "Pairing.g1FromAffine(0x" + - fromLibsnarkBigint(aff.X.as_bigint()).hex() + - ", 0x" + - fromLibsnarkBigint(aff.Y.as_bigint()).hex() + - ")"; -} - -std::string outputPointG2Affine(libsnark::alt_bn128_G2 _p) -{ - libsnark::alt_bn128_G2 aff = _p; - aff.to_affine_coordinates(); - return - "Pairing.g2FromAffine([0x" + - fromLibsnarkBigint(aff.X.c1.as_bigint()).hex() + ", 0x" + - fromLibsnarkBigint(aff.X.c0.as_bigint()).hex() + "], [0x" + - fromLibsnarkBigint(aff.Y.c1.as_bigint()).hex() + ", 0x" + - fromLibsnarkBigint(aff.Y.c0.as_bigint()).hex() + "])"; -} - - -void testProof(libsnark::r1cs_ppzksnark_verification_key const& _vk) -{ - using ppT = libsnark::alt_bn128_pp; - using Fq = libsnark::alt_bn128_Fq; - using Fq2 = libsnark::alt_bn128_Fq2; - using G1 = libsnark::alt_bn128_G1; - using G2 = libsnark::alt_bn128_G2; - libsnark::r1cs_ppzksnark_primary_input primary_input; - libsnark::r1cs_ppzksnark_proof proof; - - primary_input.emplace_back("13986731495506593864492662381614386532349950841221768152838255933892789078521"); - primary_input.emplace_back("622860516154313070522697309645122400675542217310916019527100517240519630053"); - primary_input.emplace_back("11094488463398718754251685950409355128550342438297986977413505294941943071569"); - primary_input.emplace_back("6627643779954497813586310325594578844876646808666478625705401786271515864467"); - primary_input.emplace_back("2957286918163151606545409668133310005545945782087581890025685458369200827463"); - primary_input.emplace_back("1384290496819542862903939282897996566903332587607290986044945365745128311081"); - primary_input.emplace_back("5613571677741714971687805233468747950848449704454346829971683826953541367271"); - primary_input.emplace_back("9643208548031422463313148630985736896287522941726746581856185889848792022807"); - primary_input.emplace_back("18066496933330839731877828156604"); - - proof.g_A.g = G1( - Fq("12873740738727497448187997291915224677121726020054032516825496230827252793177"), - Fq("21804419174137094775122804775419507726154084057848719988004616848382402162497"), - Fq::one() - ); - proof.g_A.h = G1( - Fq("7742452358972543465462254569134860944739929848367563713587808717088650354556"), - Fq("7324522103398787664095385319014038380128814213034709026832529060148225837366"), - Fq::one() - ); - proof.g_B.g = G2( - Fq2( - Fq("15588556568726919713003060429893850972163943674590384915350025440408631945055"), - Fq("8176651290984905087450403379100573157708110416512446269839297438960217797614") - ), - Fq2( - Fq("4265071979090628150845437155927259896060451682253086069461962693761322642015"), - Fq("15347511022514187557142999444367533883366476794364262773195059233657571533367") - ), - Fq2::one() - ); - proof.g_B.h = G1( - Fq("2979746655438963305714517285593753729335852012083057917022078236006592638393"), - Fq("6470627481646078059765266161088786576504622012540639992486470834383274712950"), - Fq::one() - ); - proof.g_C.g = G1( - Fq("6851077925310461602867742977619883934042581405263014789956638244065803308498"), - Fq("10336382210592135525880811046708757754106524561907815205241508542912494488506"), - Fq::one() - ); - proof.g_C.h = G1( - Fq("12491625890066296859584468664467427202390981822868257437245835716136010795448"), - Fq("13818492518017455361318553880921248537817650587494176379915981090396574171686"), - Fq::one() - ); - proof.g_H = G1( - Fq("12091046215835229523641173286701717671667447745509192321596954139357866668225"), - Fq("14446807589950902476683545679847436767890904443411534435294953056557941441758"), - Fq::one() - ); - proof.g_K = G1( - Fq("21341087976609916409401737322664290631992568431163400450267978471171152600502"), - Fq("2942165230690572858696920423896381470344658299915828986338281196715687693170"), - Fq::one() - ); - - - cout << "Verifying..." << endl; - bool verified = libsnark::r1cs_ppzksnark_verifier_strong_IC(_vk, primary_input, proof); - cout << "Verified: " << verified << endl; -} - -void dev::crypto::exportVK(string const& _VKFilename) -{ - initLibSnark(); - - std::stringstream ss; - std::ifstream fh(_VKFilename, std::ios::binary); - - if (!fh.is_open()) - throw std::runtime_error("could not load param file at " + _VKFilename); - - ss << fh.rdbuf(); - fh.close(); - - ss.rdbuf()->pubseekpos(0, std::ios_base::in); - - libsnark::r1cs_ppzksnark_verification_key verificationKey; - ss >> verificationKey; - -// testProof(verificationKey); -// return; - - unsigned icLength = verificationKey.encoded_IC_query.rest.indices.size() + 1; - - - cout << "\tfunction verifyingKey() internal returns (VerifyingKey vk) {" << endl; - cout << "\t\tvk.A = " << outputPointG2Affine(verificationKey.alphaA_g2) << ";" << endl; - cout << "\t\tvk.B = " << outputPointG1Affine(verificationKey.alphaB_g1) << ";" << endl; - cout << "\t\tvk.C = " << outputPointG2Affine(verificationKey.alphaC_g2) << ";" << endl; - cout << "\t\tvk.gamma = " << outputPointG2Affine(verificationKey.gamma_g2) << ";" << endl; - cout << "\t\tvk.gammaBeta1 = " << outputPointG1Affine(verificationKey.gamma_beta_g1) << ";" << endl; - cout << "\t\tvk.gammaBeta2 = " << outputPointG2Affine(verificationKey.gamma_beta_g2) << ";" << endl; - cout << "\t\tvk.Z = " << outputPointG2Affine(verificationKey.rC_Z_g2) << ";" << endl; - cout << "\t\tvk.IC = new Pairing.G1Point[](" << icLength << ");" << endl; - cout << "\t\tvk.IC[0] = " << outputPointG1Affine(verificationKey.encoded_IC_query.first) << ";" << endl; - for (size_t i = 1; i < icLength; ++i) - { - auto vkICi = outputPointG1Affine(verificationKey.encoded_IC_query.rest.values[i - 1]); - cout << "\t\tvk.IC[" << i << "] = " << vkICi << ";" << endl; - } - cout << "\t\t}" << endl; - cout << "\tfunction verify(uint[] input, Proof proof) internal returns (bool) {" << endl; - cout << "\t\tVerifyingKey memory vk = verifyingKey();" << endl; - cout << "\t\tif (input.length + 1 != vk.IC.length) throw;" << endl; - cout << "\t\t// Compute the linear combination vk_x" << endl; - cout << "\t\tPairing.G1Point memory vk_x = vk.IC[0];" << endl; - for (size_t i = 0; i < verificationKey.encoded_IC_query.rest.indices.size(); ++i) - cout << "\t\tvk_x = Pairing.add(vk_x, Pairing.mul(vk.IC[" << (i + 1) << "], input[" << i << "]));" << endl; - cout << "\t\tif (!Pairing.pairingProd2(proof.A, vk.A, Pairing.negate(proof.A_p), Pairing.P2())) return false;" << endl; - cout << "\t\tif (!Pairing.pairingProd2(vk.B, proof.B, Pairing.negate(proof.B_p), Pairing.P2())) return false;" << endl; - cout << "\t\tif (!Pairing.pairingProd2(proof.C, vk.C, Pairing.negate(proof.C_p), Pairing.P2())) return false;" << endl; - cout << "\t\tif (!Pairing.pairingProd3(proof.K, vk.gamma, Pairing.negate(Pairing.add(vk_x, Pairing.add(proof.A, proof.C))), vk.gammaBeta2, Pairing.negate(vk.gammaBeta1), proof.B)) return false;" << endl; - cout << "\t\tif (!Pairing.pairingProd3(" << endl; - cout << "\t\t\t\tPairing.add(vk_x, proof.A), proof.B," << endl; - cout << "\t\t\t\tPairing.negate(proof.H), vk.Z," << endl; - cout << "\t\t\t\tPairing.negate(proof.C), Pairing.P2()" << endl; - cout << "\t\t)) return false;" << endl; - cout << "\t\treturn true;" << endl; - cout << "\t}" << endl; -} -#endif - } } diff --git a/test/tools/jsontests/StateTests.cpp b/test/tools/jsontests/StateTests.cpp index 900ff64831c..4522628ccd0 100644 --- a/test/tools/jsontests/StateTests.cpp +++ b/test/tools/jsontests/StateTests.cpp @@ -177,6 +177,7 @@ BOOST_AUTO_TEST_CASE(stRevertTest){} BOOST_AUTO_TEST_CASE(stStackTests){} BOOST_AUTO_TEST_CASE(stStaticCall){} BOOST_AUTO_TEST_CASE(stReturnDataTest){} +BOOST_AUTO_TEST_CASE(stZeroKnowledge){} //Stress Tests BOOST_AUTO_TEST_CASE(stAttackTest){} diff --git a/test/tools/libtesteth/ImportTest.cpp b/test/tools/libtesteth/ImportTest.cpp index 84bd83cd61b..d73c427b7f5 100644 --- a/test/tools/libtesteth/ImportTest.cpp +++ b/test/tools/libtesteth/ImportTest.cpp @@ -600,7 +600,7 @@ void ImportTest::checkGeneralTestSectionSearch(json_spirit::mObject const& _expe } } else if (_expects.count("hash")) - BOOST_CHECK_MESSAGE(_expects.at("hash").get_str() == toHex(t.postState.rootHash().asBytes(), 2, HexPrefix::Add), TestOutputHelper::testName() + " Expected another postState hash! " + trInfo); + BOOST_CHECK_MESSAGE(_expects.at("hash").get_str() == toHex(t.postState.rootHash().asBytes(), 2, HexPrefix::Add), TestOutputHelper::testName() + " Expected another postState hash! expected: " + _expects.at("hash").get_str() + " actual: " + toHex(t.postState.rootHash().asBytes()) + " in " + trInfo); else BOOST_ERROR(TestOutputHelper::testName() + " Expect section or postState missing some fields!"); From a1b0264f6c68c9f480b3beac5c1b18689a8d55c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 9 Jun 2017 12:51:55 +0200 Subject: [PATCH 08/10] snark: Cleanups and tests --- libdevcrypto/Exceptions.h | 1 - libdevcrypto/LibSnark.cpp | 106 ++++++++---------- libdevcrypto/LibSnark.h | 2 - test/CMakeLists.txt | 1 - .../{ => unittests}/libdevcrypto/LibSnark.cpp | 35 ++++++ 5 files changed, 81 insertions(+), 64 deletions(-) rename test/{ => unittests}/libdevcrypto/LibSnark.cpp (92%) diff --git a/libdevcrypto/Exceptions.h b/libdevcrypto/Exceptions.h index bece7c51574..858374bda8a 100644 --- a/libdevcrypto/Exceptions.h +++ b/libdevcrypto/Exceptions.h @@ -30,7 +30,6 @@ namespace crypto /// Rare malfunction of cryptographic functions. DEV_SIMPLE_EXCEPTION(CryptoException); -DEV_SIMPLE_EXCEPTION(InvalidEncoding); } } diff --git a/libdevcrypto/LibSnark.cpp b/libdevcrypto/LibSnark.cpp index 976dc6396b0..a429e45ab3a 100644 --- a/libdevcrypto/LibSnark.cpp +++ b/libdevcrypto/LibSnark.cpp @@ -14,9 +14,6 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** - * @file LibSnark.cpp - */ #include @@ -26,13 +23,9 @@ #include #include -#include - -#include +#include #include -#include - using namespace std; using namespace dev; using namespace dev::crypto; @@ -40,40 +33,41 @@ using namespace dev::crypto; namespace { -std::once_flag initLibSnarkFlag; +DEV_SIMPLE_EXCEPTION(InvalidEncoding); -void initLibSnark() +void initLibSnark() noexcept { - call_once(initLibSnarkFlag, - [](){ - cnote << "initializing libsnark."; - libff::inhibit_profiling_info = true; - libff::inhibit_profiling_counters = true; - libff::alt_bn128_pp::init_public_params(); - }); + static bool s_initialized = []() noexcept + { + libff::inhibit_profiling_info = true; + libff::inhibit_profiling_counters = true; + libff::alt_bn128_pp::init_public_params(); + return true; + }(); + (void)s_initialized; } libff::bigint toLibsnarkBigint(h256 const& _x) { - libff::bigint x; - auto const N = x.N; - constexpr size_t L = sizeof(x.data[0]); - static_assert(sizeof(mp_limb_t) == L, "Unexpected limb size in libff:bigint."); - for (unsigned i = 0; i < N; i++) - for (unsigned j = 0; j < L; j++) - x.data[N - 1 - i] |= (mp_limb_t)(_x[i * L + j]) << (8 * (L - 1 - j)); - return x; + libff::bigint b; + auto const N = b.N; + constexpr size_t L = sizeof(b.data[0]); + static_assert(sizeof(mp_limb_t) == L, "Unexpected limb size in libff::bigint."); + for (size_t i = 0; i < N; i++) + for (size_t j = 0; j < L; j++) + b.data[N - 1 - i] |= mp_limb_t(_x[i * L + j]) << (8 * (L - 1 - j)); + return b; } -h256 fromLibsnarkBigint(libff::bigint const& _x) +h256 fromLibsnarkBigint(libff::bigint const& _b) { - auto const N = _x.N; - constexpr size_t L = sizeof(_x.data[0]); - static_assert(sizeof(mp_limb_t) == L, "Unexpected limb size in libff:bigint."); + static size_t const N = static_cast(_b.N); + static size_t const L = sizeof(_b.data[0]); + static_assert(sizeof(mp_limb_t) == L, "Unexpected limb size in libff::bigint."); h256 x; - for (unsigned i = 0; i < N; i++) - for (unsigned j = 0; j < L; j++) - x[i * L + j] = uint8_t((mp_limb_t)(_x.data[N - 1 - i]) >> (8 * (L - 1 - j))); + for (size_t i = 0; i < N; i++) + for (size_t j = 0; j < L; j++) + x[i * L + j] = uint8_t(_b.data[N - 1 - i] >> (8 * (L - 1 - j))); return x; } @@ -82,6 +76,7 @@ libff::alt_bn128_Fq decodeFqElement(dev::bytesConstRef _data) // h256::AlignLeft ensures that the h256 is zero-filled on the right if _data // is too short. h256 xbin(_data, h256::AlignLeft); + // TODO: Consider using a compiler time constant for comparison. if (u256(xbin) >= u256(fromLibsnarkBigint(libff::alt_bn128_Fq::mod))) BOOST_THROW_EXCEPTION(InvalidEncoding()); return toLibsnarkBigint(xbin); @@ -89,11 +84,11 @@ libff::alt_bn128_Fq decodeFqElement(dev::bytesConstRef _data) libff::alt_bn128_G1 decodePointG1(dev::bytesConstRef _data) { - libff::alt_bn128_Fq X = decodeFqElement(_data.cropped(0)); - libff::alt_bn128_Fq Y = decodeFqElement(_data.cropped(32)); - if (X == libff::alt_bn128_Fq::zero() && Y == libff::alt_bn128_Fq::zero()) + libff::alt_bn128_Fq x = decodeFqElement(_data.cropped(0)); + libff::alt_bn128_Fq y = decodeFqElement(_data.cropped(32)); + if (x == libff::alt_bn128_Fq::zero() && y == libff::alt_bn128_Fq::zero()) return libff::alt_bn128_G1::zero(); - libff::alt_bn128_G1 p(X, Y, libff::alt_bn128_Fq::one()); + libff::alt_bn128_G1 p(x, y, libff::alt_bn128_Fq::one()); if (!p.is_well_formed()) BOOST_THROW_EXCEPTION(InvalidEncoding()); return p; @@ -102,7 +97,7 @@ libff::alt_bn128_G1 decodePointG1(dev::bytesConstRef _data) bytes encodePointG1(libff::alt_bn128_G1 _p) { if (_p.is_zero()) - return h256().asBytes() + h256().asBytes(); + return bytes(64, 0); _p.to_affine_coordinates(); return fromLibsnarkBigint(_p.X.as_bigint()).asBytes() + @@ -119,14 +114,13 @@ libff::alt_bn128_Fq2 decodeFq2Element(dev::bytesConstRef _data) ); } - libff::alt_bn128_G2 decodePointG2(dev::bytesConstRef _data) { - libff::alt_bn128_Fq2 X = decodeFq2Element(_data); - libff::alt_bn128_Fq2 Y = decodeFq2Element(_data.cropped(64)); - if (X == libff::alt_bn128_Fq2::zero() && Y == libff::alt_bn128_Fq2::zero()) + libff::alt_bn128_Fq2 const x = decodeFq2Element(_data); + libff::alt_bn128_Fq2 const y = decodeFq2Element(_data.cropped(64)); + if (x == libff::alt_bn128_Fq2::zero() && y == libff::alt_bn128_Fq2::zero()) return libff::alt_bn128_G2::zero(); - libff::alt_bn128_G2 p(X, Y, libff::alt_bn128_Fq2::one()); + libff::alt_bn128_G2 p(x, y, libff::alt_bn128_Fq2::one()); if (!p.is_well_formed()) BOOST_THROW_EXCEPTION(InvalidEncoding()); return p; @@ -139,22 +133,20 @@ pair dev::crypto::alt_bn128_pairing_product(dev::bytesConstRef _in) // Input: list of pairs of G1 and G2 points // Output: 1 if pairing evaluates to 1, 0 otherwise (left-padded to 32 bytes) - bool result = true; size_t constexpr pairSize = 2 * 32 + 2 * 64; size_t const pairs = _in.size() / pairSize; if (pairs * pairSize != _in.size()) - { // Invalid length. - return make_pair(false, bytes()); - } + return {false, bytes{}}; + try { initLibSnark(); libff::alt_bn128_Fq12 x = libff::alt_bn128_Fq12::one(); for (size_t i = 0; i < pairs; ++i) { - dev::bytesConstRef pair = _in.cropped(i * pairSize, pairSize); - libff::alt_bn128_G2 p = decodePointG2(pair.cropped(2 * 32)); + bytesConstRef const pair = _in.cropped(i * pairSize, pairSize); + libff::alt_bn128_G2 const p = decodePointG2(pair.cropped(2 * 32)); if (-libff::alt_bn128_G2::scalar_field::one() * p + p != libff::alt_bn128_G2::zero()) // p is not an element of the group (has wrong order) return {false, bytes()}; @@ -167,21 +159,18 @@ pair dev::crypto::alt_bn128_pairing_product(dev::bytesConstRef _in) libff::alt_bn128_precompute_G2(p) ); } - result = libff::alt_bn128_final_exponentiation(x) == libff::alt_bn128_GT::one(); + bool const result = libff::alt_bn128_final_exponentiation(x) == libff::alt_bn128_GT::one(); + return {true, h256{result}.asBytes()}; } catch (InvalidEncoding const&) { - return make_pair(false, bytes()); + return {false, bytes{}}; } catch (...) { cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; - return make_pair(false, bytes()); + return {false, bytes{}}; } - - bytes res(32, 0); - res[31] = unsigned(result); - return {true, res}; } pair dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in) @@ -191,7 +180,6 @@ pair dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in) initLibSnark(); libff::alt_bn128_G1 const p1 = decodePointG1(_in); libff::alt_bn128_G1 const p2 = decodePointG1(_in.cropped(32 * 2)); - return {true, encodePointG1(p1 + p2)}; } catch (InvalidEncoding const&) @@ -201,7 +189,7 @@ pair dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in) { cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; } - return make_pair(false, bytes()); + return {false, bytes{}}; } pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) @@ -210,9 +198,7 @@ pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) { initLibSnark(); libff::alt_bn128_G1 const p = decodePointG1(_in.cropped(0)); - libff::alt_bn128_G1 const result = toLibsnarkBigint(h256(_in.cropped(64), h256::AlignLeft)) * p; - return {true, encodePointG1(result)}; } catch (InvalidEncoding const&) @@ -222,5 +208,5 @@ pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) { cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; } - return make_pair(false, bytes()); + return {false, bytes{}}; } diff --git a/libdevcrypto/LibSnark.h b/libdevcrypto/LibSnark.h index 92c2f721706..693957a561b 100644 --- a/libdevcrypto/LibSnark.h +++ b/libdevcrypto/LibSnark.h @@ -31,7 +31,5 @@ std::pair alt_bn128_pairing_product(bytesConstRef _in); std::pair alt_bn128_G1_add(bytesConstRef _in); std::pair alt_bn128_G1_mul(bytesConstRef _in); -void exportVK(std::string const& _VKFilename); - } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b987ee2fe33..612d5fa0e2c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,3 @@ - file(GLOB_RECURSE SRC_LIST *.cpp *.sol) # search for test names and create ctest tests diff --git a/test/libdevcrypto/LibSnark.cpp b/test/unittests/libdevcrypto/LibSnark.cpp similarity index 92% rename from test/libdevcrypto/LibSnark.cpp rename to test/unittests/libdevcrypto/LibSnark.cpp index 7d721f4edeb..f5040ef8ef9 100644 --- a/test/libdevcrypto/LibSnark.cpp +++ b/test/unittests/libdevcrypto/LibSnark.cpp @@ -89,6 +89,27 @@ BOOST_AUTO_TEST_CASE(ecadd) BOOST_CHECK(result.second == expectation); } +BOOST_AUTO_TEST_CASE(fieldPointInvalid) +{ + u256 const pMod{"21888242871839275222246405745257275088696311157297823662689037894645226208583"}; + + bytes input = toBigEndian(pMod); + BOOST_CHECK(!alt_bn128_G1_add(ref(input)).first); + BOOST_CHECK(!alt_bn128_G1_mul(ref(input)).first); + + input = toBigEndian(pMod + 1); + BOOST_CHECK(!alt_bn128_G1_add(ref(input)).first); + BOOST_CHECK(!alt_bn128_G1_mul(ref(input)).first); + + input = bytes(32, 0) + toBigEndian(pMod); + BOOST_CHECK(!alt_bn128_G1_add(ref(input)).first); + BOOST_CHECK(!alt_bn128_G1_mul(ref(input)).first); + + input = bytes(32, 0) + toBigEndian(pMod + 1); + BOOST_CHECK(!alt_bn128_G1_add(ref(input)).first); + BOOST_CHECK(!alt_bn128_G1_mul(ref(input)).first); +} + BOOST_AUTO_TEST_CASE(invalid) { bytes x = @@ -303,6 +324,20 @@ BOOST_AUTO_TEST_CASE(pairing) BOOST_REQUIRE(ret.second == toBigEndian(u256(0))); } +BOOST_AUTO_TEST_CASE(pairingNullInput) +{ + // TODO: Maybe the empty input should also be considered invalid? + auto r = pairingprod_helper({}); + BOOST_CHECK(r.first); + + r = pairingprod_helper(bytes(2 * 32 + 2 * 64, 0)); + BOOST_CHECK(r.first); + + // Invalid length of input. + r = pairingprod_helper(bytes(2 * 32 + 2 * 64 + 1, 0)); + BOOST_CHECK(!r.first); +} + BOOST_AUTO_TEST_SUITE_END() } From 969f83b8a369c91c6c8baf62d20dcf75cd46ae51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 12 Jun 2017 18:29:22 +0200 Subject: [PATCH 09/10] snark: Handle only expected exceptions Handle only expected exceptions around snark primitives precompiled, pass anything else higher. --- libdevcrypto/LibSnark.cpp | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/libdevcrypto/LibSnark.cpp b/libdevcrypto/LibSnark.cpp index a429e45ab3a..38ea4d4bf0a 100644 --- a/libdevcrypto/LibSnark.cpp +++ b/libdevcrypto/LibSnark.cpp @@ -164,11 +164,7 @@ pair dev::crypto::alt_bn128_pairing_product(dev::bytesConstRef _in) } catch (InvalidEncoding const&) { - return {false, bytes{}}; - } - catch (...) - { - cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; + // Signal the call failure for invalid input. return {false, bytes{}}; } } @@ -184,12 +180,9 @@ pair dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in) } catch (InvalidEncoding const&) { + // Signal the call failure for invalid input. + return {false, bytes{}}; } - catch (...) - { - cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; - } - return {false, bytes{}}; } pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) @@ -203,10 +196,7 @@ pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) } catch (InvalidEncoding const&) { + // Signal the call failure for invalid input. + return {false, bytes{}}; } - catch (...) - { - cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure."; - } - return {false, bytes{}}; } From 8eee78cbfad73ca7c58d696d1e480df3fca2d5ff Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Tue, 13 Jun 2017 15:34:32 +0200 Subject: [PATCH 10/10] Update tests --- test/jsontests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jsontests b/test/jsontests index b3dd02c7b02..ddbf0981003 160000 --- a/test/jsontests +++ b/test/jsontests @@ -1 +1 @@ -Subproject commit b3dd02c7b0200551ccdf9972366e01bbbac4500d +Subproject commit ddbf098100383eb533adfc97a891949a919ec741