Ethereum  PoC-8
The C++ Implementation of Ethereum
LibSnark.cpp
Go to the documentation of this file.
1 /*
2  This file is part of cpp-ethereum.
3 
4  cpp-ethereum is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  cpp-ethereum is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <libdevcrypto/LibSnark.h>
19 
20 #include <libff/algebra/curves/alt_bn128/alt_bn128_g1.hpp>
21 #include <libff/algebra/curves/alt_bn128/alt_bn128_g2.hpp>
22 #include <libff/algebra/curves/alt_bn128/alt_bn128_pairing.hpp>
23 #include <libff/algebra/curves/alt_bn128/alt_bn128_pp.hpp>
24 #include <libff/common/profiling.hpp>
25 
26 #include <libdevcore/Exceptions.h>
27 #include <libdevcore/Log.h>
28 
29 using namespace std;
30 using namespace dev;
31 using namespace dev::crypto;
32 
33 namespace
34 {
35 
36 DEV_SIMPLE_EXCEPTION(InvalidEncoding);
37 
38 void initLibSnark() noexcept
39 {
40  static bool s_initialized = []() noexcept
41  {
42  libff::inhibit_profiling_info = true;
43  libff::inhibit_profiling_counters = true;
44  libff::alt_bn128_pp::init_public_params();
45  return true;
46  }();
47  (void)s_initialized;
48 }
49 
50 libff::bigint<libff::alt_bn128_q_limbs> toLibsnarkBigint(h256 const& _x)
51 {
52  libff::bigint<libff::alt_bn128_q_limbs> b;
53  auto const N = b.N;
54  constexpr size_t L = sizeof(b.data[0]);
55  static_assert(sizeof(mp_limb_t) == L, "Unexpected limb size in libff::bigint.");
56  for (size_t i = 0; i < N; i++)
57  for (size_t j = 0; j < L; j++)
58  b.data[N - 1 - i] |= mp_limb_t(_x[i * L + j]) << (8 * (L - 1 - j));
59  return b;
60 }
61 
62 h256 fromLibsnarkBigint(libff::bigint<libff::alt_bn128_q_limbs> const& _b)
63 {
64  static size_t const N = static_cast<size_t>(_b.N);
65  static size_t const L = sizeof(_b.data[0]);
66  static_assert(sizeof(mp_limb_t) == L, "Unexpected limb size in libff::bigint.");
67  h256 x;
68  for (size_t i = 0; i < N; i++)
69  for (size_t j = 0; j < L; j++)
70  x[i * L + j] = uint8_t(_b.data[N - 1 - i] >> (8 * (L - 1 - j)));
71  return x;
72 }
73 
74 libff::alt_bn128_Fq decodeFqElement(dev::bytesConstRef _data)
75 {
76  // h256::AlignLeft ensures that the h256 is zero-filled on the right if _data
77  // is too short.
78  h256 xbin(_data, h256::AlignLeft);
79  // TODO: Consider using a compiler time constant for comparison.
80  if (u256(xbin) >= u256(fromLibsnarkBigint(libff::alt_bn128_Fq::mod)))
81  BOOST_THROW_EXCEPTION(InvalidEncoding());
82  return toLibsnarkBigint(xbin);
83 }
84 
85 libff::alt_bn128_G1 decodePointG1(dev::bytesConstRef _data)
86 {
87  libff::alt_bn128_Fq x = decodeFqElement(_data.cropped(0));
88  libff::alt_bn128_Fq y = decodeFqElement(_data.cropped(32));
89  if (x == libff::alt_bn128_Fq::zero() && y == libff::alt_bn128_Fq::zero())
90  return libff::alt_bn128_G1::zero();
91  libff::alt_bn128_G1 p(x, y, libff::alt_bn128_Fq::one());
92  if (!p.is_well_formed())
93  BOOST_THROW_EXCEPTION(InvalidEncoding());
94  return p;
95 }
96 
97 bytes encodePointG1(libff::alt_bn128_G1 _p)
98 {
99  if (_p.is_zero())
100  return bytes(64, 0);
101  _p.to_affine_coordinates();
102  return
103  fromLibsnarkBigint(_p.X.as_bigint()).asBytes() +
104  fromLibsnarkBigint(_p.Y.as_bigint()).asBytes();
105 }
106 
107 libff::alt_bn128_Fq2 decodeFq2Element(dev::bytesConstRef _data)
108 {
109  // Encoding: c1 (256 bits) c0 (256 bits)
110  // "Big endian", just like the numbers
111  return libff::alt_bn128_Fq2(
112  decodeFqElement(_data.cropped(32)),
113  decodeFqElement(_data.cropped(0))
114  );
115 }
116 
117 libff::alt_bn128_G2 decodePointG2(dev::bytesConstRef _data)
118 {
119  libff::alt_bn128_Fq2 const x = decodeFq2Element(_data);
120  libff::alt_bn128_Fq2 const y = decodeFq2Element(_data.cropped(64));
121  if (x == libff::alt_bn128_Fq2::zero() && y == libff::alt_bn128_Fq2::zero())
122  return libff::alt_bn128_G2::zero();
123  libff::alt_bn128_G2 p(x, y, libff::alt_bn128_Fq2::one());
124  if (!p.is_well_formed())
125  BOOST_THROW_EXCEPTION(InvalidEncoding());
126  return p;
127 }
128 
129 }
130 
132 {
133  // Input: list of pairs of G1 and G2 points
134  // Output: 1 if pairing evaluates to 1, 0 otherwise (left-padded to 32 bytes)
135 
136  size_t constexpr pairSize = 2 * 32 + 2 * 64;
137  size_t const pairs = _in.size() / pairSize;
138  if (pairs * pairSize != _in.size())
139  // Invalid length.
140  return {false, bytes{}};
141 
142  try
143  {
144  initLibSnark();
145  libff::alt_bn128_Fq12 x = libff::alt_bn128_Fq12::one();
146  for (size_t i = 0; i < pairs; ++i)
147  {
148  bytesConstRef const pair = _in.cropped(i * pairSize, pairSize);
149  libff::alt_bn128_G1 const g1 = decodePointG1(pair);
150  libff::alt_bn128_G2 const p = decodePointG2(pair.cropped(2 * 32));
151  if (-libff::alt_bn128_G2::scalar_field::one() * p + p != libff::alt_bn128_G2::zero())
152  // p is not an element of the group (has wrong order)
153  return {false, bytes()};
154  if (p.is_zero() || g1.is_zero())
155  continue; // the pairing is one
156  x = x * libff::alt_bn128_miller_loop(
157  libff::alt_bn128_precompute_G1(g1),
158  libff::alt_bn128_precompute_G2(p)
159  );
160  }
161  bool const result = libff::alt_bn128_final_exponentiation(x) == libff::alt_bn128_GT::one();
162  return {true, h256{result}.asBytes()};
163  }
164  catch (InvalidEncoding const&)
165  {
166  // Signal the call failure for invalid input.
167  return {false, bytes{}};
168  }
169 }
170 
172 {
173  try
174  {
175  initLibSnark();
176  libff::alt_bn128_G1 const p1 = decodePointG1(_in);
177  libff::alt_bn128_G1 const p2 = decodePointG1(_in.cropped(32 * 2));
178  return {true, encodePointG1(p1 + p2)};
179  }
180  catch (InvalidEncoding const&)
181  {
182  // Signal the call failure for invalid input.
183  return {false, bytes{}};
184  }
185 }
186 
188 {
189  try
190  {
191  initLibSnark();
192  libff::alt_bn128_G1 const p = decodePointG1(_in.cropped(0));
193  libff::alt_bn128_G1 const result = toLibsnarkBigint(h256(_in.cropped(64), h256::AlignLeft)) * p;
194  return {true, encodePointG1(result)};
195  }
196  catch (InvalidEncoding const&)
197  {
198  // Signal the call failure for invalid input.
199  return {false, bytes{}};
200  }
201 }
dev::vector_ref< byte const >
dev::crypto::alt_bn128_G1_add
std::pair< bool, bytes > alt_bn128_G1_add(bytesConstRef _in)
Definition: LibSnark.cpp:171
dev::h256
FixedHash< 32 > h256
Definition: FixedHash.h:356
dev::crypto::alt_bn128_pairing_product
std::pair< bool, bytes > alt_bn128_pairing_product(bytesConstRef _in)
Definition: LibSnark.cpp:131
DEV_SIMPLE_EXCEPTION
#define DEV_SIMPLE_EXCEPTION(X)
Definition: Exceptions.h:43
LibSnark.h
dev::FixedHash< 32 >
dev::crypto
Definition: Common.h:180
dev::vector_ref::cropped
vector_ref< _T > cropped(size_t _begin, size_t _count) const
Definition: vector_ref.h:60
dev::bytes
std::vector< byte > bytes
Definition: Common.h:72
Exceptions.h
dev::vector_ref::size
size_t size() const
Definition: vector_ref.h:53
std
Definition: FixedHash.h:393
dev::u256
boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void > > u256
Definition: Common.h:121
dev
Definition: Address.cpp:21
dev::FixedHash::asBytes
bytes asBytes() const
Definition: FixedHash.h:150
Log.h
dev::crypto::alt_bn128_G1_mul
std::pair< bool, bytes > alt_bn128_G1_mul(bytesConstRef _in)
Definition: LibSnark.cpp:187