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/CMakeLists.txt b/CMakeLists.txt index 4596748bf7d..7f13b871013 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,8 +60,7 @@ include(ProjectCryptopp) include(ProjectJsonCpp) include(ProjectJsonRpcCpp) include(ProjectSecp256k1) - - +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/ProjectMPIR.cmake b/cmake/ProjectMPIR.cmake new file mode 100644 index 00000000000..a3e7be9b9c9 --- /dev/null +++ b/cmake/ProjectMPIR.cmake @@ -0,0 +1,19 @@ +ExternalProject_Add(mpir + PREFIX ${CMAKE_SOURCE_DIR}/deps + DOWNLOAD_NAME mpir-cmake.tar.gz + DOWNLOAD_NO_PROGRESS TRUE + URL https://github.com/chfast/mpir/archive/cmake.tar.gz + URL_HASH SHA256=2bf35ad7aa2b61bf4c2dcdab9aab48b2b4f9d6db8b937be4b0b5fe3b5efbdfa6 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= + -DCMAKE_BUILD_TYPE=Debug + -DMPIR_GMP=On +) + +ExternalProject_Get_Property(mpir INSTALL_DIR) +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::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 new file mode 100644 index 00000000000..abdd166dc3c --- /dev/null +++ b/cmake/ProjectSnark.cmake @@ -0,0 +1,33 @@ +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-97d3fa6c.tar.gz + DOWNLOAD_NO_PROGRESS TRUE + 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) +add_dependencies(snark mpir) + +# 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 MPIR::mpir) +add_dependencies(Snark snark) +unset(INSTALL_DIR) 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..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 ${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 new file mode 100644 index 00000000000..38ea4d4bf0a --- /dev/null +++ b/libdevcrypto/LibSnark.cpp @@ -0,0 +1,202 @@ +/* + 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 . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::crypto; + +namespace +{ + +DEV_SIMPLE_EXCEPTION(InvalidEncoding); + +void initLibSnark() noexcept +{ + 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 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& _b) +{ + 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 (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; +} + +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); +} + +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()) + 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(libff::alt_bn128_G1 _p) +{ + if (_p.is_zero()) + return bytes(64, 0); + _p.to_affine_coordinates(); + return + fromLibsnarkBigint(_p.X.as_bigint()).asBytes() + + fromLibsnarkBigint(_p.Y.as_bigint()).asBytes(); +} + +libff::alt_bn128_Fq2 decodeFq2Element(dev::bytesConstRef _data) +{ + // Encoding: c1 (256 bits) c0 (256 bits) + // "Big endian", just like the numbers + return libff::alt_bn128_Fq2( + decodeFqElement(_data.cropped(32)), + decodeFqElement(_data.cropped(0)) + ); +} + +libff::alt_bn128_G2 decodePointG2(dev::bytesConstRef _data) +{ + 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()); + 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) + + size_t constexpr pairSize = 2 * 32 + 2 * 64; + size_t const pairs = _in.size() / pairSize; + if (pairs * pairSize != _in.size()) + // Invalid length. + return {false, bytes{}}; + + try + { + initLibSnark(); + libff::alt_bn128_Fq12 x = libff::alt_bn128_Fq12::one(); + for (size_t i = 0; i < pairs; ++i) + { + 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()}; + 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) + ); + } + bool const result = libff::alt_bn128_final_exponentiation(x) == libff::alt_bn128_GT::one(); + return {true, h256{result}.asBytes()}; + } + catch (InvalidEncoding const&) + { + // Signal the call failure for invalid input. + return {false, bytes{}}; + } +} + +pair dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in) +{ + try + { + 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&) + { + // Signal the call failure for invalid input. + return {false, bytes{}}; + } +} + +pair dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in) +{ + try + { + 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&) + { + // Signal the call failure for invalid input. + return {false, bytes{}}; + } +} diff --git a/libdevcrypto/LibSnark.h b/libdevcrypto/LibSnark.h new file mode 100644 index 00000000000..693957a561b --- /dev/null +++ b/libdevcrypto/LibSnark.h @@ -0,0 +1,35 @@ +/* + 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); + +} +} diff --git a/libethashseal/genesis/mainNetwork.cpp b/libethashseal/genesis/mainNetwork.cpp index 074698290d8..384e125d684 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": 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 1f42c7b8391..9bc50e2c8db 100644 --- a/libethashseal/genesis/metropolisTest.cpp +++ b/libethashseal/genesis/metropolisTest.cpp @@ -56,7 +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 } } }, - "0000000000000000000000000000000000000005": { "wei": "1", "precompiled": { "name": "modexp" } } + "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 c9a1c5735e7..cf7a94407a6 100644 --- a/libethcore/Precompiled.cpp +++ b/libethcore/Precompiled.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include using namespace std; using namespace dev; @@ -110,7 +111,7 @@ bigint parseBigEndianRightPadded(bytesConstRef _in, size_t _begin, size_t _count bigint ret = fromBigEndian(cropped); // shift as if we had right-padding zeroes ret <<= 8 * (_count - cropped.count()); - + return ret; } @@ -125,8 +126,8 @@ ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) 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); + + bytes ret(modLength); toBigEndian(result, ret); return {true, ret}; @@ -143,4 +144,24 @@ ETH_REGISTER_PRECOMPILED_PRICER(modexp)(bytesConstRef _in) 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/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/jsontests b/test/jsontests index dcbc5e771e9..ddbf0981003 160000 --- a/test/jsontests +++ b/test/jsontests @@ -1 +1 @@ -Subproject commit dcbc5e771e9e732ce369752707939649352965bd +Subproject commit ddbf098100383eb533adfc97a891949a919ec741 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!"); 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(); diff --git a/test/unittests/libdevcrypto/LibSnark.cpp b/test/unittests/libdevcrypto/LibSnark.cpp new file mode 100644 index 00000000000..f5040ef8ef9 --- /dev/null +++ b/test/unittests/libdevcrypto/LibSnark.cpp @@ -0,0 +1,344 @@ +/* + 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(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 = + 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_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() + +} +}