From b9d43f8834ff5397b9d44516f0a6ac896012ab3e Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 2 Jun 2021 00:19:06 +0300 Subject: [PATCH 01/13] increment build number script fix --- utils/increment_build_number.sh | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/utils/increment_build_number.sh b/utils/increment_build_number.sh index e9f78c08..c309f973 100644 --- a/utils/increment_build_number.sh +++ b/utils/increment_build_number.sh @@ -3,12 +3,11 @@ curr_path=${BASH_SOURCE%/*} version_file_path=../src/version.h.in pushd $curr_path + +# clear old local changes if any +git checkout -- src/* + git pull --ff-only -if [ $? -ne 0 ]; then - echo "Failed to pull" - popd - exit $? -fi build_no_before=`cat $version_file_path | grep 'PROJECT_VERSION_BUILD_NO ' | awk {'print $3'}` @@ -19,15 +18,11 @@ build_no_after=`cat $version_file_path | grep 'PROJECT_VERSION_BUILD_NO ' | awk echo "$build_no_before -> $build_no_after" echo $(pwd -P) + git status git commit -a -m"=== build number: $build_no_before -> $build_no_after ===" git push -if [ $? -ne 0 ]; then - echo "Failed to push" - popd - exit $? -fi echo "Build number was succesefully incremented." popd From 1a8c6b0dfa180c42cbd986e6a1be9bd3094559e3 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 2 Jun 2021 00:46:10 +0300 Subject: [PATCH 02/13] increment build number script fix 2 --- utils/increment_build_number.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/increment_build_number.sh b/utils/increment_build_number.sh index c309f973..3666c956 100644 --- a/utils/increment_build_number.sh +++ b/utils/increment_build_number.sh @@ -5,7 +5,7 @@ version_file_path=../src/version.h.in pushd $curr_path # clear old local changes if any -git checkout -- src/* +git checkout -- ../src/* git pull --ff-only From 907d428689c2d1f6647b3284bdbe58f0415e55d4 Mon Sep 17 00:00:00 2001 From: sowle Date: Thu, 3 Jun 2021 16:02:15 +0300 Subject: [PATCH 03/13] readme: versions update, boost link fix, grammar corrections, GUI/server emphasized --- README.md | 70 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 6af4daa1..83722e9f 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,73 @@ [![Coverity Scan](https://scan.coverity.com/projects/18767/badge.svg)](https://scan.coverity.com/projects/zanoproject) [![Discord](https://img.shields.io/discord/538361472691077130?label=discord&logo=discord)](https://discord.gg/wE3rmYY) -Building +## Cloning + +Be sure to clone the repository properly:\ +`$ git clone --recursive https://github.com/hyle-team/zano.git` + +# Building -------- -### Cloning - -Be sure to properly clone the repository: - -`$ git clone --recursive https://github.com/hyle-team/zano.git` ### Dependencies | component / version | minimum
(not recommended but may work) | recommended | most recent of what we have ever tested | |--|--|--|--| -| gcc (Linux) | 5.4.0 | 7.2.0 | 8.3.0 | +| gcc (Linux) | 5.4.0 | 7.4.0 | 8.3.0 | | llvm/clang (Linux) | UNKNOWN | 7.0.1 | 8.0.0 | -| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2015 (14.0 update 1) | 2017 (15.5.7) | 2019 | -| [XCode](https://developer.apple.com/downloads/) (macOS) | 7.3.1 | 9.2 | 9.2 | -| [CMake](https://cmake.org/download/) | 2.8.6 | 3.15.5 | 3.15.5 | -| [Boost](https://www.boost.org/users/download/) | 1.56 | 1.68 | 1.68 | -| [Qt](https://download.qt.io/archive/qt/) (only for GUI) | 5.8.0 | 5.11.2 | 5.13.2 | +| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2015 (14.0 update 1) | 2017 (15.9.0) | 2019 | +| [XCode](https://developer.apple.com/downloads/) (macOS) | 9.2 | 12.3 | 12.3 | +| [CMake](https://cmake.org/download/) | 2.8.6 | 3.15.5 | 3.18.1 | +| [Boost](https://www.boost.org/users/download/) | 1.56 | 1.68 | 1.69 | +| [Qt](https://download.qt.io/archive/qt/) (*only for GUI*) | 5.8.0 | 5.11.2 | 5.13.2 | ### Linux Recommended OS version: Ubuntu 18.04 LTS. 1. Prerequisites - 1. Prerequisites for server version: - - sudo apt-get install -y build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev cmake git screen - - 1. Prerequisites for GUI version: - sudo apt-get install -y build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev cmake git screen mesa-common-dev libglu1-mesa-dev` + [*server version*] + + sudo apt-get install -y build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev cmake git screen + + [*GUI version*] + + sudo apt-get install -y build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev cmake git screen mesa-common-dev libglu1-mesa-dev` 2. Download and build Boost - wget https://dl.bintray.com/boostorg/release/1.68.0/source/boost_1_68_0.tar.bz2 + wget https://boostorg.jfrog.io/artifactory/main/release/1.68.0/source/boost_1_68_0.tar.bz2 tar -xjf boost_1_68_0.tar.bz2 cd boost_1_68_0 ./bootstrap.sh --with-libraries=system,filesystem,thread,date_time,chrono,regex,serialization,atomic,program_options,locale,timer ./b2 -3. Install Qt +3. Install Qt\ +(*GUI version only, skip this step if you're building server version*) + + [*GUI version*] wget https://download.qt.io/new_archive/qt/5.11/5.11.2/qt-opensource-linux-x64-5.11.2.run chmod +x qt-opensource-linux-x64-5.11.2.run ./qt-opensource-linux-x64-5.11.2.run - Then follow the instructions in Wizard. Don't forget to tick WebEngine module! + Then follow the instructions in Wizard. Don't forget to tick the WebEngine module checkbox! + +4. Set environment variables properly\ +For instance, by adding the following lines to `~/.bashrc` + + [*server version*] + + export BOOST_ROOT=/home/user/boost_1_68_0 + + + [*GUI version*] -4. Set `BOOST_ROOT` and `QT_PREFIX_PATH` envinorment variables\ - For instance, by adding these lines to `~/.bashrc`: - export BOOST_ROOT=/home/user/boost_1_68_0 export QT_PREFIX_PATH=/home/user/Qt5.11.2/5.11.2/gcc_64 + 5. Building binaries 1. Building daemon and simplewallet: @@ -66,7 +78,7 @@ Recommended OS version: Ubuntu 18.04 LTS. cmake .. make -j1 daemon simplewallet - **NOTICE**: If you are building on machine with relatively high anount of RAM or with proper setting of virtual memory, then you can use `-j2` or `-j` option to speed up the building process. Use with caution. + **NOTICE**: If you are building on a machine with a relatively high amount of RAM or with the proper setting of virtual memory, then you can use `-j2` or `-j` option to speed up the building process. Use with caution. 1. Building GUI: @@ -75,6 +87,7 @@ Recommended OS version: Ubuntu 18.04 LTS. 7. Look for the binaries in `build` folder +
### Windows Recommended OS version: Windows 7 x64. 1. Install required prerequisites (Boost, Qt, CMake). @@ -83,13 +96,14 @@ Recommended OS version: Windows 7 x64. 4. Go to the build folder and open generated Zano.sln in MSVC. 5. Build. -In order to correctly deploy Qt GUI application you also need to do the following: +In order to correctly deploy Qt GUI application, you also need to do the following: 6. Copy Zano.exe to a folder (e.g. `depoy`). 7. Run `PATH_TO_QT\bin\windeployqt.exe deploy/Zano.exe`. 8. Copy folder `\src\gui\qt-daemon\html` to `deploy\html`. +
### macOS -Recommended OS version: macOS Sierra 10.12.6 x64. +Recommended OS version: macOS Sierra 10.15.4 x64. 1. Install required prerequisites. 2. Set environment variables as stated in `utils/macosx_build_config.command`. 3. `mkdir build`
`cd build`
`cmake ..`
`make` @@ -104,7 +118,7 @@ To build GUI application: e. Press “Create”, then “Done”.\ f. Make sure the certificate was added to keychain "System". If not—move it to "System".\ g. Double click the certificate you've just added, enter the trust section and under "When using this certificate" select "Always trust".\ - h. Unfold the certificate in Keychain Access window and double click underlying private key "Zano". Select "Access Control" tab, then select "Allow all applications to access this item". Click "Save Changes". + h. Unfold the certificate in Keychain Access window and double click the underlying private key "Zano". Select "Access Control" tab, then select "Allow all applications to access this item". Click "Save Changes". 2. Revise building script, comment out unwanted steps and run it: `utils/build_script_mac_osx.sh` 3. The application should be here: `/buid_mac_osx_64/release/src` From eda26e44d0663add591b200d40594a70c7d1e627 Mon Sep 17 00:00:00 2001 From: sowle Date: Thu, 3 Jun 2021 16:06:49 +0300 Subject: [PATCH 04/13] readme: more fixes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 83722e9f..0ef375ea 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ For instance, by adding the following lines to `~/.bashrc` 7. Look for the binaries in `build` folder -
+ ### Windows Recommended OS version: Windows 7 x64. 1. Install required prerequisites (Boost, Qt, CMake). @@ -101,7 +101,7 @@ In order to correctly deploy Qt GUI application, you also need to do the followi 7. Run `PATH_TO_QT\bin\windeployqt.exe deploy/Zano.exe`. 8. Copy folder `\src\gui\qt-daemon\html` to `deploy\html`. -
+ ### macOS Recommended OS version: macOS Sierra 10.15.4 x64. 1. Install required prerequisites. From e0a6468241da93bae683cd8504513a3b8f06c41e Mon Sep 17 00:00:00 2001 From: "crypto.sowle" Date: Thu, 3 Jun 2021 16:21:00 +0300 Subject: [PATCH 05/13] readme: more fixes --- README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0ef375ea..be80f313 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,12 @@ Be sure to clone the repository properly:\ | [Boost](https://www.boost.org/users/download/) | 1.56 | 1.68 | 1.69 | | [Qt](https://download.qt.io/archive/qt/) (*only for GUI*) | 5.8.0 | 5.11.2 | 5.13.2 | +Note:\ +[*server version*] denotes steps required for building command-line tools (daemon, simplewallet, etc.).\ +[*GUI version*] denotes steps required for building Zano executable with GUI. + +
+ ### Linux Recommended OS version: Ubuntu 18.04 LTS. @@ -87,6 +93,7 @@ For instance, by adding the following lines to `~/.bashrc` 7. Look for the binaries in `build` folder +
### Windows Recommended OS version: Windows 7 x64. @@ -97,10 +104,13 @@ Recommended OS version: Windows 7 x64. 5. Build. In order to correctly deploy Qt GUI application, you also need to do the following: -6. Copy Zano.exe to a folder (e.g. `depoy`). -7. Run `PATH_TO_QT\bin\windeployqt.exe deploy/Zano.exe`. -8. Copy folder `\src\gui\qt-daemon\html` to `deploy\html`. +6. Copy Zano.exe to a folder (e.g. `depoy`). +7. Run `PATH_TO_QT\bin\windeployqt.exe deploy\Zano.exe`. +8. Copy folder `\src\gui\qt-daemon\html` to `deploy\html`. +9. Now you can run `Zano.exe` + +
### macOS Recommended OS version: macOS Sierra 10.15.4 x64. From eaa3b92963c5cdeaf83794478268d1367b141147 Mon Sep 17 00:00:00 2001 From: sowle Date: Fri, 4 Jun 2021 15:02:25 +0300 Subject: [PATCH 06/13] Dockerfile: fixed broken boost downloading link --- utils/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/docker/Dockerfile b/utils/docker/Dockerfile index 9176be64..08f2ffe1 100644 --- a/utils/docker/Dockerfile +++ b/utils/docker/Dockerfile @@ -46,7 +46,7 @@ RUN curl https://github.com/Kitware/CMake/releases/download/v3.15.5/cmake-3.15.5 # Boost 1.68 -RUN curl https://dl.bintray.com/boostorg/release/1.68.0/source/boost_1_68_0.tar.bz2 -OL &&\ +RUN curl https://boostorg.jfrog.io/artifactory/main/release/1.68.0/source/boost_1_68_0.tar.bz2 -OL &&\ echo '7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7 boost_1_68_0.tar.bz2' | sha256sum -c - &&\ tar -xjf boost_1_68_0.tar.bz2 &&\ rm boost_1_68_0.tar.bz2 &&\ From 5b1fa3d5e810c7d4bb02cdb811e8f9b3e194f46a Mon Sep 17 00:00:00 2001 From: sowle Date: Sat, 5 Jun 2021 02:18:45 +0300 Subject: [PATCH 07/13] crypto: scalar_t::assign_mul --- src/crypto/crypto-sugar.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h index 43e69c42..30abccb9 100644 --- a/src/crypto/crypto-sugar.h +++ b/src/crypto/crypto-sugar.h @@ -315,6 +315,13 @@ namespace crypto return *this; } + // returns this = a * b + scalar_t& assign_mul(const scalar_t& a, const scalar_t& b) + { + sc_mul(m_s, a.m_s, b.m_s); + return *this; + } + /* I think it has bad symantic (operator-like), consider rename/reimplement -- sowle */ From 2fdeefb545f034b290b366b20c8335389d3b270b Mon Sep 17 00:00:00 2001 From: sowle Date: Sat, 5 Jun 2021 03:36:58 +0300 Subject: [PATCH 08/13] crypto: scalar_vec_t, scalar_mat_t --- src/crypto/crypto-sugar.h | 147 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h index 30abccb9..1c1b1b9a 100644 --- a/src/crypto/crypto-sugar.h +++ b/src/crypto/crypto-sugar.h @@ -717,6 +717,146 @@ namespace crypto }; // struct point_g_t + // + // vector of scalars + // + struct scalar_vec_t : public std::vector + { + typedef std::vector super_t; + + scalar_vec_t() {} + scalar_vec_t(size_t n) : super_t(n) {} + scalar_vec_t(std::initializer_list init_list) : super_t(init_list) {} + + bool is_reduced() const + { + for (auto& el : *this) + if (!el.is_reduced()) + return false; + return true; + } + + // add a scalar rhs to each element + scalar_vec_t operator+(const scalar_t& rhs) const + { + scalar_vec_t result(size()); + for (size_t i = 0, n = size(); i < n; ++i) + result[i] = at(i) + rhs; + return result; + } + + // subtract a scalar rhs to each element + scalar_vec_t operator-(const scalar_t& rhs) const + { + scalar_vec_t result(size()); + for (size_t i = 0, n = size(); i < n; ++i) + result[i] = at(i) - rhs; + return result; + } + + // multiply each element of the vector by a scalar + scalar_vec_t operator*(const scalar_t& rhs) const + { + scalar_vec_t result(size()); + for (size_t i = 0, n = size(); i < n; ++i) + result[i] = at(i) * rhs; + return result; + } + + // component-wise multiplication (a.k.a the Hadamard product) (only if their sizes match) + scalar_vec_t operator*(const scalar_vec_t& rhs) const + { + scalar_vec_t result; + const size_t n = size(); + if (n != rhs.size()) + return result; + + result.resize(size()); + for (size_t i = 0; i < n; ++i) + result[i] = at(i) * rhs[i]; + return result; + } + + // add each element of two vectors, but only if their sizes match + scalar_vec_t operator+(const scalar_vec_t& rhs) const + { + scalar_vec_t result; + const size_t n = size(); + if (n != rhs.size()) + return result; + + result.resize(size()); + for (size_t i = 0; i < n; ++i) + result[i] = at(i) + rhs[i]; + return result; + } + + // zeroes all elements + void zero() + { + size_t size_bytes = sizeof(scalar_t) * size(); + memset(data(), 0, size_bytes); + } + + // invert all elements in-place efficiently: 4*N muptiplications + 1 inversion + void invert() + { + // muls muls_rev + // 0: 1 2 3 .. n-1 + // 1: 0 2 3 .. n-1 + // 2: 0 1 3 .. n-1 + // + // n-1: 0 1 2 3 .. n-2 + + const size_t size = this->size(); + + if (size < 2) + { + if (size == 1) + at(0) = at(0).reciprocal(); + return; + } + + scalar_vec_t muls(size), muls_rev(size); + muls[0] = 1; + for (size_t i = 0; i < size - 1; ++i) + muls[i + 1] = at(i) * muls[i]; + + muls_rev[size - 1] = 1; + for (size_t i = size - 1; i != 0; --i) + muls_rev[i - 1] = at(i) * muls_rev[i]; + + scalar_t inv = (muls[size - 1] * at(size - 1)).reciprocal(); + + for (size_t i = 0; i < size; ++i) + at(i) = muls[i] * inv * muls_rev[i]; + } + + scalar_t calc_hs() const; + + }; // scalar_vec_t + + + // treats vector of scalars as an M x N matrix just for convenience + template + struct scalar_mat_t : public scalar_vec_t + { + typedef scalar_vec_t super_t; + static_assert(N > 0, "invalid N value"); + + scalar_mat_t() {} + scalar_mat_t(size_t n) : super_t(n) {} + scalar_mat_t(std::initializer_list init_list) : super_t(init_list) {} + + // matrix accessor M rows x N cols + scalar_t& operator()(size_t row, size_t col) + { + return at(row * N + col); + } + }; // scalar_mat_t + + + // // Global constants // @@ -908,4 +1048,11 @@ namespace crypto }; // hash_helper_t struct + inline scalar_t scalar_vec_t::calc_hs() const + { + // hs won't touch memory if size is 0, so it's safe + return hash_helper_t::hs(data(), sizeof(scalar_t) * size()); + } + + } // namespace crypto From 57bc1278f46a5c48cc128cea9d6dad8e667e38f8 Mon Sep 17 00:00:00 2001 From: sowle Date: Sat, 5 Jun 2021 03:42:09 +0300 Subject: [PATCH 09/13] Bulletproofs+: the first working version --- src/crypto/range_proofs.cpp | 12 + src/crypto/range_proofs.h | 822 ++++++++++++++++++++++++++++++++++++ 2 files changed, 834 insertions(+) create mode 100644 src/crypto/range_proofs.cpp create mode 100644 src/crypto/range_proofs.h diff --git a/src/crypto/range_proofs.cpp b/src/crypto/range_proofs.cpp new file mode 100644 index 00000000..b6750a09 --- /dev/null +++ b/src/crypto/range_proofs.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2021 Zano Project (https://zano.org/) +// Copyright (c) 2021 sowle (val@zano.org, crypto.sowle@gmail.com) +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "range_proofs.h" + +namespace crypto +{ + const point_t& bpp_crypto_trait_zano::bpp_H = c_point_H; + + +} diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h new file mode 100644 index 00000000..7478e64b --- /dev/null +++ b/src/crypto/range_proofs.h @@ -0,0 +1,822 @@ +// Copyright (c) 2021 Zano Project (https://zano.org/) +// Copyright (c) 2021 sowle (val@zano.org, crypto.sowle@gmail.com) +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + +// +// This file contains the implementation of range proof protocol. +// Namely, Bulletproofs+ https://eprint.iacr.org/2020/735.pdf +// + +#include "crypto-sugar.h" + +namespace crypto +{ + + // returns x + x^2 + x^3 + ... + x^(2^f) + // == x * (x + 1) * (x^2 + 1) * (x^4 + 1) * ...(x^(f+1) + 1) + inline scalar_t sum_of_powers(scalar_t x, size_t f) + { + scalar_t result = x; + for (size_t i = 0; i < f; ++i) + { + result.assign_muladd(result, x, result); + x *= x; + } + return result; + } + + constexpr size_t c_bpp_log2_n = 6; + constexpr size_t c_bpp_n = 64; // 2^64 is the upper bound for the witness's range + constexpr size_t c_bpp_values_max = 16; // maximum number of elements in BP+ proof, i.e. max allowed BP+ outputs + constexpr size_t c_bpp_mn_max = c_bpp_n * c_bpp_values_max; + + // returns smallest k, s.t. v <= 2**k + inline size_t calc_exp_power_of_2_upper_bound(size_t v) + { + constexpr size_t max_v = (SIZE_MAX >> 1) + 1; + //if (v > max_v) + // return 0; + + size_t pow = 1, result = 0; + while (v > pow) + { + pow <<= 1; + ++result; + } + return result; + } + + // returns least significant bit uing de Bruijn sequence + // http://graphics.stanford.edu/~seander/bithacks.html + inline uint8_t calc_lsb_32(uint32_t v) + { + static const uint8_t multiply_de_bruijn_bit_position[32] = + { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + return multiply_de_bruijn_bit_position[((uint32_t)((v & -(int32_t)v) * 0x077CB531U)) >> 27]; + } + + + //////////////////////////////////////// + // crypto trait for Zano + //////////////////////////////////////// + struct bpp_crypto_trait_zano + { + static void calc_pedersen_commitment(const scalar_t& value, const scalar_t& mask, point_t& commitment) + { + commitment = value * c_point_G + mask * c_point_H; + } + + static const scalar_t& get_initial_transcript() + { + static scalar_t value = hash_helper_t::hs("Zano BP+ initial transcript"); + return value; + } + + // assumes hsc is cleared + static void update_transcript(hash_helper_t::hs_t& hsc, scalar_t& e, const std::vector& points) + { + hsc.add_scalar(e); + hsc.add_points_array(points); + e = hsc.calc_hash(); + } + + static const point_t& get_generator(bool select_H, size_t index) + { + if (index >= c_bpp_mn_max) + return c_point_0; // out of bound + + static std::vector generators(2 * c_bpp_mn_max); + static bool calculated = false; + if (!calculated) + { + scalar_t hash_buf[2] = { hash_helper_t::hs("Zano BP+ generator"), 0 }; + for (size_t i = 0; i < 2 * c_bpp_mn_max; ++i) + { + hash_buf[1].m_u64[0] = i; + ge_bytes_hash_to_ec(&generators[i].m_p3, &hash_buf, sizeof hash_buf); + } + calculated = true; + } + + return generators[2 * index + (select_H ? 1 : 0)]; + } + + static const point_t& bpp_H; + }; + + + struct bpp_signature + { + std::vector L; // size = log_2(m * n) + std::vector R; + public_key A0; + public_key A; + public_key B; + scalar_t r; + scalar_t s; + scalar_t delta; + }; + +#define DBG_VAL_PRINT(x) std::cout << #x ": " << x << ENDL +#define DBG_PRINT(x) std::cout << x << ENDL + + template + bool bpp_gen(const std::vector& values, const scalar_vec_t& masks, bpp_signature& sig, std::vector& commitments, uint8_t* p_err = nullptr) + { +#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("bpp_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) { *p_err = err_code; } return false; } + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(values.size() > 0 && values.size() <= c_bpp_values_max && values.size() == masks.size(), 1); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(masks.is_reduced(), 3); + + const size_t c_bpp_log2_m = calc_exp_power_of_2_upper_bound(values.size()); + const size_t c_bpp_m = 1ull << c_bpp_log2_m; + const size_t c_bpp_mn = c_bpp_m * c_bpp_n; + const size_t c_bpp_log2_mn = c_bpp_log2_m + c_bpp_log2_n; + + // TODO: multiply values and masks by c_scalar_1div8 + // in order to enforce that points in verify() after mul by 8 will be in the prime-order subgroup + + // calc commitments vector as commitments[i] = 1/8 * values[i] * G + 1/8 * masks[i] * H + commitments.resize(values.size()); + for (size_t i = 0; i < values.size(); ++i) + CT::calc_pedersen_commitment(scalar_t(values[i]) * c_scalar_1div8, masks[i] * c_scalar_1div8, commitments[i]); + + + // s.a. BP+ paper, page 15, eq. 11 + // decompose v into aL and aR: + // v = aL o (1, 2, 2^2, ..., 2^n-1), o - component-wise product aka Hadamard product + // aR = aL - (1, 1, ... 1) + // aR o aL = 0 + + // aLs = (aL_0, aL_1, ..., aL_m-1) -- `bit` matrix of c_bpp_m x c_bpp_n, each element is a scalar + + scalar_mat_t aLs(c_bpp_mn), aRs(c_bpp_mn); + aLs.zero(); + aRs.zero(); + // m >= values.size, first set up [0..values.size-1], then -- [values.size..m-1] (padding area) + for (size_t i = 0; i < values.size(); ++i) + { + uint64_t v = values[i]; + for (size_t j = 0; j < c_bpp_n; ++j) + { + if (v & 1) + aLs(i, j) = c_scalar_1; // aL = 1, aR = 0 + else + aRs(i, j) = c_scalar_Lm1; // aL = 0, aR = -1 + v >>= 1; + } + } + + for (size_t i = values.size(); i < c_bpp_m; ++i) + for (size_t j = 0; j < c_bpp_n; ++j) + aRs(i, j) = c_scalar_Lm1; // aL = 0, aR = -1 + + + // using e as Fiat-Shamir transcript + scalar_t e = CT::get_initial_transcript(); + DBG_PRINT("initial transcript: " << e); + + hash_helper_t::hs_t hsc; + CT::update_transcript(hsc, e, commitments); + + // BP+ paper, page 15: The prover begins with sending A = g^aL h^aR h^alpha (group element) + // so we calculate A0 = alpha * H + SUM(aL_i * G_i) + SUM(aR_i * H_i) + + scalar_t alpha = scalar_t::random(); + point_t A0 = alpha * CT::bpp_H; + + for (size_t i = 0; i < c_bpp_mn; ++i) + A0 += aLs[i] * CT::get_generator(false, i) + aRs[i] * CT::get_generator(true, i); + + // part of 1/8 defense scheme + A0 *= c_scalar_1div8; + A0.to_public_key(sig.A0); + + DBG_VAL_PRINT(alpha); + DBG_VAL_PRINT(A0); + + // calculate scalar challenges y and z + hsc.add_scalar(e); + hsc.add_pub_key(sig.A0); + scalar_t y = hsc.calc_hash(); + scalar_t z = hash_helper_t::hs(y); + e = z; // transcript for further steps + DBG_VAL_PRINT(y); + DBG_VAL_PRINT(z); + + // Computing vector d for aggregated version of the protocol (BP+ paper, page 17) + // (note: elements is stored column-by-column in memory) + // d = | 1 * z^(2*1), 1 * z^(2*2), 1 * z^(2*3), ..., 1 * z^(2*m) | + // | 2 * z^(2*1), 2 * z^(2*2), 2 * z^(2*3), ..., 2 * z^(2*m) | + // | 4 * z^(2*1), 4 * z^(2*2), 4 * z^(2*3), ..., 4 * z^(2*m) | + // | ....................................................................................... | + // | 2^(n-1) * z^(2*1), 2^(n-1) * z^(2*2), 2^(n-1) * z^(2*3), ..., 2^(n-1) * z^(2*m)) | + // Note: sum(d_i) = (2^n - 1) * ((z^2)^1 + (z^2)^2 + ... (z^2)^m)) = (2^n-1) * sum_of_powers(x^2, log(m)) + + scalar_t z_sq = z * z; + scalar_mat_t d(c_bpp_mn); + d(0, 0) = z_sq; + // first row + for (size_t i = 1; i < c_bpp_m; ++i) + d(i, 0) = d(i - 1, 0) * z_sq; + // all rows + for (size_t j = 1; j < c_bpp_n; ++j) + for (size_t i = 0; i < c_bpp_m; ++i) + d(i, j) = d(i, j - 1) + d(i, j - 1); + + DBG_PRINT("Hs(d): " << d.calc_hs()); + + // calculate extended Vandermonde vector y = (1, y, y^2, ..., y^(mn+1)) (BP+ paper, page 18, Fig. 3) + // (calculate two more elements (1 and y^(mn+1)) for convenience) + scalar_vec_t y_powers(c_bpp_mn + 2); + y_powers[0] = 1; + for (size_t i = 1; i <= c_bpp_mn + 1; ++i) + y_powers[i] = y_powers[i - 1] * y; + + const scalar_t& y_mn_p1 = y_powers[c_bpp_mn + 1]; + + DBG_PRINT("Hs(y_powers): " << y_powers.calc_hs()); + + // aL_hat = aL - 1*z + scalar_vec_t aLs_hat = aLs - z; + // aL_hat = aR + d o y^leftarr + 1*z where y^leftarr = (y^n, y^(n-1), ..., y) (BP+ paper, page 18, Fig. 3) + scalar_vec_t aRs_hat = aRs + z; + for (size_t i = 0; i < c_bpp_mn; ++i) + aRs_hat[i] += d[i] * y_powers[c_bpp_mn - i]; + + DBG_PRINT("Hs(aLs_hat): " << aLs_hat.calc_hs()); + DBG_PRINT("Hs(aRs_hat): " << aRs_hat.calc_hs()); + + // calculate alpha_hat + // alpha_hat = alpha + SUM(z^(2j) * gamma_j * y^(mn+1)) for j = 1..m + // i.e. \hat{\alpha} = \alpha + y^{m n+1} \sum_{j = 1}^{m} z^{2j} \gamma_j + scalar_t alpha_hat = 0; + for (size_t i = 0; i < masks.size(); ++i) + alpha_hat += d(i, 0) * masks[i]; + alpha_hat = alpha + y_mn_p1 * alpha_hat; + + DBG_VAL_PRINT(alpha_hat); + + // calculate y^-1, y^-2, ... + const scalar_t y_inverse = y.reciprocal(); + scalar_vec_t y_inverse_powers(c_bpp_mn / 2 + 1); // the greatest power we need is c_bpp_mn/2 (at the first reduction round) + y_inverse_powers[0] = 1; + for (size_t i = 1, size = y_inverse_powers.size(); i < size; ++i) + y_inverse_powers[i] = y_inverse_powers[i - 1] * y_inverse; + + // prepare generator's vector + std::vector g(c_bpp_mn), h(c_bpp_mn); + for (size_t i = 0; i < c_bpp_mn; ++i) + { + g[i] = CT::get_generator(false, i); + h[i] = CT::get_generator(true, i); + } + + // WIP zk-argument called with zk-WIP(g, h, G, H, A_hat, aL_hat, aR_hat, alpha_hat) + + scalar_vec_t& a = aLs_hat; + scalar_vec_t& b = aRs_hat; + + sig.L.resize(c_bpp_log2_mn); + sig.R.resize(c_bpp_log2_mn); + + // zk-WIP reduction rounds (s.a. the preprint page 13 Fig. 1) + for (size_t n = c_bpp_mn / 2, ni = 0; n >= 1; n /= 2, ++ni) + { + DBG_PRINT(ENDL << "#" << ni); + + // zk-WIP(g, h, G, H, P, a, b, alpha) + + scalar_t dL = scalar_t::random(); + DBG_VAL_PRINT(dL); + scalar_t dR = scalar_t::random(); + DBG_VAL_PRINT(dR); + + // a = (a1, a2), b = (b1, b2) -- vectors of scalars + // cL = -- scalar + scalar_t cL = 0; + for (size_t i = 0; i < n; ++i) + cL += a[i] * y_powers[i + 1] * b[n + i]; + + DBG_VAL_PRINT(cL); + + // cR = * y^n -- scalar + scalar_t cR = 0; + for (size_t i = 0; i < n; ++i) + cR += a[n + i] * y_powers[i + 1] * b[i]; + cR *= y_powers[n]; + + DBG_VAL_PRINT(cR); + + // L = y^-n * a1 * g2 + b2 * h1 + cL * G + dL * H -- point + point_t sum = c_point_0; + for (size_t i = 0; i < n; ++i) + sum += a[i] * g[n + i]; + point_t L; + CT::calc_pedersen_commitment(cL, dL, L); + for (size_t i = 0; i < n; ++i) + L += b[n + i] * h[i]; + L += y_inverse_powers[n] * sum; + L *= c_scalar_1div8; + DBG_VAL_PRINT(L); + + // R = y^n * a2 * g1 + b1 * h2 + cR * G + dR * H -- point + sum.zero(); + for (size_t i = 0; i < n; ++i) + sum += a[n + i] * g[i]; + point_t R; + CT::calc_pedersen_commitment(cR, dR, R); + for (size_t i = 0; i < n; ++i) + R += b[i] * h[n + i]; + R += y_powers[n] * sum; + R *= c_scalar_1div8; + DBG_VAL_PRINT(R); + + // put L, R to the sig + L.to_public_key(sig.L[ni]); + R.to_public_key(sig.R[ni]); + + // update the transcript + hsc.add_scalar(e); + hsc.add_pub_key(sig.L[ni]); + hsc.add_pub_key(sig.R[ni]); + e = hsc.calc_hash(); + DBG_VAL_PRINT(e); + + // recalculate arguments for the next round + scalar_t e_squared = e * e; + scalar_t e_inverse = e.reciprocal(); + scalar_t e_inverse_squared = e_inverse * e_inverse; + scalar_t e_y_inv_n = e * y_inverse_powers[n]; + scalar_t e_inv_y_n = e_inverse * y_powers[n]; + + // g_hat = e^-1 * g1 + (e * y^-n) * g2 -- vector of points + for (size_t i = 0; i < n; ++i) + g[i] = e_inverse * g[i] + e_y_inv_n * g[n + i]; + + // h_hat = e * h1 + e^-1 * h2 -- vector of points + for (size_t i = 0; i < n; ++i) + h[i] = e * h[i] + e_inverse * h[n + i]; + + // P_hat = e^2 * L + P + e^-2 * R -- point + + // a_hat = e * a1 + e^-1 * y^n * a2 -- vector of scalars + for (size_t i = 0; i < n; ++i) + a[i] = e * a[i] + e_inv_y_n * a[n + i]; + + // b_hat = e^-1 * b1 + e * b2 -- vector of scalars + for (size_t i = 0; i < n; ++i) + b[i] = e_inverse * b[i] + e * b[n + i]; + + // alpha_hat = e^2 * dL + alpha + e^-2 * dR -- scalar + alpha_hat += e_squared * dL + e_inverse_squared * dR; + + // run next iteraton zk-WIP(g_hat, h_hat, G, H, P_hat, a_hat, b_hat, alpha_hat) + } + DBG_PRINT(""); + + // zk-WIP last round + scalar_t r = scalar_t::random(); + scalar_t s = scalar_t::random(); + scalar_t delta = scalar_t::random(); + scalar_t eta = scalar_t::random(); + DBG_VAL_PRINT(r); + DBG_VAL_PRINT(s); + DBG_VAL_PRINT(delta); + DBG_VAL_PRINT(eta); + + // A = r * g + s * h + (r y b + s y a) * G + delta * H -- point + point_t A = c_point_0; + CT::calc_pedersen_commitment(y * (r * b[0] + s * a[0]), delta, A); + A += r * g[0] + s * h[0]; + A *= c_scalar_1div8; + A.to_public_key(sig.A); + DBG_VAL_PRINT(A); + + // B = (r * y * s) * G + eta * H + point_t B = c_point_0; + CT::calc_pedersen_commitment(r * y * s, eta, B); + B *= c_scalar_1div8; + B.to_public_key(sig.B); + DBG_VAL_PRINT(B); + + // update the transcript + hsc.add_scalar(e); + hsc.add_pub_key(sig.A); + hsc.add_pub_key(sig.B); + e = hsc.calc_hash(); + DBG_VAL_PRINT(e); + + // finalize the signature + sig.r = r + e * a[0]; + sig.s = s + e * b[0]; + sig.delta = eta + e * delta + e * e * alpha_hat; + DBG_VAL_PRINT(sig.r); + DBG_VAL_PRINT(sig.s); + DBG_VAL_PRINT(sig.delta); + + return true; +#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE + } // bpp_gen() + + + // efficient multiexponentiation (naive stub implementation atm, TODO) + template + bool multiexp_and_check_being_zero(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand) + { + CHECK_AND_ASSERT_MES(g_scalars.size() < c_bpp_mn_max, false, "g_scalars oversized"); + CHECK_AND_ASSERT_MES(h_scalars.size() < c_bpp_mn_max, false, "h_scalars oversized"); + + point_t result = summand; + + for (size_t i = 0; i < g_scalars.size(); ++i) + result += g_scalars[i] * CT::get_generator(false, i); + + for (size_t i = 0; i < h_scalars.size(); ++i) + result += h_scalars[i] * CT::get_generator(true, i); + + if (!result.is_zero()) + { + LOG_PRINT_L0("multiexp result is non zero: " << result); + return false; + } + return true; + } + + + struct bpp_sig_commit_ref_t + { + bpp_sig_commit_ref_t(const bpp_signature& sig, const std::vector& commitments) + : sig(sig) + , commitments(commitments) + {} + const bpp_signature& sig; + const std::vector& commitments; + }; + + + template + bool bpp_verify(const std::vector& sigs, uint8_t* p_err = nullptr) + { +#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("bpp_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) { *p_err = err_code; } return false; } + + DBG_PRINT(ENDL << " . . . . bpp_verify() . . . . "); + + const size_t kn = sigs.size(); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(kn > 0, 1); + + struct intermediate_element_t + { + scalar_t y; + scalar_t z; + scalar_t z_sq; + scalar_vec_t e; + scalar_vec_t e_sq; + scalar_t e_final; + scalar_t e_final_sq; + size_t inv_e_offset; // offset in batch_for_inverse + size_t inv_y_offset; // offset in batch_for_inverse + size_t c_bpp_log2_m; + size_t c_bpp_m; + size_t c_bpp_mn; + point_t A; + point_t A0; + point_t B; + std::vector L; + std::vector R; + }; + std::vector interms(kn); + + size_t c_bpp_log2_m_max = 0; + for (size_t k = 0; k < kn; ++k) + { + const bpp_sig_commit_ref_t& bsc = sigs[k]; + const bpp_signature& sig = bsc.sig; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bsc.commitments.size() > 0, 2); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.L.size() > 0 && sig.L.size() == sig.R.size(), 3); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.r.is_reduced() && sig.s.is_reduced() && sig.delta.is_reduced(), 4); + + intermediate_element_t& interm = interms[k]; + interm.c_bpp_log2_m = calc_exp_power_of_2_upper_bound(bsc.commitments.size()); + if (c_bpp_log2_m_max < interm.c_bpp_log2_m) + c_bpp_log2_m_max = interm.c_bpp_log2_m; + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.L.size() == interm.c_bpp_log2_m + c_bpp_log2_n, 5); + + interm.c_bpp_m = 1ull << interm.c_bpp_log2_m; + interm.c_bpp_mn = interm.c_bpp_m * c_bpp_n; + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.A0.from_public_key(sig.A0), 6); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.A.from_public_key(sig.A), 7); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.B.from_public_key(sig.B), 8); + interm.L.resize(sig.L.size()); + interm.R.resize(sig.R.size()); + for (size_t i = 0; i < interm.L.size(); ++i) + { + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.L[i].from_public_key(sig.L[i]), 9); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.R[i].from_public_key(sig.R[i]), 10); + } + } + const size_t c_bpp_m_max = 1ull << c_bpp_log2_m_max; + const size_t c_bpp_mn_max = c_bpp_m_max * c_bpp_n; + const size_t c_bpp_LR_size_max = c_bpp_log2_m_max + c_bpp_log2_n; + + + // + // prepare stuff + // + /* + std::vector g(c_bpp_mn_max), h(c_bpp_mn_max); + for (size_t i = 0; i < c_bpp_mn_max; ++i) + { + g[i] = CT::get_generator(false, i); + h[i] = CT::get_generator(true, i); + } + */ + + scalar_vec_t batch_for_inverse; + batch_for_inverse.reserve(kn + kn * c_bpp_LR_size_max); + + + for (size_t k = 0; k < kn; ++k) + { + DBG_PRINT(ENDL << "SIG #" << k); + const bpp_sig_commit_ref_t& bsc = sigs[k]; + const bpp_signature& sig = bsc.sig; + intermediate_element_t& interm = interms[k]; + + // restore y and z + // using e as Fiat-Shamir transcript + scalar_t e = CT::get_initial_transcript(); + DBG_PRINT("initial transcript: " << e); + hash_helper_t::hs_t hsc; + CT::update_transcript(hsc, e, bsc.commitments); + // calculate scalar challenges y and z + hsc.add_scalar(e); + hsc.add_pub_key(sig.A0); + hsc.assign_calc_hash(interm.y); + interm.z = hash_helper_t::hs(interm.y); + interm.z_sq = interm.z * interm.z; + DBG_VAL_PRINT(interm.y); + DBG_VAL_PRINT(interm.z); + e = interm.z; // transcript for further steps + + interm.inv_y_offset = batch_for_inverse.size(); + batch_for_inverse.push_back(interm.y); + interm.inv_e_offset = batch_for_inverse.size(); + + interm.e.resize(sig.L.size()); + interm.e_sq.resize(sig.L.size()); + + for (size_t i = 0; i < sig.L.size(); ++i) + { + hsc.add_scalar(e); + hsc.add_pub_key(sig.L[i]); + hsc.add_pub_key(sig.R[i]); + hsc.assign_calc_hash(e); + interm.e[i] = e; + interm.e_sq[i] = e * e; + DBG_PRINT("e[" << i << "]: " << e); + batch_for_inverse.push_back(e); + } + + hsc.add_scalar(e); + hsc.add_pub_key(sig.A); + hsc.add_pub_key(sig.B); + hsc.assign_calc_hash(interm.e_final); + interm.e_final_sq = interm.e_final * interm.e_final; + DBG_VAL_PRINT(interm.e_final); + } + + batch_for_inverse.invert(); + + // Notation: + // 1_vec ^ n = (1, 1, 1, ..., 1) + // 2_vec ^ n = (2^0, 2^1, 2^2, ..., 2^(n-1)) + // -1_vec ^ n = ((-1)^0, (-1)^1, (-1)^2, ... (-1)^(n-1)) = (1, -1, 1, -1, ...) + // y<^n = (y^n, y^(n-1), ..., y^1) + // y>^n = (y^1, y^2, ..., y^n) + + // from page 13, Fig 1: + // Verifier outputs Accept IFF the following equality holds (single proof): + // P^e^2 * A^e * B == g ^ (r' e) * h ^ (s' e) * G ^ (r' y s') * H ^ delta' + // (where g and h are calculated in each round) + // The same equation in additive notation: + // e^2 * P + e * A + B == (r' * e) * g + (s' * e) * h + (r' y s') * G + delta' * H + // <=> + // (r' * e) * g + (s' * e) * h + (r' y s') * G + delta' * H - e^2 * P - e * A - B == 0 (*) + // where A, B, r', s', delta' is taken from the signature + // and P_{k+1} = e^2 * L_k + P_k + e^-2 * R_k for all rounds + // + // from page 18, Fig 3: + // P and V computes: + // A_hat = A0 + (- 1^(mn) * z) * g + (d o y<^(mn) + 1^(mn) * z) * h + + // + y^(mn+1) * (SUM{j=1..m} z^(2j) * V_j) + + // + (z*SUM(y^>mn) - z*y^(mn+1)*SUM(d) - z^2 * SUM(y^>mn)) * G + // (calculated once) + // + // As suggested in Section 6.1 "Practical Optimizations": + // 1) g and h exponentianions can be optimized in order not to be calculated at each round as the following (page 20): + // + // (r' * e * s_vec) * g + (s' * e * s'_vec) * h + (r' y s') * G + delta' * H - + // - e^2 * A_hat + // - SUM{j=1..log(n)}(e_final^2 * e_j^2 * L_j + e_final^2 * e_j^-2 * R_j) + // - e * A - B = 0 (**) + // + // where: + // g, h - vector of fixed generators + // s_vec_i = y^(1-i) * PROD{j=1..log(n)}(e_j ^ b(i,j)) + // s'_vec_i = PROD{j=1..log(n)}(e_j ^ -b(i,j)) + // b(i, j) = { 2 * ((1<<(j-1)) & (i-1)) - 1) (counting both from 1) (page 20) + // b(i, j) = { 2 * ((1< + + // (r' * e * s_vec) * g + (s' * e * s'_vec) * h + (r' y s') * G + delta' * H - + // - e^2 * (A0 + (- 1^(mn) * z) * g + (d o y<^(mn) + 1^(mn) * z) * h + + // + y^(mn+1) * (SUM{j=1..m} z^(2j) * V_j) + + // + (z*SUM(y^>mn) - z*y^(mn+1)*SUM(d) - z^2 * SUM(y^>mn)) * G + // ) + // - SUM{j=1..log(n)}(e_final^2 * e_j^2 * L_j + e_final^2 * e_j^-2 * R_j) + // - e * A - B = 0 + + // => + + // (for single signature) + // + // (r' * e * s_vec - e^2 * (- 1_vec^(mn) * z)) * g | these are + // + (s' * e * s'_vec - e^2 * (d o y<^(mn) + 1_vec^(mn) * z)) * h | fixed generators + // + (r' y s' - e^2 * ((z - z^2)*SUM(y^>mn) - z*y^(mn+1)*SUM(d)) * G | across all + // + delta' * H | the signatures + // + // - e^2 * A0 + // - e^2 * y^(mn+1) * (SUM{j=1..m} z^(2j) * V_j)) + // - e^2 * SUM{j=1..log(n)}(e_j^2 * L_j + e_j^-2 * R_j) + // - e * A - B = 0 (***) + // + // All (***) will be muptiplied by random weightning factor and then summed up. + + // Calculate cummulative sclalar multiplicand for fixed generators across all the sigs. + scalar_vec_t g_scalars; + g_scalars.resize(c_bpp_mn_max, 0); + scalar_vec_t h_scalars; + h_scalars.resize(c_bpp_mn_max, 0); + scalar_t G_scalar = 0; + scalar_t H_scalar = 0; + point_t summand = c_point_0; + + for (size_t k = 0; k < kn; ++k) + { + DBG_PRINT(ENDL << "SIG #" << k); + const bpp_sig_commit_ref_t& bsc = sigs[k]; + const bpp_signature& sig = bsc.sig; + intermediate_element_t& interm = interms[k]; + + // random weightning factor for speed-optimized batch verification (preprint page 20) + const scalar_t rwf = scalar_t::random(); + DBG_PRINT("rwf: " << rwf); + + // prepare d vector (see also d structure description in proof function) + scalar_mat_t d(interm.c_bpp_mn); + d(0, 0) = interm.z_sq; + // first row + for (size_t i = 1; i < interm.c_bpp_m; ++i) + d(i, 0) = d(i - 1, 0) * interm.z_sq; + // all rows + for (size_t j = 1; j < c_bpp_n; ++j) + for (size_t i = 0; i < interm.c_bpp_m; ++i) + d(i, j) = d(i, j - 1) + d(i, j - 1); + // sum(d) (see also note in proof function for this) + static const scalar_t c_scalar_2_power_n_minus_1 = { 0xffffffffffffffff, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 }; + const scalar_t sum_d = c_scalar_2_power_n_minus_1 * sum_of_powers(interm.z_sq, interm.c_bpp_log2_m); + + DBG_PRINT("Hs(d): " << d.calc_hs()); + DBG_PRINT("sum(d): " << sum_d); + + const scalar_t& y_inv = batch_for_inverse[interm.inv_y_offset]; + auto get_e_inv = [&](size_t i) { return batch_for_inverse[interm.inv_e_offset + i]; }; // i belongs to [0; L.size()-1] + + // prepare s_vec (unlike the paper here we moved y-component out of s_vec for convenience, so s_vec'[x] = s_vec[~x & (MN-1)]) + // complexity (sc_mul's): MN+2*log2(MN)-2 + // the idea is the following: + // s_vec[00000b] = ... * (e_4)^-1 * (e_3)^-1 * (e_2)^-1 * (e_1)^-1 * (e_0)^-1 + // s_vec[00101b] = ... * (e_4)^-1 * (e_3)^-1 * (e_2)^+1 * (e_1)^-1 * (e_0)^+1 + const size_t log2_mn = sig.L.size(); // at the beginning we made sure that sig.L.size() == c_bpp_log2_m + c_bpp_log2_n + scalar_vec_t s_vec(interm.c_bpp_mn); + s_vec[0] = get_e_inv(0); + for (size_t i = 1; i < log2_mn; ++i) + s_vec[0] *= get_e_inv(i); // s_vec[0] = (e_0)^-1 * (e_1)^-1 * .. (e_{log2_mn-1})^-1 + DBG_PRINT("[0] " << s_vec[0]); + for (size_t i = 1; i < interm.c_bpp_mn; ++i) + { + size_t base_el_index = i & (i - 1); // base element index: 0, 0, 2, 0, 4, 4, 6, 0, 8, 8, 10... base element differs in one bit (0) from the current one (1) + size_t bit_index = log2_mn - calc_lsb_32((uint32_t)i) - 1; // the bit index where current element has the difference with the base + s_vec[i] = s_vec[base_el_index] * interm.e_sq[bit_index]; // (e_j)^-1 * (e_j)^2 = (e_j)^+1 + DBG_PRINT("[" << i << "] " << " " << base_el_index << ", " << bit_index << " : " << s_vec[i]); + } + + // prepare y_inv vector + scalar_vec_t y_inverse_powers(interm.c_bpp_mn); + y_inverse_powers[0] = 1; + for (size_t i = 1; i < interm.c_bpp_mn; ++i) + y_inverse_powers[i] = y_inverse_powers[i - 1] * y_inv; + + // y^(mn+1) + scalar_t y_power_mnp1 = interm.y; + for (size_t i = 0; i < log2_mn; ++i) + y_power_mnp1 *= y_power_mnp1; + y_power_mnp1 *= interm.y; + DBG_VAL_PRINT(y_power_mnp1); + + // now calculate all multiplicands for common generators + + // g vector multiplicands: + // rwf * (r' * e * (1, y^-1, y^-2, ...) o s_vec + e^2 * z) = + // rwf * r' * e * ((1, y^-1, ...) o s_vec) + rwf * e^2 * z * (1, 1, ...) + scalar_t rwf_e_sq_z = rwf * interm.e_final_sq * interm.z; + scalar_t rwf_r_e = rwf * interm.e_final * sig.r; + for (size_t i = 0; i < interm.c_bpp_mn; ++i) + g_scalars[i] += rwf_r_e * y_inverse_powers[i] * s_vec[i] + rwf_e_sq_z; + + DBG_PRINT("Hs(g_scalars): " << g_scalars.calc_hs()); + + // h vector multiplicands: + // rwf * (s' * e * s'_vec - e^2 * (d o y<^(mn) + 1_vec^(mn) * z)) + // rwf * s' * e * s'_vec - rwf * e^2 * z * (1, 1...) - rwf * e^2 * (d o y<^(mn)) + //scalar_t rwf_e_sq_z = rwf * interm.e_final_sq * interm.z; + scalar_t rwf_s_e = rwf * sig.s * interm.e_final; + scalar_t rwf_e_sq_y = rwf * interm.e_final_sq * interm.y; + for (size_t i = interm.c_bpp_mn - 1; i != SIZE_MAX; --i) + { + h_scalars[i] += rwf_s_e * s_vec[interm.c_bpp_mn - 1 - i] - rwf_e_sq_z - rwf_e_sq_y * d[i]; + rwf_e_sq_y *= interm.y; + } + + DBG_PRINT("Hs(h_scalars): " << h_scalars.calc_hs()); + + // G point multiplicands: + // rwf * (r' y s' - e ^ 2 * ((z - z ^ 2)*SUM(y^>mn) - z * y^(mn+1) * SUM(d)) = + // = rwf * r' y s' - rwf * e^2 * (z - z ^ 2)*SUM(y^>mn) + rwf * e^2 * z * y^(mn+1) * SUM(d) + G_scalar += rwf * sig.r * interm.y * sig.s + rwf_e_sq_y * sum_d * interm.z; + G_scalar -= rwf * interm.e_final_sq * (interm.z - interm.z_sq) * sum_of_powers(interm.y, log2_mn); + DBG_PRINT("sum_y: " << sum_of_powers(interm.y, log2_mn)); + DBG_PRINT("G_scalar: " << G_scalar); + + // H point multiplicands: + // rwf * delta + H_scalar += rwf * sig.delta; + DBG_PRINT("H_scalar: " << H_scalar); + + // uncommon generators' multiplicands + point_t summand_8 = c_point_0; // this summand to be multiplied by 8 and rwf before adding to the main summand + // - rwf * e^2 * A0 + summand_8 -= rwf * interm.e_final_sq * interm.A0; + DBG_PRINT("A0_scalar: " << c_scalar_Lm1 * interm.e_final_sq * rwf); + + // - rwf * e^2 * y^(mn+1) * (SUM{j=1..m} (z^2)^j * V_j)) + scalar_t e_sq_y_mn1_z_sq_power = rwf * interm.e_final_sq * y_power_mnp1; + for (size_t j = 0; j < bsc.commitments.size(); ++j) + { + e_sq_y_mn1_z_sq_power *= interm.z_sq; + summand_8 -= e_sq_y_mn1_z_sq_power * bsc.commitments[j]; + DBG_PRINT("V_scalar[" << j << "]: " << c_scalar_Lm1 * e_sq_y_mn1_z_sq_power); + } + + // - rwf * e^2 * SUM{j=1..log(n)}(e_j^2 * L_j + e_j^-2 * R_j) + scalar_t rwf_e_sq = rwf * interm.e_final_sq; + for (size_t j = 0; j < log2_mn; ++j) + { + summand_8 -= rwf_e_sq * (interm.e_sq[j] * interm.L[j] + get_e_inv(j) * get_e_inv(j) * interm.R[j]); + DBG_PRINT("L_scalar[" << j << "]: " << c_scalar_Lm1 * rwf_e_sq * interm.e_sq[j]); + DBG_PRINT("R_scalar[" << j << "]: " << c_scalar_Lm1 * rwf_e_sq * get_e_inv(j) * get_e_inv(j)); + } + + // - rwf * e * A - rwf * B = 0 + summand_8 -= rwf * interm.e_final * interm.A + rwf * interm.B; + DBG_PRINT("A_scalar: " << c_scalar_Lm1 * rwf * interm.e_final); + DBG_PRINT("B_scalar: " << c_scalar_Lm1 * rwf); + + summand_8.modify_mul8(); + summand += summand_8; + } + + point_t GH_exponents = c_point_0; + CT::calc_pedersen_commitment(G_scalar, H_scalar, GH_exponents); + bool result = multiexp_and_check_being_zero(g_scalars, h_scalars, summand + GH_exponents); + if (result) + DBG_PRINT(ENDL << " . . . . bpp_verify() -- SUCCEEDED!!!" << ENDL); + return result; +#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE + } + +} // namespace crypto From 5fa0e1584380a414e435dda05fcb31d3c2be64be Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 7 Jun 2021 14:43:19 +0300 Subject: [PATCH 10/13] crypto tests: crypto_calc_lsb_32 --- tests/functional_tests/crypto_tests.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index ba4313fd..eaf5a5f3 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1551,6 +1551,30 @@ TEST(crypto, hex_tools) return true; } + +TEST(crypto, calc_lsb_32) +{ + auto& local_calc_lsb = [](uint32_t v) { + uint8_t r = 0; + while (v != 0 && (v & 1) == 0) + { + v >>= 1; + ++r; + } + return r; + }; + + for (uint32_t x = 0; x < UINT32_MAX; ++x) + { + if (x % 10000000 == 0) + std::cout << x << ENDL; + ASSERT_EQ((int)local_calc_lsb(x), (int)calc_lsb_32(x)); + } + ASSERT_EQ((int)local_calc_lsb(UINT32_MAX), (int)calc_lsb_32(UINT32_MAX)); + + return true; +} + // // test's runner // From bb6dea509e97700689ff50e99545f1174ed5cdf9 Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 7 Jun 2021 15:00:58 +0300 Subject: [PATCH 11/13] wallet resync-on-load issue fixed (the bug was in data storing on Boost > 69), also Zano made compatible with Boost 1.72 --- contrib/eos_portable_archive/eos/portable_oarchive.hpp | 2 +- src/pch/stdafx.h | 1 - src/wallet/wallet2.cpp | 5 +---- src/wallet/wallet2.h | 2 +- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/contrib/eos_portable_archive/eos/portable_oarchive.hpp b/contrib/eos_portable_archive/eos/portable_oarchive.hpp index fce2bd26..5f390194 100644 --- a/contrib/eos_portable_archive/eos/portable_oarchive.hpp +++ b/contrib/eos_portable_archive/eos/portable_oarchive.hpp @@ -340,7 +340,7 @@ namespace eos { // we choose to use little endian because this way we just // save the first size bytes to the stream and skip the rest #if BOOST_VERSION >= 106900 - temp = endian::native_to_little(temp); + temp = endian::native_to_little(t); #else endian::store_little_endian(&temp, t); #endif diff --git a/src/pch/stdafx.h b/src/pch/stdafx.h index 7aec3431..a1aa51ed 100644 --- a/src/pch/stdafx.h +++ b/src/pch/stdafx.h @@ -67,7 +67,6 @@ POP_VS_WARNINGS #include #include #include -#include #include #include #include diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9c8c9c33..d0456a00 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2389,10 +2389,6 @@ bool wallet2::deinit() bool wallet2::clear() { reset_all(); - //currency::block b; - //currency::generate_genesis_block(b); - m_chain.clear(); - //m_blockchain.push_back(get_block_hash(b)); return true; } //---------------------------------------------------------------------------------------------------- @@ -2666,6 +2662,7 @@ void wallet2::load(const std::wstring& wallet_, const std::string& password) bool need_to_resync = false; if (wbh.m_ver == 1000) { + // old WALLET_FILE_BINARY_HEADER_VERSION version means no encryption need_to_resync = !tools::portable_unserialize_obj_from_stream(*this, data_file); } else diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index f1ab885e..6723795c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -707,7 +707,7 @@ namespace tools if (ver < 149) { - WLT_LOG_MAGENTA("Wallet file truncated due to old version", LOG_LEVEL_0); + WLT_LOG_MAGENTA("Wallet file truncated due to old version: " << ver, LOG_LEVEL_0); return; } From d38c852ead86985646e639b1a3207d1dad4e31b4 Mon Sep 17 00:00:00 2001 From: sowle Date: Tue, 8 Jun 2021 17:56:25 +0300 Subject: [PATCH 12/13] crypto_tests: minor fix --- tests/functional_tests/crypto_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index eaf5a5f3..b70c5390 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -14,6 +14,7 @@ #include "currency_core/difficulty.h" #include "crypto/crypto-sugar.h" +#include "crypto/range_proofs.h" using namespace crypto; From ca10d2de17037f710f921eae102bda8f0b147b6c Mon Sep 17 00:00:00 2001 From: sowle Date: Tue, 8 Jun 2021 18:07:53 +0300 Subject: [PATCH 13/13] Bulletproofs+: gcc compilation fix + minor improvements --- src/crypto/range_proofs.h | 7 ++++--- tests/functional_tests/crypto_tests.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h index 7478e64b..b3d04d68 100644 --- a/src/crypto/range_proofs.h +++ b/src/crypto/range_proofs.h @@ -9,6 +9,7 @@ // Namely, Bulletproofs+ https://eprint.iacr.org/2020/735.pdf // +#include "epee/include/misc_log_ex.h" #include "crypto-sugar.h" namespace crypto @@ -140,8 +141,8 @@ namespace crypto const size_t c_bpp_mn = c_bpp_m * c_bpp_n; const size_t c_bpp_log2_mn = c_bpp_log2_m + c_bpp_log2_n; - // TODO: multiply values and masks by c_scalar_1div8 - // in order to enforce that points in verify() after mul by 8 will be in the prime-order subgroup + // pre-multiply all output points by c_scalar_1div8 + // in order to enforce these points to be in the prime-order subgroup (after mul by 8 in bpp_verify()) // calc commitments vector as commitments[i] = 1/8 * values[i] * G + 1/8 * masks[i] * H commitments.resize(values.size()); @@ -778,7 +779,7 @@ namespace crypto DBG_PRINT("H_scalar: " << H_scalar); // uncommon generators' multiplicands - point_t summand_8 = c_point_0; // this summand to be multiplied by 8 and rwf before adding to the main summand + point_t summand_8 = c_point_0; // this summand to be multiplied by 8 before adding to the main summand // - rwf * e^2 * A0 summand_8 -= rwf * interm.e_final_sq * interm.A0; DBG_PRINT("A0_scalar: " << c_scalar_Lm1 * interm.e_final_sq * rwf); diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index b70c5390..f5ea3a8e 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1555,7 +1555,7 @@ TEST(crypto, hex_tools) TEST(crypto, calc_lsb_32) { - auto& local_calc_lsb = [](uint32_t v) { + auto local_calc_lsb = [](uint32_t v) { uint8_t r = 0; while (v != 0 && (v & 1) == 0) {