Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Commit

Permalink
snark: Cleanups and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
chfast committed Jun 12, 2017
1 parent 8d097bf commit 5b383c5
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 64 deletions.
1 change: 0 additions & 1 deletion libdevcrypto/Exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ namespace crypto

/// Rare malfunction of cryptographic functions.
DEV_SIMPLE_EXCEPTION(CryptoException);
DEV_SIMPLE_EXCEPTION(InvalidEncoding);

}
}
106 changes: 46 additions & 60 deletions libdevcrypto/LibSnark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file LibSnark.cpp
*/

#include <libdevcrypto/LibSnark.h>

Expand All @@ -26,54 +23,51 @@
#include <algebra/curves/alt_bn128/alt_bn128_pp.hpp>
#include <common/profiling.hpp>

#include <libdevcrypto/Exceptions.h>

#include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Log.h>

#include <mutex>

using namespace std;
using namespace dev;
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<libff::alt_bn128_q_limbs> toLibsnarkBigint(h256 const& _x)
{
libff::bigint<libff::alt_bn128_q_limbs> 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<libff::alt_bn128_q_limbs> 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<libff::alt_bn128_q_limbs> const& _x)
h256 fromLibsnarkBigint(libff::bigint<libff::alt_bn128_q_limbs> 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<size_t>(_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;
}

Expand All @@ -82,18 +76,19 @@ 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())
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;
Expand All @@ -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() +
Expand All @@ -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;
Expand All @@ -139,22 +133,20 @@ pair<bool, bytes> 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()};
Expand All @@ -167,21 +159,18 @@ pair<bool, bytes> 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<bool, bytes> dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in)
Expand All @@ -191,7 +180,6 @@ pair<bool, bytes> 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&)
Expand All @@ -201,7 +189,7 @@ pair<bool, bytes> 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<bool, bytes> dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in)
Expand All @@ -210,9 +198,7 @@ pair<bool, bytes> 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&)
Expand All @@ -222,5 +208,5 @@ pair<bool, bytes> 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{}};
}
2 changes: 0 additions & 2 deletions libdevcrypto/LibSnark.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,5 @@ std::pair<bool, bytes> alt_bn128_pairing_product(bytesConstRef _in);
std::pair<bool, bytes> alt_bn128_G1_add(bytesConstRef _in);
std::pair<bool, bytes> alt_bn128_G1_mul(bytesConstRef _in);

void exportVK(std::string const& _VKFilename);

}
}
1 change: 0 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

file(GLOB_RECURSE SRC_LIST *.cpp *.sol)

# search for test names and create ctest tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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()

}
Expand Down

0 comments on commit 5b383c5

Please sign in to comment.