diff --git a/CMakeLists.txt b/CMakeLists.txt
index e2651c5d..56d249d3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,9 @@ if(TESTNET)
add_definitions(-DTESTNET)
endif()
+set(OPENSSL_USE_STATIC_LIBS TRUE) # link statically
+find_package(OpenSSL REQUIRED)
+
if(DISABLE_TOR)
message("NOTICE: Building with disabled TOR support!")
add_definitions(-DDISABLE_TOR)
diff --git a/README.md b/README.md
index c6ad2f56..13558e5f 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,11 @@ Be sure to clone the repository properly:\
### Dependencies
| component / version | minimum
(not recommended but may work) | recommended | most recent of what we have ever tested |
|--|--|--|--|
-| gcc (Linux) | 5.4.0 | 7.4.0 | 8.3.0 |
+| gcc (Linux) | 5.4.0 | 7.5.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.9.0) | 2019 |
+| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2015 (14.0 update 1) | 2017 (15.9.0) | 2022 (17.4.2) |
| [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.20 |
+| [CMake](https://cmake.org/download/) | 2.8.6 | 3.15.5 | 3.26.3 |
| [Boost](https://www.boost.org/users/download/) | 1.70 | 1.70 | 1.76 |
| [OpenSSL](https://www.openssl.org/source/) [(win)](https://slproweb.com/products/Win32OpenSSL.html) | - | 1.1.1n | 1.1.1n |
| [Qt](https://download.qt.io/archive/qt/) (*only for GUI*) | 5.8.0 | 5.11.2 | 5.15.2 |
@@ -45,7 +45,7 @@ Recommended OS version: Ubuntu 18.04 LTS.
2. Download and build Boost
curl -OL https://boostorg.jfrog.io/artifactory/main/release/1.70.0/source/boost_1_70_0.tar.bz2
- tar -xjf boost_1_70_0.tar.bz2
+ echo "430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778 boost_1_70_0.tar.bz2" | shasum -c && tar -xjf boost_1_70_0.tar.bz2
cd boost_1_70_0
./bootstrap.sh --with-libraries=system,filesystem,thread,date_time,chrono,regex,serialization,atomic,program_options,locale,timer,log
./b2
@@ -63,10 +63,10 @@ Recommended OS version: Ubuntu 18.04 LTS.
4. Install OpenSSL
- We recommend installing OpenSSL v1.1.1n locally unless you would like to use the same version system-wide.
+ We recommend installing OpenSSL v1.1.1n locally unless you would like to use the same version system-wide. Adjust the local path `/home/user/openssl` in the commands below according to your needs.
curl -OL https://www.openssl.org/source/openssl-1.1.1n.tar.gz
- tar xaf openssl-1.1.1n.tar.gz
+ echo "40dceb51a4f6a5275bde0e6bf20ef4b91bfc32ed57c0552e2e8e15463372b17a openssl-1.1.1n.tar.gz" | shasum -c && tar xaf openssl-1.1.1n.tar.gz
cd openssl-1.1.1n/
./config --prefix=/home/user/openssl --openssldir=/home/user/openssl shared zlib
make
@@ -108,7 +108,7 @@ For instance, by adding the following lines to `~/.bashrc`
1. Build GUI:
cd zano
- utils/build_sript_linux.sh
+ utils/build_script_linux.sh
7. Look for the binaries in `build` folder
diff --git a/contrib/epee/include/misc_helpers.h b/contrib/epee/include/misc_helpers.h
index fb990e8a..58ddc3cd 100644
--- a/contrib/epee/include/misc_helpers.h
+++ b/contrib/epee/include/misc_helpers.h
@@ -66,6 +66,7 @@
}
#define CATCH_ENTRY(location, return_val) CATCH_ENTRY_CUSTOM(location, (void)0, return_val)
#define CATCH_ENTRY2(return_val) CATCH_ENTRY_CUSTOM(LOCATION_SS, (void)0, return_val)
+#define CATCH_ENTRY_CUSTOM2(custom_code, return_val) CATCH_ENTRY_CUSTOM(LOCATION_SS, custom_code, return_val)
#define CATCH_ENTRY_L0(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L1(location, return_val) CATCH_ENTRY(location, return_val)
diff --git a/resources/app_icon.svg b/resources/app_icon.svg
new file mode 100644
index 00000000..d5ca861a
--- /dev/null
+++ b/resources/app_icon.svg
@@ -0,0 +1,42 @@
+
diff --git a/resources/app_icon_256.png b/resources/app_icon_256.png
new file mode 100644
index 00000000..c2cb1525
Binary files /dev/null and b/resources/app_icon_256.png differ
diff --git a/src/common/crypto_serialization.h b/src/common/crypto_serialization.h
index 89b05d8f..a5b5bd6c 100644
--- a/src/common/crypto_serialization.h
+++ b/src/common/crypto_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2022 Zano Project
+// Copyright (c) 2014-2023 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
@@ -19,6 +19,8 @@
#include "crypto/hash.h"
#include "crypto/range_proofs.h"
#include "crypto/clsag.h"
+#include "crypto/zarcanum.h"
+#include "crypto/one_out_of_many_proofs.h"
#include "boost_serialization_maps.h"
#include "serialization/keyvalue_enable_POD_serialize_as_string.h"
//
@@ -94,6 +96,25 @@ namespace crypto
END_BOOST_SERIALIZATION()
};
+ struct CLSAG_GGX_signature_serialized : public CLSAG_GGX_signature
+ {
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(c)
+ FIELD((std::vector&)(r_g))
+ FIELD((std::vector&)(r_x))
+ FIELD(K1)
+ FIELD(K2)
+ END_SERIALIZE()
+
+ BEGIN_BOOST_SERIALIZATION()
+ BOOST_SERIALIZE(c)
+ BOOST_SERIALIZE((std::vector&)(r_g))
+ BOOST_SERIALIZE((std::vector&)(r_x))
+ BOOST_SERIALIZE(K1)
+ BOOST_SERIALIZE(K2)
+ END_BOOST_SERIALIZATION()
+ };
+
struct CLSAG_GGXG_signature_serialized : public CLSAG_GGXG_signature
{
BEGIN_SERIALIZE_OBJECT()
@@ -115,6 +136,95 @@ namespace crypto
END_BOOST_SERIALIZATION()
};
+ struct CLSAG_GGXXG_signature_serialized : public CLSAG_GGXXG_signature
+ {
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(c)
+ FIELD_N("r_g", (std::vector&)(r_g))
+ FIELD_N("r_x", (std::vector&)(r_x))
+ FIELD(K1)
+ FIELD(K2)
+ FIELD(K3)
+ FIELD(K4)
+ END_SERIALIZE()
+
+ BEGIN_BOOST_SERIALIZATION()
+ BOOST_SERIALIZE(c)
+ BOOST_SERIALIZE((std::vector&)(r_g))
+ BOOST_SERIALIZE((std::vector&)(r_x))
+ BOOST_SERIALIZE(K1)
+ BOOST_SERIALIZE(K2)
+ BOOST_SERIALIZE(K3)
+ BOOST_SERIALIZE(K4)
+ END_BOOST_SERIALIZATION()
+ };
+
+ struct vector_UG_aggregation_proof_serialized : public vector_UG_aggregation_proof
+ {
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(amount_commitments_for_rp_aggregation)
+ FIELD((std::vector&)(y0s))
+ FIELD((std::vector&)(y1s))
+ FIELD(c)
+ END_SERIALIZE()
+
+ BEGIN_BOOST_SERIALIZATION()
+ BOOST_SERIALIZE(amount_commitments_for_rp_aggregation)
+ BOOST_SERIALIZE((std::vector&)(y0s))
+ BOOST_SERIALIZE((std::vector&)(y1s))
+ BOOST_SERIALIZE(c)
+ END_BOOST_SERIALIZATION()
+ };
+
+ struct linear_composition_proof_s : public linear_composition_proof
+ {
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(c)
+ FIELD(y0)
+ FIELD(y1)
+ END_SERIALIZE()
+
+ BEGIN_BOOST_SERIALIZATION()
+ BOOST_SERIALIZE(c)
+ BOOST_SERIALIZE(y0)
+ BOOST_SERIALIZE(y1)
+ END_BOOST_SERIALIZATION()
+ };
+
+ struct generic_schnorr_sig_s : public generic_schnorr_sig
+ {
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(c)
+ FIELD(y)
+ END_SERIALIZE()
+
+ BEGIN_BOOST_SERIALIZATION()
+ BOOST_SERIALIZE(c)
+ BOOST_SERIALIZE(y)
+ END_BOOST_SERIALIZATION()
+ };
+
+ struct BGE_proof_s : public BGE_proof
+ {
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(A)
+ FIELD(B)
+ FIELD(Pk)
+ FIELD_N("f", (std::vector&)(f))
+ FIELD(y)
+ FIELD(z)
+ END_SERIALIZE()
+
+ BEGIN_BOOST_SERIALIZATION()
+ BOOST_SERIALIZE(A)
+ BOOST_SERIALIZE(B)
+ BOOST_SERIALIZE(Pk)
+ BOOST_SERIALIZE(f)
+ BOOST_SERIALIZE(y)
+ BOOST_SERIALIZE(z)
+ END_BOOST_SERIALIZATION()
+ };
+
} // namespace crypto
BLOB_SERIALIZER(crypto::chacha8_iv);
diff --git a/src/crypto/clsag.cpp b/src/crypto/clsag.cpp
index bcc369e1..ca51c644 100644
--- a/src/crypto/clsag.cpp
+++ b/src/crypto/clsag.cpp
@@ -1,5 +1,5 @@
-// Copyright (c) 2022 Zano Project
-// Copyright (c) 2022 sowle (val@zano.org, crypto.sowle@gmail.com)
+// Copyright (c) 2022-2023 Zano Project
+// Copyright (c) 2022-2023 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.
//
@@ -13,8 +13,13 @@ DISABLE_GCC_AND_CLANG_WARNING(unused-function)
namespace crypto
{
- #define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << std::endl
- #define DBG_PRINT(x) (void(0)) // std::cout << x << std::endl
+#if 0
+# define DBG_VAL_PRINT(x) std::cout << std::setw(30) << std::left << #x ": " << x << std::endl
+# define DBG_PRINT(x) std::cout << x << std::endl
+#else
+# define DBG_VAL_PRINT(x) (void(0))
+# define DBG_PRINT(x) (void(0))
+#endif
static std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return o << pod_to_hex(v); }
static std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return o << pod_to_hex(v); }
@@ -171,7 +176,327 @@ namespace crypto
return c_prev == sig.c;
}
-
+
+ //---------------------------------------------------------------
+
+ //
+ // Disclaimer: extensions to the CLSAG implemented below are non-standard and are in proof-of-concept state.
+ // They shouldn't be used in production code until formal security proofs are done and (ideally) the code is peer-reviewed.
+ // -- sowle
+ //
+
+
+ bool generate_CLSAG_GGX(const hash& m, const std::vector& ring, const point_t& pseudo_out_amount_commitment, const point_t& pseudo_out_blinded_asset_id, const key_image& ki,
+ const scalar_t& secret_0_xp, const scalar_t& secret_1_f, const scalar_t& secret_2_t, uint64_t secret_index, CLSAG_GGX_signature& sig)
+ {
+ DBG_PRINT("== generate_CLSAG_GGX ==");
+ size_t ring_size = ring.size();
+ CRYPTO_CHECK_AND_THROW_MES(ring_size > 0, "ring size is zero");
+ CRYPTO_CHECK_AND_THROW_MES(secret_index < ring_size, "secret_index is out of range");
+
+ // calculate key images
+ point_t ki_base = hash_helper_t::hp(ring[secret_index].stealth_address);
+ point_t key_image = secret_0_xp * ki_base;
+
+#ifndef NDEBUG
+ CRYPTO_CHECK_AND_THROW_MES(key_image == point_t(ki), "key image 0 mismatch");
+ CRYPTO_CHECK_AND_THROW_MES((secret_0_xp * c_point_G).to_public_key() == ring[secret_index].stealth_address, "secret_0_xp mismatch");
+ CRYPTO_CHECK_AND_THROW_MES( secret_1_f * c_point_G == 8 * point_t(ring[secret_index].amount_commitment) - pseudo_out_amount_commitment, "secret_1_f mismatch");
+ CRYPTO_CHECK_AND_THROW_MES( secret_2_t * c_point_X == 8 * point_t(ring[secret_index].blinded_asset_id) - pseudo_out_blinded_asset_id, "secret_2_t mismatch");
+ //CRYPTO_CHECK_AND_THROW_MES( secret_3_q * c_point_G == 8 * point_t(ring[secret_index].concealing_point), "");
+ //CRYPTO_CHECK_AND_THROW_MES( secret_4_x * c_point_X == extended_amount_commitment - 8 * point_t(ring[secret_index].amount_commitment) - 8 * point_t(ring[secret_index].concealing_point), "");
+#endif
+
+ point_t K1_div8 = (c_scalar_1div8 * secret_1_f) * ki_base;
+ K1_div8.to_public_key(sig.K1);
+ point_t K1 = K1_div8;
+ K1.modify_mul8();
+
+ point_t K2_div8 = (c_scalar_1div8 * secret_2_t) * ki_base;
+ K2_div8.to_public_key(sig.K2);
+ point_t K2 = K2_div8;
+ K2.modify_mul8();
+
+ //point_t K3_div8 = (c_scalar_1div8 * secret_3_q) * ki_base;
+ //K3_div8.to_public_key(sig.K3);
+ //point_t K3 = K3_div8;
+ //K3.modify_mul8();
+
+ //point_t K4_div8 = (c_scalar_1div8 * secret_4_x) * ki_base;
+ //K4_div8.to_public_key(sig.K4);
+ //point_t K4 = K4_div8;
+ //K4.modify_mul8();
+
+ // calculate aggregation coefficients
+ hash_helper_t::hs_t hsc(4 + 3 * ring_size);
+ hsc.add_scalar(m);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ hsc.add_pub_key(ring[i].stealth_address);
+ hsc.add_pub_key(ring[i].amount_commitment);
+ hsc.add_pub_key(ring[i].blinded_asset_id);
+ DBG_PRINT("ring[" << i << "]: sa:" << ring[i].stealth_address << ", ac:" << ring[i].amount_commitment << ", baid:" << ring[i].blinded_asset_id);
+ }
+ hsc.add_point(c_scalar_1div8 * pseudo_out_amount_commitment);
+ hsc.add_point(c_scalar_1div8 * pseudo_out_blinded_asset_id);
+ hsc.add_key_image(ki);
+ hash input_hash = hsc.calc_hash_no_reduce();
+ DBG_VAL_PRINT(input_hash);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_0);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_0 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_0);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_1);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_1 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_1);
+
+ // may we get rid of it?
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_2);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_2 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_2);
+
+ //hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_3);
+ //hsc.add_hash(input_hash);
+ //scalar_t agg_coeff_3 = hsc.calc_hash();
+ //DBG_VAL_PRINT(agg_coeff_3);
+
+ // prepare A_i, Q_i
+ std::vector A_i, Q_i;
+ A_i.reserve(ring_size), Q_i.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ A_i.emplace_back(ring[i].amount_commitment);
+ A_i.back().modify_mul8();
+ Q_i.emplace_back(ring[i].blinded_asset_id);
+ Q_i.back().modify_mul8();
+ DBG_PRINT("A_i[" << i << "] = " << A_i[i] << " Q_i[" << i << "] = " << Q_i[i]);
+ }
+
+ // calculate aggregate pub keys (layers 0, 1; G components)
+ std::vector W_pub_keys_g;
+ W_pub_keys_g.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ W_pub_keys_g.emplace_back(
+ agg_coeff_0 * point_t(ring[i].stealth_address) +
+ agg_coeff_1 * (A_i[i] - pseudo_out_amount_commitment)
+ );
+ DBG_VAL_PRINT(W_pub_keys_g[i]);
+ }
+
+ // calculate aggregate pub keys (layer 2; X component)
+ std::vector W_pub_keys_x;
+ W_pub_keys_x.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ W_pub_keys_x.emplace_back(
+ agg_coeff_2 * (Q_i[i] - pseudo_out_blinded_asset_id)
+ );
+ DBG_VAL_PRINT(W_pub_keys_x[i]);
+ }
+
+ // aggregate secret key (layers 0, 1; G component)
+ scalar_t w_sec_key_g = agg_coeff_0 * secret_0_xp + agg_coeff_1 * secret_1_f; // + agg_coeff_3 * secret_3_q;
+ DBG_VAL_PRINT(w_sec_key_g * c_point_G);
+
+ // aggregate secret key (layer 2; X component)
+ scalar_t w_sec_key_x = agg_coeff_2 * secret_2_t;
+ DBG_VAL_PRINT(w_sec_key_x * c_point_X);
+
+ // calculate aggregate key image (layers 0, 1, 3; G component)
+ point_t W_key_image_g = agg_coeff_0 * key_image + agg_coeff_1 * K1 /* + agg_coeff_3 * K3 */;
+ DBG_VAL_PRINT(key_image);
+ DBG_VAL_PRINT(K1);
+ //DBG_VAL_PRINT(K3);
+ DBG_VAL_PRINT(W_key_image_g);
+
+ // calculate aggregate key image (layer 2; X component)
+ point_t W_key_image_x = agg_coeff_2 * K2;
+ DBG_VAL_PRINT(K2);
+ DBG_VAL_PRINT(W_key_image_x);
+
+#ifndef NDEBUG
+ CRYPTO_CHECK_AND_THROW_MES(w_sec_key_g * c_point_G == W_pub_keys_g[secret_index], "aggregated secret G and pub key mismatch");
+ CRYPTO_CHECK_AND_THROW_MES(w_sec_key_g * hash_helper_t::hp(ring[secret_index].stealth_address) == W_key_image_g, "aggregated secret G and key image mismatch");
+ CRYPTO_CHECK_AND_THROW_MES(w_sec_key_x * c_point_X == W_pub_keys_x[secret_index], "aggregated secret X and pub key mismatch");
+ CRYPTO_CHECK_AND_THROW_MES(w_sec_key_x * hash_helper_t::hp(ring[secret_index].stealth_address) == W_key_image_x, "aggregated secret X and key image mismatch");
+#endif
+
+ // initial commitment
+ scalar_t alpha_g = scalar_t::random(); // randomness for layers 0,1
+ scalar_t alpha_x = scalar_t::random(); // randomness for layer 2
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_CHALLENGE);
+ hsc.add_hash(input_hash);
+ hsc.add_point(alpha_g * c_point_G);
+ hsc.add_point(alpha_g * ki_base);
+ hsc.add_point(alpha_x * c_point_X);
+ hsc.add_point(alpha_x * ki_base);
+ //DBG_PRINT("c[" << secret_index << "] = Hs(ih, " << alpha_g * c_point_G << ", " << alpha_g * ki_base << ", " << alpha_x * c_point_X << ", " << alpha_x * ki_base << ")");
+ scalar_t c_prev = hsc.calc_hash(); // c_{secret_index + 1}
+
+ sig.r_g.clear();
+ sig.r_x.clear();
+ sig.r_g.reserve(ring_size);
+ sig.r_x.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ sig.r_g.emplace_back(scalar_t::random());
+ sig.r_x.emplace_back(scalar_t::random());
+ }
+
+ for(size_t j = 0, i = (secret_index + 1) % ring_size; j < ring_size - 1; ++j, i = (i + 1) % ring_size)
+ {
+ DBG_PRINT("c[" << i << "] = " << c_prev);
+ if (i == 0)
+ sig.c = c_prev; // c_0
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_CHALLENGE);
+ hsc.add_hash(input_hash);
+ hsc.add_point(sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i]);
+ hsc.add_point(sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g);
+ hsc.add_point(sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i]);
+ hsc.add_point(sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x);
+ c_prev = hsc.calc_hash(); // c_{i + 1}
+ }
+ DBG_PRINT("c[" << secret_index << "] = " << c_prev);
+
+ if (secret_index == 0)
+ sig.c = c_prev;
+
+ sig.r_g[secret_index] = alpha_g - c_prev * w_sec_key_g;
+ sig.r_x[secret_index] = alpha_x - c_prev * w_sec_key_x;
+
+ return true;
+ }
+
+ bool verify_CLSAG_GGX(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment,
+ const public_key& pseudo_out_blinded_asset_id, const key_image& ki, const CLSAG_GGX_signature& sig)
+ {
+ DBG_PRINT("== verify_CLSAG_GGX ==");
+ size_t ring_size = ring.size();
+ CRYPTO_CHECK_AND_THROW_MES(ring_size > 0, "ring size is zero");
+ CRYPTO_CHECK_AND_THROW_MES(ring_size == sig.r_g.size(), "ring size != r_g size");
+ CRYPTO_CHECK_AND_THROW_MES(ring_size == sig.r_x.size(), "ring size != r_x size");
+
+ point_t key_image(ki);
+ CRYPTO_CHECK_AND_THROW_MES(key_image.is_in_main_subgroup(), "key image 0 does not belong to the main subgroup");
+
+ point_t pseudo_out_amount_commitment_pt(pseudo_out_amount_commitment);
+ pseudo_out_amount_commitment_pt.modify_mul8();
+
+ point_t pseudo_out_blinded_asset_id_pt(pseudo_out_blinded_asset_id);
+ pseudo_out_blinded_asset_id_pt.modify_mul8();
+
+ // calculate aggregation coefficients
+ hash_helper_t::hs_t hsc(4 + 3 * ring_size);
+ hsc.add_scalar(m);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ hsc.add_pub_key(ring[i].stealth_address);
+ hsc.add_pub_key(ring[i].amount_commitment);
+ hsc.add_pub_key(ring[i].blinded_asset_id);
+ DBG_PRINT("ring[" << i << "]: sa:" << ring[i].stealth_address << ", ac:" << ring[i].amount_commitment << ", baid:" << ring[i].blinded_asset_id);
+ }
+ hsc.add_pub_key(pseudo_out_amount_commitment);
+ hsc.add_pub_key(pseudo_out_blinded_asset_id);
+ hsc.add_key_image(ki);
+ hash input_hash = hsc.calc_hash_no_reduce();
+ DBG_VAL_PRINT(input_hash);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_0);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_0 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_0);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_1);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_1 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_1);
+
+ // may we get rid of it?
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_2);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_2 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_2);
+
+ //hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_3);
+ //hsc.add_hash(input_hash);
+ //scalar_t agg_coeff_3 = hsc.calc_hash();
+ //DBG_VAL_PRINT(agg_coeff_3);
+
+ // prepare A_i, Q_i
+ std::vector A_i, Q_i;
+ A_i.reserve(ring_size), Q_i.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ A_i.emplace_back(ring[i].amount_commitment);
+ A_i.back().modify_mul8();
+ Q_i.emplace_back(ring[i].blinded_asset_id);
+ Q_i.back().modify_mul8();
+ DBG_PRINT("A_i[" << i << "] = " << A_i[i] << " Q_i[" << i << "] = " << Q_i[i]);
+ }
+
+ // calculate aggregate pub keys (layers 0, 1; G components)
+ std::vector W_pub_keys_g;
+ W_pub_keys_g.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ W_pub_keys_g.emplace_back(
+ agg_coeff_0 * point_t(ring[i].stealth_address) +
+ agg_coeff_1 * (A_i[i] - pseudo_out_amount_commitment_pt)
+ );
+ DBG_VAL_PRINT(W_pub_keys_g[i]);
+ }
+
+ // calculate aggregate pub keys (layer 2; X component)
+ std::vector W_pub_keys_x;
+ W_pub_keys_x.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ W_pub_keys_x.emplace_back(
+ agg_coeff_2 * (Q_i[i] - pseudo_out_blinded_asset_id_pt)
+ );
+ DBG_VAL_PRINT(W_pub_keys_x[i]);
+ }
+
+ // calculate aggregate key image (layers 0, 1, 3; G components)
+ point_t W_key_image_g =
+ agg_coeff_0 * key_image +
+ agg_coeff_1 * point_t(sig.K1).modify_mul8();
+ // agg_coeff_3 * point_t(sig.K3).modify_mul8();
+ DBG_VAL_PRINT(point_t(sig.K1).modify_mul8());
+ //DBG_VAL_PRINT(point_t(sig.K3).modify_mul8());
+ DBG_VAL_PRINT(W_key_image_g);
+
+ // calculate aggregate key image (layer 2; X component)
+ point_t W_key_image_x =
+ agg_coeff_2 * point_t(sig.K2).modify_mul8();
+ DBG_VAL_PRINT(point_t(sig.K2).modify_mul8());
+ DBG_VAL_PRINT(W_key_image_x);
+
+
+ scalar_t c_prev = sig.c;
+ DBG_PRINT("c[0] = " << c_prev);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_CHALLENGE);
+ hsc.add_hash(input_hash);
+ hsc.add_point(sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i]);
+ hsc.add_point(sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g);
+ hsc.add_point(sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i]);
+ hsc.add_point(sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x);
+ c_prev = hsc.calc_hash(); // c_{i + 1}
+ DBG_PRINT("c[" << i + 1 << "] = " << c_prev);
+ //DBG_PRINT("c[" << i + 1 << "] = Hs(ih, " << sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i] << ", " << sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g << ", " << sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i] << ", " << sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x << ")");
+ }
+
+ return c_prev == sig.c;
+ }
+
+
//---------------------------------------------------------------
@@ -192,7 +517,7 @@ namespace crypto
CRYPTO_CHECK_AND_THROW_MES((secret_0_xp * c_point_G).to_public_key() == ring[secret_index].stealth_address, "secret_0_xp mismatch");
CRYPTO_CHECK_AND_THROW_MES(secret_1_f * c_point_G == 8 * point_t(ring[secret_index].amount_commitment) - pseudo_out_amount_commitment, "secret_1_f mismatch");
CRYPTO_CHECK_AND_THROW_MES(secret_3_q * c_point_G == 8 * point_t(ring[secret_index].concealing_point), "secret_3_q mismatch");
- CRYPTO_CHECK_AND_THROW_MES(secret_2_x * c_point_X == extended_amount_commitment - 8 * point_t(ring[secret_index].amount_commitment) - 8 * point_t(ring[secret_index].concealing_point), "secret_3_q mismatch");
+ CRYPTO_CHECK_AND_THROW_MES(secret_2_x * c_point_X == extended_amount_commitment - 8 * point_t(ring[secret_index].amount_commitment) - 8 * point_t(ring[secret_index].concealing_point), "secret_2_x mismatch");
#endif
point_t K1_div8 = (c_scalar_1div8 * secret_1_f) * ki_base;
@@ -482,4 +807,354 @@ namespace crypto
return c_prev == sig.c;
}
+
+ //---------------------------------------------------------------
+
+
+ bool generate_CLSAG_GGXXG(const hash& m, const std::vector& ring, const point_t& pseudo_out_amount_commitment, const point_t& pseudo_out_blinded_asset_id, const point_t& extended_amount_commitment, const key_image& ki,
+ const scalar_t& secret_0_xp, const scalar_t& secret_1_f, const scalar_t& secret_2_t, const scalar_t& secret_3_x, const scalar_t& secret_4_q, uint64_t secret_index,
+ CLSAG_GGXXG_signature& sig)
+ {
+ DBG_PRINT("== generate_CLSAG_GGXXG ==");
+ size_t ring_size = ring.size();
+ CRYPTO_CHECK_AND_THROW_MES(ring_size > 0, "ring size is zero");
+ CRYPTO_CHECK_AND_THROW_MES(secret_index < ring_size, "secret_index is out of range");
+
+ // calculate key images
+ point_t ki_base = hash_helper_t::hp(ring[secret_index].stealth_address);
+ point_t key_image = secret_0_xp * ki_base;
+
+#ifndef NDEBUG
+ CRYPTO_CHECK_AND_THROW_MES(key_image == point_t(ki), "key image 0 mismatch");
+ CRYPTO_CHECK_AND_THROW_MES((secret_0_xp * c_point_G).to_public_key() == ring[secret_index].stealth_address, "secret_0_xp mismatch");
+ CRYPTO_CHECK_AND_THROW_MES( secret_1_f * c_point_G == 8 * point_t(ring[secret_index].amount_commitment) - pseudo_out_amount_commitment, "secret_1_f mismatch");
+ CRYPTO_CHECK_AND_THROW_MES( secret_2_t * c_point_X == 8 * point_t(ring[secret_index].blinded_asset_id) - pseudo_out_blinded_asset_id, "secret_2_t mismatch");
+ CRYPTO_CHECK_AND_THROW_MES( secret_3_x * c_point_X == extended_amount_commitment - 8 * point_t(ring[secret_index].amount_commitment) - 8 * point_t(ring[secret_index].concealing_point), "");
+ CRYPTO_CHECK_AND_THROW_MES( secret_4_q * c_point_G == 8 * point_t(ring[secret_index].concealing_point), "secret_3_q mismatch");
+#endif
+
+ point_t K1_div8 = (c_scalar_1div8 * secret_1_f) * ki_base;
+ K1_div8.to_public_key(sig.K1);
+ point_t K1 = K1_div8;
+ K1.modify_mul8();
+
+ point_t K2_div8 = (c_scalar_1div8 * secret_2_t) * ki_base;
+ K2_div8.to_public_key(sig.K2);
+ point_t K2 = K2_div8;
+ K2.modify_mul8();
+
+ point_t K3_div8 = (c_scalar_1div8 * secret_3_x) * ki_base;
+ K3_div8.to_public_key(sig.K3);
+ point_t K3 = K3_div8;
+ K3.modify_mul8();
+
+ point_t K4_div8 = (c_scalar_1div8 * secret_4_q) * ki_base;
+ K4_div8.to_public_key(sig.K4);
+ point_t K4 = K4_div8;
+ K4.modify_mul8();
+
+ // calculate aggregation coefficients
+ hash_helper_t::hs_t hsc(5 + 4 * ring_size);
+ hsc.add_scalar(m);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ hsc.add_pub_key(ring[i].stealth_address);
+ hsc.add_pub_key(ring[i].amount_commitment);
+ hsc.add_pub_key(ring[i].blinded_asset_id);
+ hsc.add_pub_key(ring[i].concealing_point);
+ DBG_PRINT("ring[" << i << "]: sa:" << ring[i].stealth_address << ", ac:" << ring[i].amount_commitment << ", baid:" << ring[i].blinded_asset_id << ", cp:" << ring[i].concealing_point);
+ }
+ hsc.add_point(c_scalar_1div8 * pseudo_out_amount_commitment);
+ hsc.add_point(c_scalar_1div8 * pseudo_out_blinded_asset_id);
+ hsc.add_point(c_scalar_1div8 * extended_amount_commitment);
+ hsc.add_key_image(ki);
+ hash input_hash = hsc.calc_hash_no_reduce();
+ DBG_VAL_PRINT(input_hash);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_0);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_0 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_0);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_1);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_1 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_1);
+
+ // may we get rid of it?
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_2);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_2 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_2);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_3);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_3 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_3);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_4);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_4 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_4);
+
+ // prepare A_i, Q_i
+ std::vector A_i, P_i, Q_i;
+ A_i.reserve(ring_size), P_i.reserve(ring_size), Q_i.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ A_i.emplace_back(ring[i].amount_commitment);
+ A_i.back().modify_mul8();
+ P_i.emplace_back(ring[i].blinded_asset_id);
+ P_i.back().modify_mul8();
+ Q_i.emplace_back(ring[i].concealing_point);
+ Q_i.back().modify_mul8();
+ DBG_PRINT("A_i[" << i << "] = " << A_i[i] << " P_i[" << i << "] = " << P_i[i] << " Q_i[" << i << "] = " << Q_i[i]);
+ }
+
+ // calculate aggregate pub keys (layers 0, 1, 4; G components)
+ std::vector W_pub_keys_g;
+ W_pub_keys_g.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ W_pub_keys_g.emplace_back(
+ agg_coeff_0 * point_t(ring[i].stealth_address) +
+ agg_coeff_1 * (A_i[i] - pseudo_out_amount_commitment) +
+ agg_coeff_4 * Q_i[i]
+ );
+ DBG_VAL_PRINT(W_pub_keys_g[i]);
+ }
+
+ // calculate aggregate pub keys (layerx 2, 3; X component)
+ std::vector W_pub_keys_x;
+ W_pub_keys_x.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ W_pub_keys_x.emplace_back(
+ agg_coeff_2 * (P_i[i] - pseudo_out_blinded_asset_id) +
+ agg_coeff_3 * (extended_amount_commitment - A_i[i] - Q_i[i])
+ );
+ DBG_VAL_PRINT(W_pub_keys_x[i]);
+ }
+
+ // aggregate secret key (layers 0, 1, 4; G component)
+ scalar_t w_sec_key_g = agg_coeff_0 * secret_0_xp + agg_coeff_1 * secret_1_f + agg_coeff_4 * secret_4_q;
+ DBG_VAL_PRINT(w_sec_key_g * c_point_G);
+
+ // aggregate secret key (layer 2, 3; X component)
+ scalar_t w_sec_key_x = agg_coeff_2 * secret_2_t + agg_coeff_3 * secret_3_x;
+ DBG_VAL_PRINT(w_sec_key_x * c_point_X);
+
+ // calculate aggregate key image (layers 0, 1, 4; G component)
+ point_t W_key_image_g = agg_coeff_0 * key_image + agg_coeff_1 * K1 + agg_coeff_4 * K4;
+ DBG_VAL_PRINT(key_image);
+ DBG_VAL_PRINT(K1);
+ DBG_VAL_PRINT(K4);
+ DBG_VAL_PRINT(W_key_image_g);
+
+ // calculate aggregate key image (layer 2, 3; X component)
+ point_t W_key_image_x = agg_coeff_2 * K2 + agg_coeff_3 * K3;
+ DBG_VAL_PRINT(K2);
+ DBG_VAL_PRINT(K3);
+ DBG_VAL_PRINT(W_key_image_x);
+
+#ifndef NDEBUG
+ CRYPTO_CHECK_AND_THROW_MES(w_sec_key_g * c_point_G == W_pub_keys_g[secret_index], "aggregated secret G and pub key mismatch");
+ CRYPTO_CHECK_AND_THROW_MES(w_sec_key_g * hash_helper_t::hp(ring[secret_index].stealth_address) == W_key_image_g, "aggregated secret G and key image mismatch");
+ CRYPTO_CHECK_AND_THROW_MES(w_sec_key_x * c_point_X == W_pub_keys_x[secret_index], "aggregated secret X and pub key mismatch");
+ CRYPTO_CHECK_AND_THROW_MES(w_sec_key_x * hash_helper_t::hp(ring[secret_index].stealth_address) == W_key_image_x, "aggregated secret X and key image mismatch");
+#endif
+
+ // initial commitment
+ scalar_t alpha_g = scalar_t::random(); // randomness for layers 0,1,4
+ scalar_t alpha_x = scalar_t::random(); // randomness for layer 2,3
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_CHALLENGE);
+ hsc.add_hash(input_hash);
+ hsc.add_point(alpha_g * c_point_G);
+ hsc.add_point(alpha_g * ki_base);
+ hsc.add_point(alpha_x * c_point_X);
+ hsc.add_point(alpha_x * ki_base);
+ //DBG_PRINT("c[" << secret_index << "] = Hs(ih, " << alpha_g * c_point_G << ", " << alpha_g * ki_base << ", " << alpha_x * c_point_X << ", " << alpha_x * ki_base << ")");
+ scalar_t c_prev = hsc.calc_hash(); // c_{secret_index + 1}
+
+ sig.r_g.clear();
+ sig.r_x.clear();
+ sig.r_g.reserve(ring_size);
+ sig.r_x.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ sig.r_g.emplace_back(scalar_t::random());
+ sig.r_x.emplace_back(scalar_t::random());
+ }
+
+ for(size_t j = 0, i = (secret_index + 1) % ring_size; j < ring_size - 1; ++j, i = (i + 1) % ring_size)
+ {
+ DBG_PRINT("c[" << i << "] = " << c_prev);
+ if (i == 0)
+ sig.c = c_prev; // c_0
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_CHALLENGE);
+ hsc.add_hash(input_hash);
+ hsc.add_point(sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i]);
+ hsc.add_point(sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g);
+ hsc.add_point(sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i]);
+ hsc.add_point(sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x);
+ c_prev = hsc.calc_hash(); // c_{i + 1}
+ //DBG_PRINT("c[" << i + 1 << "] = Hs(ih, " << sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i] << ", " << sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g << ", " << sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i] << ", " << sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x << ")");
+ }
+ DBG_PRINT("c[" << secret_index << "] = " << c_prev);
+
+
+ if (secret_index == 0)
+ sig.c = c_prev;
+
+ sig.r_g[secret_index] = alpha_g - c_prev * w_sec_key_g;
+ sig.r_x[secret_index] = alpha_x - c_prev * w_sec_key_x;
+
+ return true;
+ }
+
+ bool verify_CLSAG_GGXXG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment, const public_key& pseudo_out_blinded_asset_id, const public_key& extended_amount_commitment, const key_image& ki,
+ const CLSAG_GGXXG_signature& sig)
+ {
+ DBG_PRINT("== verify_CLSAG_GGXXG ==");
+ size_t ring_size = ring.size();
+ CRYPTO_CHECK_AND_THROW_MES(ring_size > 0, "ring size is zero");
+ CRYPTO_CHECK_AND_THROW_MES(ring_size == sig.r_g.size(), "ring size != r_g size");
+ CRYPTO_CHECK_AND_THROW_MES(ring_size == sig.r_x.size(), "ring size != r_x size");
+
+ point_t key_image(ki);
+ CRYPTO_CHECK_AND_THROW_MES(key_image.is_in_main_subgroup(), "key image 0 does not belong to the main subgroup");
+
+ point_t pseudo_out_amount_commitment_pt(pseudo_out_amount_commitment);
+ pseudo_out_amount_commitment_pt.modify_mul8();
+
+ point_t pseudo_out_blinded_asset_id_pt(pseudo_out_blinded_asset_id);
+ pseudo_out_blinded_asset_id_pt.modify_mul8();
+
+ point_t extended_amount_commitment_pt(extended_amount_commitment);
+ extended_amount_commitment_pt.modify_mul8();
+
+ // calculate aggregation coefficients
+ hash_helper_t::hs_t hsc(5 + 4 * ring_size);
+ hsc.add_scalar(m);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ hsc.add_pub_key(ring[i].stealth_address);
+ hsc.add_pub_key(ring[i].amount_commitment);
+ hsc.add_pub_key(ring[i].blinded_asset_id);
+ hsc.add_pub_key(ring[i].concealing_point);
+ DBG_PRINT("ring[" << i << "]: sa:" << ring[i].stealth_address << ", ac:" << ring[i].amount_commitment << ", baid:" << ring[i].blinded_asset_id << ", cp:" << ring[i].concealing_point);
+ }
+ hsc.add_pub_key(pseudo_out_amount_commitment);
+ hsc.add_pub_key(pseudo_out_blinded_asset_id);
+ hsc.add_pub_key(extended_amount_commitment);
+ hsc.add_key_image(ki);
+ hash input_hash = hsc.calc_hash_no_reduce();
+ DBG_VAL_PRINT(input_hash);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_0);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_0 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_0);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_1);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_1 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_1);
+
+ // may we get rid of it?
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_2);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_2 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_2);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_3);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_3 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_3);
+
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_LAYER_4);
+ hsc.add_hash(input_hash);
+ scalar_t agg_coeff_4 = hsc.calc_hash();
+ DBG_VAL_PRINT(agg_coeff_4);
+
+ // prepare A_i, Q_i
+ std::vector A_i, P_i, Q_i;
+ A_i.reserve(ring_size), P_i.reserve(ring_size), Q_i.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ A_i.emplace_back(ring[i].amount_commitment);
+ A_i.back().modify_mul8();
+ P_i.emplace_back(ring[i].blinded_asset_id);
+ P_i.back().modify_mul8();
+ Q_i.emplace_back(ring[i].concealing_point);
+ Q_i.back().modify_mul8();
+ DBG_PRINT("A_i[" << i << "] = " << A_i[i] << " P_i[" << i << "] = " << P_i[i] << " Q_i[" << i << "] = " << Q_i[i]);
+ }
+
+ // calculate aggregate pub keys (layers 0, 1, 4; G components)
+ std::vector W_pub_keys_g;
+ W_pub_keys_g.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ W_pub_keys_g.emplace_back(
+ agg_coeff_0 * point_t(ring[i].stealth_address) +
+ agg_coeff_1 * (A_i[i] - pseudo_out_amount_commitment_pt) +
+ agg_coeff_4 * Q_i[i]
+ );
+ DBG_VAL_PRINT(W_pub_keys_g[i]);
+ }
+
+ // calculate aggregate pub keys (layer 2, 3; X component)
+ std::vector W_pub_keys_x;
+ W_pub_keys_x.reserve(ring_size);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ W_pub_keys_x.emplace_back(
+ agg_coeff_2 * (P_i[i] - pseudo_out_blinded_asset_id_pt) +
+ agg_coeff_3 * (extended_amount_commitment_pt - A_i[i] - Q_i[i])
+ );
+ DBG_VAL_PRINT(W_pub_keys_x[i]);
+ }
+
+ DBG_VAL_PRINT(point_t(ki));
+
+ // calculate aggregate key image (layers 0, 1, 4; G components)
+ DBG_VAL_PRINT(point_t(sig.K1).modify_mul8());
+ point_t W_key_image_g =
+ agg_coeff_0 * key_image +
+ agg_coeff_1 * point_t(sig.K1).modify_mul8() +
+ agg_coeff_4 * point_t(sig.K4).modify_mul8();
+ DBG_VAL_PRINT(point_t(sig.K1).modify_mul8());
+ DBG_VAL_PRINT(point_t(sig.K4).modify_mul8());
+ DBG_VAL_PRINT(W_key_image_g);
+
+ // calculate aggregate key image (layer 2, 3; X component)
+ point_t W_key_image_x =
+ agg_coeff_2 * point_t(sig.K2).modify_mul8() +
+ agg_coeff_3 * point_t(sig.K3).modify_mul8();
+ DBG_VAL_PRINT(point_t(sig.K2).modify_mul8());
+ DBG_VAL_PRINT(point_t(sig.K3).modify_mul8());
+ DBG_VAL_PRINT(W_key_image_x);
+
+
+ scalar_t c_prev = sig.c;
+ DBG_PRINT("c[0] = " << c_prev);
+ for(size_t i = 0; i < ring_size; ++i)
+ {
+ hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXXG_CHALLENGE);
+ hsc.add_hash(input_hash);
+ hsc.add_point(sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i]);
+ hsc.add_point(sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g);
+ hsc.add_point(sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i]);
+ hsc.add_point(sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x);
+ c_prev = hsc.calc_hash(); // c_{i + 1}
+ DBG_PRINT("c[" << i + 1 << "] = " << c_prev);
+ //DBG_PRINT("c[" << i + 1 << "] = Hs(ih, " << sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i] << ", " << sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g << ", " << sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i] << ", " << sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x << ")");
+ }
+
+ return c_prev == sig.c;
+ }
+
+
+
} // namespace crypto
diff --git a/src/crypto/clsag.h b/src/crypto/clsag.h
index 8a4400e5..576ba917 100644
--- a/src/crypto/clsag.h
+++ b/src/crypto/clsag.h
@@ -1,5 +1,5 @@
-// Copyright (c) 2022 Zano Project
-// Copyright (c) 2022 sowle (val@zano.org, crypto.sowle@gmail.com)
+// Copyright (c) 2022-2023 Zano Project
+// Copyright (c) 2022-2023 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.
//
@@ -47,7 +47,52 @@ namespace crypto
bool verify_CLSAG_GG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment, const key_image& ki,
const CLSAG_GG_signature& sig);
-
+
+ //
+ // Disclaimer: extensions to the CLSAG implemented below are non-standard and are in proof-of-concept state.
+ // They shouldn't be used in production code until formal security proofs are done and (ideally) the code is peer-reviewed.
+ // -- sowle
+ //
+
+
+
+ //
+ // 3-CLSAG
+ //
+
+
+ // 3-CLSAG signature (with respect to the group element G, G, X -- that's why 'GGX')
+ struct CLSAG_GGX_signature
+ {
+ scalar_t c;
+ scalar_vec_t r_g; // for G-components (layers 0, 1), size = size of the ring
+ scalar_vec_t r_x; // for X-component (layer 2), size = size of the ring
+ public_key K1; // auxiliary key image for layer 1 (G)
+ public_key K2; // auxiliary key image for layer 2 (X)
+ };
+
+ struct CLSAG_GGX_input_ref_t : public CLSAG_GG_input_ref_t
+ {
+ CLSAG_GGX_input_ref_t(const public_key& stealth_address, const public_key& amount_commitment, const public_key& blinded_asset_id)
+ : CLSAG_GG_input_ref_t(stealth_address, amount_commitment)
+ , blinded_asset_id(blinded_asset_id)
+ {}
+
+ const public_key& blinded_asset_id; // T, premultiplied by 1/8
+ };
+
+ // pseudo_out_amount_commitment -- not premultiplied by 1/8
+ // pseudo_out_asset_id -- not premultiplied by 1/8
+ bool generate_CLSAG_GGX(const hash& m, const std::vector& ring, const point_t& pseudo_out_amount_commitment, const point_t& pseudo_out_asset_id, const key_image& ki,
+ const scalar_t& secret_0_xp, const scalar_t& secret_1_f, const scalar_t& secret_2_t, uint64_t secret_index, CLSAG_GGX_signature& sig);
+
+ // pseudo_out_amount_commitment -- premultiplied by 1/8
+ // pseudo_out_asset_id -- premultiplied by 1/8
+ // may throw an exception TODO @#@# make sure it's okay
+ bool verify_CLSAG_GGX(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment,
+ const public_key& pseudo_out_asset_id, const key_image& ki, const CLSAG_GGX_signature& sig);
+
+
//
// 4-CLSAG
//
@@ -85,4 +130,47 @@ namespace crypto
bool verify_CLSAG_GGXG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment,
const public_key& extended_amount_commitment, const key_image& ki, const CLSAG_GGXG_signature& sig);
+
+
+ //
+ // 5-CLSAG
+ //
+
+
+ // 5-CLSAG signature (with respect to the group element G, G, X, X, G -- that's why 'GGXXG')
+ struct CLSAG_GGXXG_signature
+ {
+ scalar_t c;
+ scalar_vec_t r_g; // for G-components (layers 0, 1, 4), size = size of the ring
+ scalar_vec_t r_x; // for X-component (layers 2, 3), size = size of the ring
+ public_key K1; // auxiliary key image for layer 1 (G)
+ public_key K2; // auxiliary key image for layer 2 (X)
+ public_key K3; // auxiliary key image for layer 2 (X)
+ public_key K4; // auxiliary key image for layer 3 (G)
+ };
+
+ struct CLSAG_GGXXG_input_ref_t : public CLSAG_GGX_input_ref_t
+ {
+ CLSAG_GGXXG_input_ref_t(const public_key& stealth_address, const public_key& amount_commitment, const public_key& blinded_asset_id, const public_key& concealing_point)
+ : CLSAG_GGX_input_ref_t(stealth_address, amount_commitment, blinded_asset_id)
+ , concealing_point(concealing_point)
+ {}
+
+ const public_key& concealing_point; // Q, premultiplied by 1/8
+ };
+
+ // pseudo_out_amount_commitment -- not premultiplied by 1/8
+ // pseudo_out_asset_id -- not premultiplied by 1/8
+ // extended_amount_commitment -- not premultiplied by 1/8
+ bool generate_CLSAG_GGXXG(const hash& m, const std::vector& ring, const point_t& pseudo_out_amount_commitment, const point_t& pseudo_out_blinded_asset_id, const point_t& extended_amount_commitment, const key_image& ki,
+ const scalar_t& secret_0_xp, const scalar_t& secret_1_f, const scalar_t& secret_2_r, const scalar_t& secret_3_x, const scalar_t& secret_4_q, uint64_t secret_index, CLSAG_GGXXG_signature& sig);
+
+ // pseudo_out_amount_commitment -- premultiplied by 1/8
+ // pseudo_out_asset_id -- premultiplied by 1/8
+ // extended_amount_commitment -- premultiplied by 1/8
+ // may throw an exception TODO @#@# make sure it's okay
+ bool verify_CLSAG_GGXXG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment, const public_key& pseudo_out_blinded_asset_id, const public_key& extended_amount_commitment, const key_image& ki,
+ const CLSAG_GGXXG_signature& sig);
+
+
} // namespace crypto
diff --git a/src/crypto/crypto-sugar.cpp b/src/crypto/crypto-sugar.cpp
index 1823833a..fe7cd63a 100644
--- a/src/crypto/crypto-sugar.cpp
+++ b/src/crypto/crypto-sugar.cpp
@@ -10,27 +10,5 @@
namespace crypto
{
- const point_g_t c_point_G;
-
- const scalar_t c_scalar_1 = { 1 };
- const scalar_t c_scalar_2p64 = { 0, 1, 0, 0 };
- const scalar_t c_scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
- const scalar_t c_scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
- const scalar_t c_scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
- const scalar_t c_scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
- const scalar_t c_scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff };
- const scalar_t c_scalar_1div8 = { 0x6106e529e2dc2f79, 0x07d39db37d1cdad0, 0x0, 0x0600000000000000 };
-
- const point_t c_point_H = { 0x05087c1f5b9b32d6, 0x00547595f445c3b5, 0x764df64578552f2a, 0x8a49a651e0e0da45 }; // == Hp(G), this is being checked in bpp_basics
- const point_t c_point_H2 = { 0x70c8d1ab9dbf1cc0, 0xc561bb12639a8516, 0x3cfff1def9e5b268, 0xe0936386f3bcce1a }; // == Hp("h2_generator"), checked in bpp_basics
-
- const point_t c_point_X = { 0xc9d2f543dbbc253a, 0x87099e9ac33d06dd, 0x76bcf12dcf6ffcba, 0x20384a4a88752d32 }; // == Hp("X_generator"), checked in clsag_ggxg_basics
-
- const point_t c_point_0 = point_t(point_t::tag_zero());
-
- const point_t c_point_H_plus_G = c_point_H + c_point_G; // checked in crypto_constants
- const point_t c_point_H_minus_G = c_point_H - c_point_G; // checked in crypto_constants
-
- static_assert(sizeof(scalar_t::m_sk) == sizeof(scalar_t::m_u64) && sizeof(scalar_t::m_u64) == sizeof(scalar_t::m_s), "size missmatch");
} // namespace crypto
diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h
index 7ebe078e..643ca19b 100644
--- a/src/crypto/crypto-sugar.h
+++ b/src/crypto/crypto-sugar.h
@@ -1,5 +1,5 @@
-// Copyright (c) 2020-2022 Zano Project
-// Copyright (c) 2020-2022 sowle (val@zano.org, crypto.sowle@gmail.com)
+// Copyright (c) 2020-2023 Zano Project
+// Copyright (c) 2020-2023 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.
//
@@ -20,6 +20,34 @@ namespace crypto
// Helpers
//
+ // returns greatest k, s.t. n**k <= v
+ // tests in crypto_tests_range_proofs.h
+ constexpr uint64_t constexpr_floor_log_n(uint64_t v, uint64_t n)
+ {
+ return (v < n || n <= 1) ? 0 : constexpr_floor_log_n(v / n, n) + 1;
+ }
+
+ // returns smallest k, s.t. v <= n**k
+ // tests in crypto_tests_range_proofs.h
+ constexpr uint64_t constexpr_ceil_log_n(uint64_t v, uint64_t n)
+ {
+ return (v <= 1 || n <= 1) ? 0 : constexpr_floor_log_n(v - 1, n) + 1;
+ }
+
+ // returns smallest k, s.t. v <= 2**k
+ // tests in crypto_tests_range_proofs.h
+ constexpr uint64_t constexpr_ceil_log2(uint64_t v)
+ {
+ return constexpr_ceil_log_n(v, 2);
+ }
+
+ // returns base ** k
+ constexpr uint64_t constexpr_pow(uint64_t k, uint64_t base)
+ {
+ return k == 0 ? 1 : base * constexpr_pow(k - 1, base);
+ }
+
+
template
std::string pod_to_hex_reversed(const pod_t &h)
{
@@ -70,6 +98,22 @@ namespace crypto
return ss.str();
}
+ template
+ std::string pod_to_comma_separated_chars(const pod_t &h)
+ {
+ std::stringstream ss;
+ ss << std::hex << std::setfill('0');
+ size_t len = sizeof h;
+ const unsigned char* p = (const unsigned char*)&h;
+ for (size_t i = 0; i < len; ++i)
+ {
+ ss << "'\\x" << std::setw(2) << static_cast(p[i]) << "'";
+ if (i + 1 != len)
+ ss << ", ";
+ }
+ return ss.str();
+ }
+
template
std::string pod_to_hex_comma_separated_uint64(const pod_t &h)
{
@@ -87,6 +131,22 @@ namespace crypto
return ss.str();
}
+ template
+ std::string pod_to_comma_separated_int32(const pod_t &h)
+ {
+ static_assert((sizeof h) % 4 == 0, "size of h should be a multiple of 32 bit");
+ size_t len = (sizeof h) / 4;
+ std::stringstream ss;
+ const int32_t* p = (const int32_t*)&h;
+ for (size_t i = 0; i < len; ++i)
+ {
+ ss << static_cast(p[i]);
+ if (i + 1 != len)
+ ss << ", ";
+ }
+ return ss.str();
+ }
+
template
bool parse_tpod_from_hex_string(const std::string& hex_str, t_pod_type& t_pod)
{
@@ -144,12 +204,9 @@ namespace crypto
scalar_t() = default;
// won't check scalar range validity (< L)
- scalar_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3)
+ constexpr scalar_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) noexcept
+ : m_u64{a0, a1, a2, a3}
{
- m_u64[0] = a0;
- m_u64[1] = a1;
- m_u64[2] = a2;
- m_u64[3] = a3;
}
// won't check scalar range validity (< L)
@@ -309,6 +366,14 @@ namespace crypto
return *this;
}
+ scalar_t operator-() const
+ {
+ static unsigned char zero[32] = { 0 };
+ scalar_t result;
+ sc_sub(&result.m_s[0], zero, &m_s[0]);
+ return result;
+ }
+
// returns this = a * b
scalar_t& assign_mul(const scalar_t& a, const scalar_t& b)
{
@@ -319,7 +384,7 @@ namespace crypto
/*
I think it has bad symantic (operator-like), consider rename/reimplement -- sowle
*/
- // returns this * b + c
+ // returns c + this * b
scalar_t muladd(const scalar_t& b, const scalar_t& c) const
{
scalar_t result;
@@ -327,13 +392,20 @@ namespace crypto
return result;
}
- // returns this = a * b + c
+ // returns this = c + a * b
scalar_t& assign_muladd(const scalar_t& a, const scalar_t& b, const scalar_t& c)
{
sc_muladd(m_s, a.m_s, b.m_s, c.m_s);
return *this;
}
+ // returns this = c - a * b
+ scalar_t& assign_mulsub(const scalar_t& a, const scalar_t& b, const scalar_t& c)
+ {
+ sc_mulsub(m_s, a.m_s, b.m_s, c.m_s);
+ return *this;
+ }
+
scalar_t reciprocal() const
{
scalar_t result;
@@ -443,6 +515,7 @@ namespace crypto
m_u64[bit_index >> 6] &= ~(1ull << (bit_index & 63));
}
+ // does not reduce
static scalar_t power_of_2(uint8_t exponent)
{
scalar_t result = 0;
@@ -453,17 +526,20 @@ namespace crypto
}; // struct scalar_t
//
- // Global constants
+ // Global constants (checked in crypto_constants)
//
- extern const scalar_t c_scalar_1;
- extern const scalar_t c_scalar_2p64;
- extern const scalar_t c_scalar_L;
- extern const scalar_t c_scalar_Lm1;
- extern const scalar_t c_scalar_P;
- extern const scalar_t c_scalar_Pm1;
- extern const scalar_t c_scalar_256m1;
- extern const scalar_t c_scalar_1div8;
+ static constexpr scalar_t c_scalar_0 = { 0, 0, 0, 0 };
+ static constexpr scalar_t c_scalar_1 = { 1, 0, 0, 0 };
+ static constexpr scalar_t c_scalar_2p64 = { 0, 1, 0, 0 };
+ static constexpr scalar_t c_scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
+ static constexpr scalar_t c_scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
+ static constexpr scalar_t c_scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
+ static constexpr scalar_t c_scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
+ static constexpr scalar_t c_scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff };
+ static constexpr scalar_t c_scalar_1div8 = { 0x6106e529e2dc2f79, 0x07d39db37d1cdad0, 0x0, 0x0600000000000000 };
+
+ static_assert(sizeof(scalar_t::m_sk) == sizeof(scalar_t::m_u64) && sizeof(scalar_t::m_u64) == sizeof(scalar_t::m_s), "size missmatch");
//
//
@@ -476,9 +552,7 @@ namespace crypto
// with x = X / Z, y = Y / Z, x * y = T / Z.
ge_p3 m_p3;
- point_t()
- {
- }
+ point_t() = default;
explicit point_t(const crypto::public_key& pk) // can throw std::runtime_error
{
@@ -518,6 +592,16 @@ namespace crypto
{
}
+ explicit constexpr point_t(const int32_t(&v)[40]) noexcept
+ : m_p3{
+ {v[ 0], v[ 1], v[ 2], v[ 3], v[ 4], v[ 5], v[ 6], v[ 7], v[ 8], v[9]},
+ {v[10], v[11], v[12], v[13], v[14], v[15], v[16], v[17], v[18], v[19]},
+ {v[20], v[21], v[22], v[23], v[24], v[25], v[26], v[27], v[28], v[29]},
+ {v[30], v[31], v[32], v[33], v[34], v[35], v[36], v[37], v[38], v[39]}
+ }
+ {
+ }
+
// as we're using additive notation, zero means identity group element (EC point (0, 1)) here and after
void zero()
{
@@ -694,12 +778,32 @@ namespace crypto
return false;
return true;
- };
+ }
friend bool operator!=(const point_t& lhs, const point_t& rhs)
{
return !(lhs == rhs);
- };
+ }
+
+ friend bool operator==(const point_t& lhs, const public_key& rhs)
+ {
+ return lhs.to_public_key() == rhs;
+ }
+
+ friend bool operator!=(const point_t& lhs, const public_key& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ friend bool operator==(const public_key& lhs, const point_t& rhs)
+ {
+ return lhs == rhs.to_public_key();
+ }
+
+ friend bool operator!=(const public_key& lhs, const point_t& rhs)
+ {
+ return !(lhs == rhs);
+ }
friend std::ostream& operator<<(std::ostream& ss, const point_t &v)
{
@@ -731,6 +835,11 @@ namespace crypto
return pod_to_hex_comma_separated_uint64(pk);
}
+ std::string to_comma_separated_int32_str() const
+ {
+ return pod_to_comma_separated_int32(m_p3);
+ }
+
}; // struct point_t
@@ -739,10 +848,9 @@ namespace crypto
//
struct point_g_t : public point_t
{
- point_g_t()
+ explicit constexpr point_g_t(const int32_t(&v)[40]) noexcept
+ : point_t(v)
{
- scalar_t one(1);
- ge_scalarmult_base(&m_p3, &one.m_s[0]);
}
friend point_t operator*(const scalar_t& lhs, const point_g_t&)
@@ -886,6 +994,19 @@ namespace crypto
scalar_t calc_hs() const;
+ void make_random()
+ {
+ for(size_t size = this->size(), i = 0; i < size; ++i)
+ at(i).make_random();
+ }
+
+ void resize_and_make_random(size_t size)
+ {
+ this->resize(size);
+ make_random();
+ }
+
+
}; // scalar_vec_t
@@ -910,18 +1031,18 @@ namespace crypto
//
- // Global constants
+ // Global constants (checked in crypto_constants test)
//
- extern const point_g_t c_point_G;
-
- extern const point_t c_point_H;
- extern const point_t c_point_H2;
- extern const point_t c_point_X;
- extern const point_t c_point_0;
- extern const point_t c_point_H_plus_G;
- extern const point_t c_point_H_minus_G;
-
+ static constexpr point_t c_point_0 {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }};
+ static constexpr point_g_t c_point_G {{ 25485296, 5318399, 8791791, -8299916, -14349720, 6939349, -3324311, -7717049, 7287234, -6577708, -758052, -1832720, 13046421, -4857925, 6576754, 14371947, -13139572, 6845540, -2198883, -4003719, -947565, 6097708, -469190, 10704810, -8556274, -15589498, -16424464, -16608899, 14028613, -5004649, 6966464, -2456167, 7033433, 6781840, 28785542, 12262365, -2659449, 13959020, -21013759, -5262166 }};
+ static constexpr point_t c_point_H {{ 20574939, 16670001, -29137604, 14614582, 24883426, 3503293, 2667523, 420631, 2267646, -4769165, -11764015, -12206428, -14187565, -2328122, -16242653, -788308, -12595746, -8251557, -10110987, 853396, -4982135, 6035602, -21214320, 16156349, 977218, 2807645, 31002271, 5694305, -16054128, 5644146, -15047429, -568775, -22568195, -8089957, -27721961, -10101877, -29459620, -13359100, -31515170, -6994674 }};
+ static constexpr point_t c_point_H2 {{ 1318371, 14804112, 12545972, -13482561, -12089798, -16020744, -21221907, -8410994, -33080606, 11275578, 3807637, 11185450, -23227561, -12892068, 1356866, -1025012, -8022738, -8139671, -20315029, -13916324, -6475650, -7025596, 12403179, -5139984, -12068178, 10445584, -14826705, -4927780, 13964546, 12525942, -2314107, -10566315, 32243863, 15603849, 5154154, 4276633, -20918372, -15718796, -26386151, 8434696 }};
+ static constexpr point_t c_point_U {{ 30807552, 984924, 23426137, -5598760, 7545909, 16325843, 993742, 2594106, -31962071, -959867, 16454190, -4091093, 1197656, 13586872, -9269020, -14133290, 1869274, 13360979, -24627258, -10663086, 2212027, 1198856, 20515811, 15870563, -23833732, 9839517, -19416306, 11567295, -4212053, 348531, -2671541, 484270, -19128078, 1236698, -16002690, 9321345, 9776066, 10711838, 11187722, -16371275 }};
+ static constexpr point_t c_point_X {{ 25635916, -5459446, 5768861, 5666160, -6357364, -12939311, 29490001, -4543704, -31266450, -2582476, 23705213, 9562626, -716512, 16560168, 7947407, 2039790, -2752711, 4742449, 3356761, 16338966, 17303421, -5790717, -5684800, 12062431, -3307947, 8139265, -26544839, 12058874, 3452748, 3359034, 26514848, -6060876, 31255039, 11154418, -21741975, -3782423, -19871841, 5729859, 21754676, -12454027 }};
+ static constexpr point_t c_point_H_plus_G {{ 12291435, 3330843, -3390294, 13894858, -1099584, -6848191, 12040668, -15950068, -7494633, 12566672, -5526901, -16645799, -31081168, -1095427, -13082463, 4573480, -11255691, 4344628, 33477173, 11137213, -3837023, -12436594, -8471924, -814016, 10785607, 9492721, 10992667, 7406385, -5687296, -127915, -6229107, -9324867, 558657, 6493750, 4895261, 12642545, 9549220, 696086, 21894285, -10521807 }};
+ static constexpr point_t c_point_H_minus_G {{ -28347682, 3523701, -3380175, -14453727, 4238027, -6032522, 20235758, 4091609, 12557126, -8064113, 4212476, -13419094, -114185, -7650727, -24238, 16663404, 23676363, -6819610, 18286466, 8714527, -3837023, -12436594, -8471924, -814016, 10785607, 9492721, 10992667, 7406385, -5687296, -127915, -20450317, 13815641, -11604061, -447489, 27380225, 9400847, -8551293, -1173627, -28110171, 14241295 }};
+
//
// hash functions' helper
//
@@ -1199,6 +1320,13 @@ namespace crypto
return result;
}
+ static point_t hp(const std::string& str)
+ {
+ point_t result;
+ ge_bytes_hash_to_ec(&result.m_p3, str.data(), str.size());
+ return result;
+ }
+
}; // hash_helper_t struct
diff --git a/src/crypto/one_out_of_many_proofs.cpp b/src/crypto/one_out_of_many_proofs.cpp
new file mode 100644
index 00000000..6f6d8c9d
--- /dev/null
+++ b/src/crypto/one_out_of_many_proofs.cpp
@@ -0,0 +1,322 @@
+// Copyright (c) 2023 Zano Project
+// Copyright (c) 2023 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 "one_out_of_many_proofs.h"
+#include "../currency_core/crypto_config.h"
+#include "epee/include/misc_log_ex.h"
+
+//DISABLE_GCC_AND_CLANG_WARNING(unused-function)
+
+#if 0
+# define DBG_VAL_PRINT(x) std::cout << std::setw(30) << std::left << #x ": " << x << std::endl
+# define DBG_PRINT(x) std::cout << x << std::endl
+#else
+# define DBG_VAL_PRINT(x) (void(0))
+# define DBG_PRINT(x) (void(0))
+#endif
+
+namespace crypto
+{
+ static const size_t N_max = 256;
+ static const size_t mn_max = 16;
+
+ const point_t& get_BGE_generator(size_t index, bool& ok)
+ {
+ static std::vector precalculated_generators;
+ if (precalculated_generators.empty())
+ {
+ precalculated_generators.resize(mn_max * 2);
+
+ scalar_t hash_buf[2] = { hash_helper_t::hs("Zano BGE generator"), 0 };
+
+ for(size_t i = 0; i < precalculated_generators.size(); ++i)
+ {
+ hash_buf[1].m_u64[0] = i;
+ precalculated_generators[i] = hash_helper_t::hp(&hash_buf, sizeof hash_buf);
+ }
+ }
+
+ if (index >= mn_max * 2)
+ {
+ ok = false;
+ return c_point_0;
+ }
+
+ ok = true;
+ return precalculated_generators[index];
+ }
+
+
+
+
+#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
+ if (!(cond)) { LOG_PRINT_RED("generate_BGE_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
+ if (p_err) { *p_err = err_code; } return false; }
+
+ bool generate_BGE_proof(const hash& context_hash, const std::vector& ring, const scalar_t& secret, const size_t secret_index, BGE_proof& result, uint8_t* p_err /* = nullptr */)
+ {
+ static constexpr size_t n = 4; // TODO: @#@# move it out
+
+ DBG_PRINT(" - - - generate_BGE_proof - - -");
+ size_t ring_size = ring.size();
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(ring_size > 0, 0);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(secret_index < ring_size, 1);
+
+#ifndef NDEBUG
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(ring[secret_index] == secret * crypto::c_point_X, 2);
+#endif
+
+
+ const size_t m = std::max(static_cast(1), constexpr_ceil_log_n(ring_size, n));
+ const size_t N = constexpr_pow(m, n);
+ const size_t mn = m * n;
+
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(N <= N_max, 3);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(mn <= mn_max, 4);
+
+ scalar_mat_t a_mat(mn); // m x n matrix
+ a_mat.zero();
+ std::vector l_digits(m); // l => n-ary gidits
+ size_t l = secret_index;
+ for(size_t j = 0; j < m; ++j)
+ {
+ for(size_t i = n - 1; i != 0; --i) // [n - 1; 1]
+ {
+ a_mat(j, i).make_random();
+ a_mat(j, 0) -= a_mat(j, i); // a[j; 0] = -sum( a[j; i] ), i in [1; n-1]
+ }
+
+ size_t digit = l % n; // j-th digit of secret_index
+ l_digits[j] = digit;
+ l = l / n;
+ }
+
+#ifndef NDEBUG
+ for(size_t j = 0; j < m; ++j)
+ {
+ scalar_t a_sum{};
+ for(size_t i = 0; i != n; ++i)
+ a_sum += a_mat(j, i);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(a_sum.is_zero(), 230);
+ }
+#endif
+
+ //
+ // coeffs calculation (naive implementation, consider optimization in future)
+ //
+ scalar_vec_t coeffs(N * m); // m x N matrix
+ coeffs.zero();
+ for(size_t i = 0; i < N; ++i)
+ {
+ coeffs[i] = c_scalar_1; // first row is (1, ..., 1)
+ size_t i_tmp = i;
+ size_t m_bound = 1;
+ for(size_t j = 0; j < m; ++j)
+ {
+ size_t i_j = i_tmp % n; // j-th digit of i
+ i_tmp /= n;
+
+ if (i_j == l_digits[j]) // true if j-th digits of i and l matches
+ {
+ scalar_t carry{};
+ for(size_t k = 0; k < m_bound; ++k)
+ {
+ scalar_t old = coeffs[k * N + i];
+ coeffs[k * N + i] *= a_mat(j, i_j);
+ coeffs[k * N + i] += carry;
+ carry = old;
+ }
+ if (m_bound < m)
+ coeffs[m_bound * N + i] += carry;
+ ++m_bound;
+ }
+ else
+ {
+ for(size_t k = 0; k < m_bound; ++k)
+ coeffs[k * N + i] *= a_mat(j, i_j);
+ }
+ }
+ }
+
+ scalar_t r_A = scalar_t::random();
+ scalar_t r_B = scalar_t::random();
+ scalar_vec_t ro(m);
+ ro.make_random();
+
+ point_t A = c_point_0;
+ point_t B = c_point_0;
+
+ result.Pk.clear();
+
+ bool r = false, r2 = false;
+ for(size_t j = 0; j < m; ++j)
+ {
+ for(size_t i = 0; i < n; ++i)
+ {
+ const point_t& gen_1 = get_BGE_generator((j * n + i) * 2 + 0, r);
+ const point_t& gen_2 = get_BGE_generator((j * n + i) * 2 + 1, r2);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(r && r2, 5);
+ const scalar_t& a = a_mat(j, i);
+ A += a * gen_1 - a * a * gen_2;
+ if (l_digits[j] == i)
+ B += gen_1 - a * gen_2;
+ else
+ B += a * gen_2;
+ }
+
+ point_t Pk = c_point_0;
+ for(size_t i = 0; i < ring_size; ++i)
+ Pk += coeffs[j * N + i] * ring[i];
+ for(size_t i = ring_size; i < N; ++i)
+ Pk += coeffs[j * N + i] * ring[ring_size - 1];
+
+ Pk += ro[j] * c_point_X;
+ result.Pk.emplace_back(std::move((c_scalar_1div8 * Pk).to_public_key()));
+ }
+
+ A += r_A * c_point_X;
+ result.A = (c_scalar_1div8 * A).to_public_key();
+ B += r_B * c_point_X;
+ result.B = (c_scalar_1div8 * B).to_public_key();
+
+ hash_helper_t::hs_t hsc(1 + ring_size + 2 + m);
+ hsc.add_hash(context_hash);
+ for(auto& ring_el : ring)
+ hsc.add_point(c_scalar_1div8 * ring_el);
+ hsc.add_pub_key(result.A);
+ hsc.add_pub_key(result.B);
+ hsc.add_pub_keys_array(result.Pk);
+ scalar_t x = hsc.calc_hash();
+ DBG_VAL_PRINT(x);
+
+ result.f.resize(m * (n - 1));
+ for(size_t j = 0; j < m; ++j)
+ {
+ for(size_t i = 1; i < n; ++i)
+ {
+ result.f[j * (n - 1) + i - 1] = a_mat(j, i);
+ if (l_digits[j] == i)
+ result.f[j * (n - 1) + i - 1] += x;
+ }
+ }
+
+ result.y = r_A + x * r_B;
+
+ result.z = 0;
+ scalar_t x_power = c_scalar_1;
+ for(size_t k = 0; k < m; ++k)
+ {
+ result.z -= x_power * ro[k];
+ x_power *= x;
+ }
+ result.z += secret * x_power;
+
+ return true;
+ }
+#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
+
+ //---------------------------------------------------------------
+
+
+#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
+ if (!(cond)) { LOG_PRINT_RED("generate_BGE_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
+ if (p_err) { *p_err = err_code; } return false; }
+
+ bool verify_BGE_proof(const hash& context_hash, const std::vector& ring, const BGE_proof& sig, uint8_t* p_err /* = nullptr */)
+ {
+ static constexpr size_t n = 4; // TODO: @#@# move it out
+
+ DBG_PRINT(" - - - verify_BGE_proof - - -");
+ size_t ring_size = ring.size();
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(ring_size > 0, 0);
+
+ const size_t m = std::max(static_cast(1), constexpr_ceil_log_n(ring_size, n));
+ const size_t N = constexpr_pow(m, n);
+ const size_t mn = m * n;
+
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.Pk.size() == m, 1);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.f.size() == m * (n - 1), 2);
+
+ hash_helper_t::hs_t hsc(1 + ring_size + 2 + m);
+ hsc.add_hash(context_hash);
+ for(const public_key* ppk : ring)
+ hsc.add_pub_key(*ppk);
+ hsc.add_pub_key(sig.A);
+ hsc.add_pub_key(sig.B);
+ hsc.add_pub_keys_array(sig.Pk);
+ scalar_t x = hsc.calc_hash();
+ DBG_VAL_PRINT(x);
+
+ scalar_vec_t f0(m); // the first column f_{i,0} = x - sum{j=1}{n-1}( f_{i,j} )
+ for(size_t j = 0; j < m; ++j)
+ {
+ f0[j] = x;
+ for(size_t i = 1; i < n; ++i)
+ f0[j] -= sig.f[j * (n - 1) + i - 1];
+ }
+
+ //
+ // 1
+ //
+ point_t A = point_t(sig.A).modify_mul8();
+ point_t B = point_t(sig.B).modify_mul8();
+
+ point_t Z = A + x * B;
+
+ bool r = false, r2 = false;
+ for(size_t j = 0; j < m; ++j)
+ {
+ for(size_t i = 0; i < n; ++i)
+ {
+ const point_t& gen_1 = get_BGE_generator((j * n + i) * 2 + 0, r);
+ const point_t& gen_2 = get_BGE_generator((j * n + i) * 2 + 1, r2);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(r && r2, 5);
+ const scalar_t& f_ji = (i == 0) ? f0[j] : sig.f[j * (n - 1) + i - 1];
+
+ Z -= f_ji * gen_1 + f_ji * (x - f_ji) * gen_2;
+ }
+ }
+ Z -= sig.y * c_point_X;
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(Z.is_zero(), 100);
+
+ //
+ // 2
+ //
+ scalar_vec_t p_vec(N);
+ for(size_t i = 0; i < N; ++i)
+ {
+ p_vec[i] = c_scalar_1;
+ size_t i_tmp = i;
+ for(size_t j = 0; j < m; ++j)
+ {
+ size_t i_j = i_tmp % n; // j-th digit of i
+ i_tmp /= n;
+ const scalar_t& f_jij = (i_j == 0) ? f0[j] : sig.f[j * (n - 1) + i_j - 1];
+ p_vec[i] *= f_jij;
+ }
+ }
+
+ for(size_t i = 0; i < ring_size; ++i)
+ Z += p_vec[i] * point_t(*ring[i]).modify_mul8();
+ for(size_t i = ring_size; i < N; ++i)
+ Z += p_vec[i] * point_t(*ring[ring_size - 1]).modify_mul8();
+
+ scalar_t x_power = c_scalar_1;
+ for(size_t k = 0; k < m; ++k)
+ {
+ Z -= x_power * point_t(sig.Pk[k]).modify_mul8();
+ x_power *= x;
+ }
+
+ Z -= sig.z * c_point_X;
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(Z.is_zero(), 101);
+
+ return true;
+ }
+
+#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
+
+
+} // namespace crypto
diff --git a/src/crypto/one_out_of_many_proofs.h b/src/crypto/one_out_of_many_proofs.h
new file mode 100644
index 00000000..b52e8de8
--- /dev/null
+++ b/src/crypto/one_out_of_many_proofs.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2023 Zano Project
+// Copyright (c) 2023 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
+#include "crypto-sugar.h"
+
+namespace crypto
+{
+ //
+ // BGE stands for Bootle, Groth, Esgin
+ //
+ // This is a proof-of-concept implementation of a log-size one-out-of-many proof based on ideas and approaches by Bootle et al, Groth et al and Esgin et al
+ //
+ // https://eprint.iacr.org/2014/764
+ // https://eprint.iacr.org/2015/643
+ // https://eprint.iacr.org/2019/1287
+ //
+ // Disclaimer: shouldn't be used in production code until the security proofs and the code are peer-reviewed.
+ //
+
+ // m+2 group elements, m(n-1)+2 field elements
+ struct BGE_proof
+ {
+ public_key A; // premultiplied by 1/8
+ public_key B; // premultiplied by 1/8
+ std::vector Pk; // premultiplied by 1/8, size = m
+ scalar_vec_t f; // size = m * (n - 1)
+ scalar_t y;
+ scalar_t z;
+ };
+
+ bool generate_BGE_proof(const hash& context_hash, const std::vector& ring, const scalar_t& secret, const size_t secret_index, BGE_proof& result, uint8_t* p_err = nullptr);
+
+
+ bool verify_BGE_proof(const hash& context_hash, const std::vector& ring, const BGE_proof& sig, uint8_t* p_err = nullptr);
+
+
+} // namespace crypto
diff --git a/src/crypto/range_proof_bpp.h b/src/crypto/range_proof_bpp.h
index fb85def3..77dcb1a7 100644
--- a/src/crypto/range_proof_bpp.h
+++ b/src/crypto/range_proof_bpp.h
@@ -23,15 +23,21 @@ namespace crypto
scalar_t delta;
};
-#define DBG_VAL_PRINT(x) (void(0)) //*/ std::cout << #x ": " << x << ENDL
-#define DBG_PRINT(x) (void(0)) //*/ std::cout << x << ENDL
+#if 0
+# define DBG_VAL_PRINT(x) std::cout << std::setw(30) << std::left << #x ": " << x << std::endl
+# define DBG_PRINT(x) std::cout << x << std::endl
+#else
+# define DBG_VAL_PRINT(x) (void(0))
+# define DBG_PRINT(x) (void(0))
+#endif
+
#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 = " << (int)err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
- template>
+ template
bool bpp_gen(const scalar_vec_t& values, const scalar_vec_t& masks, const std::vector& commitments_1div8, bpp_signature& sig, uint8_t* p_err = nullptr)
{
// Note: commitments_1div8 are supposed to be already calculated
@@ -44,6 +50,15 @@ namespace crypto
const size_t c_bpp_mn = c_bpp_m * CT::c_bpp_n;
const size_t c_bpp_log2_mn = c_bpp_log2_m + CT::c_bpp_log2_n;
+#ifndef NDEBUG
+ for(size_t i = 0; i < values.size(); ++i)
+ {
+ point_t V{};
+ CT::calc_pedersen_commitment(values[i], masks[i], V);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(point_t(*commitments_1div8[i]).modify_mul8() == V, 4);
+ }
+#endif
+
// 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
@@ -322,7 +337,7 @@ namespace crypto
// convenient overload for tests
- template>
+ template
bool bpp_gen(const scalar_vec_t& values, const scalar_vec_t& masks, bpp_signature& sig, std::vector& commitments_1div8_to_be_generated, uint8_t* p_err = nullptr)
{
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(values.size() == masks.size(), 91);
@@ -351,7 +366,7 @@ namespace crypto
};
- template>
+ template
bool bpp_verify(const std::vector& sigs, uint8_t* p_err = nullptr)
{
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
diff --git a/src/crypto/range_proof_bppe.h b/src/crypto/range_proof_bppe.h
index 3b2158d0..260132b0 100644
--- a/src/crypto/range_proof_bppe.h
+++ b/src/crypto/range_proof_bppe.h
@@ -24,12 +24,17 @@ namespace crypto
scalar_t delta_2;
};
-#define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << ENDL
-#define DBG_PRINT(x) (void(0)) // std::cout << x << ENDL
+#if 0
+# define DBG_VAL_PRINT(x) std::cout << std::setw(30) << std::left << #x ": " << x << std::endl
+# define DBG_PRINT(x) std::cout << x << std::endl
+#else
+# define DBG_VAL_PRINT(x) (void(0))
+# define DBG_PRINT(x) (void(0))
+#endif
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
- if (!(cond)) { LOG_PRINT_RED("bppe_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
- if (p_err) { *p_err = err_code; } return false; }
+ if (!(cond)) { LOG_PRINT_RED("bppe_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
+ if (p_err) { *p_err = err_code; } return false; }
template
@@ -45,6 +50,15 @@ namespace crypto
const size_t c_bpp_mn = c_bpp_m * CT::c_bpp_n;
const size_t c_bpp_log2_mn = c_bpp_log2_m + CT::c_bpp_log2_n;
+#ifndef NDEBUG
+ for(size_t i = 0; i < values.size(); ++i)
+ {
+ point_t V{};
+ CT::calc_pedersen_commitment_2(values[i], masks[i], masks2[i], V);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(point_t(*commitments_1div8[i]).modify_mul8() == V, 4);
+ }
+#endif
+
// 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
diff --git a/src/crypto/range_proofs.cpp b/src/crypto/range_proofs.cpp
index 783a0c2b..fd14ec0f 100644
--- a/src/crypto/range_proofs.cpp
+++ b/src/crypto/range_proofs.cpp
@@ -6,4 +6,12 @@
namespace crypto
{
+ // TODO @#@# redesign needed, consider changing to inline constexpr
+ const point_t& bpp_ct_generators_HGX::bpp_G = c_point_H;
+ const point_t& bpp_ct_generators_HGX::bpp_H = c_point_G;
+ const point_t& bpp_ct_generators_HGX::bpp_H2 = c_point_X;
+
+ const point_t& bpp_ct_generators_UGX::bpp_G = c_point_U;
+ const point_t& bpp_ct_generators_UGX::bpp_H = c_point_G;
+ const point_t& bpp_ct_generators_UGX::bpp_H2 = c_point_X;
}
diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h
index 4e25ed79..95cbbc43 100644
--- a/src/crypto/range_proofs.h
+++ b/src/crypto/range_proofs.h
@@ -1,5 +1,5 @@
-// Copyright (c) 2021-2022 Zano Project (https://zano.org/)
-// Copyright (c) 2021-2022 sowle (val@zano.org, crypto.sowle@gmail.com)
+// Copyright (c) 2021-2023 Zano Project (https://zano.org/)
+// Copyright (c) 2021-2023 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
@@ -23,23 +23,6 @@ namespace crypto
return result;
}
-
- // returns greatest k, s.t. 2**k <= v
- // tests in crypto_tests_range_proofs.h
- constexpr size_t constexpr_floor_log2(size_t v)
- {
- return v <= 1 ? 0 : constexpr_floor_log2(v >> 1) + 1;
- }
-
- // returns smallest k, s.t. v <= 2**k
- // tests in crypto_tests_range_proofs.h
- constexpr size_t constexpr_ceil_log2(size_t v)
- {
- return v <= 1 ? 0 : constexpr_floor_log2(v - 1) + 1;
- }
-
-
-
// returns least significant bit uing de Bruijn sequence
// http://graphics.stanford.edu/~seander/bithacks.html
inline uint8_t calc_lsb_32(uint32_t v)
@@ -56,8 +39,25 @@ namespace crypto
////////////////////////////////////////
// crypto trait for Zano
////////////////////////////////////////
- template
- struct bpp_crypto_trait_zano
+ struct bpp_ct_generators_HGX
+ {
+ // NOTE! This notation follows the original BP+ whitepaper, see mapping to Zano's generators below
+ static const point_t& bpp_G;
+ static const point_t& bpp_H;
+ static const point_t& bpp_H2;
+ };
+
+ struct bpp_ct_generators_UGX
+ {
+ // NOTE! This notation follows the original BP+ whitepaper, see mapping to Zano's generators below
+ static const point_t& bpp_G;
+ static const point_t& bpp_H;
+ static const point_t& bpp_H2;
+ };
+
+
+ template
+ struct bpp_crypto_trait_zano : gen_trait_t
{
static constexpr size_t c_bpp_n = N; // the upper bound for the witness's range
static constexpr size_t c_bpp_values_max = values_max; // maximum number of elements in BP+ proof, i.e. max allowed BP+ outputs
@@ -66,12 +66,14 @@ namespace crypto
static void calc_pedersen_commitment(const scalar_t& value, const scalar_t& mask, point_t& commitment)
{
- commitment = value * bpp_G + mask * bpp_H;
+ // commitment = value * bpp_G + mask * bpp_H
+ commitment = operator*(value, bpp_G) + mask * bpp_H;
}
static void calc_pedersen_commitment_2(const scalar_t& value, const scalar_t& mask1, const scalar_t& mask2, point_t& commitment)
{
- commitment = value * bpp_G + mask1 * bpp_H + mask2 * bpp_H2;
+ // commitment = value * bpp_G + mask1 * bpp_H * mask2 * bpp_H2
+ commitment = operator*(value, bpp_G) + mask1 * bpp_H + mask2 * bpp_H2;
}
static const scalar_t& get_initial_transcript()
@@ -98,6 +100,7 @@ namespace crypto
}
// TODO: refactor with proper OOB handling
+ // TODO: @#@# add domain separation
static const point_t& get_generator(bool select_H, size_t index)
{
if (index >= c_bpp_mn_max)
@@ -125,19 +128,15 @@ namespace crypto
return result;
}
- static const point_t& bpp_G; // NOTE! This notation follows original BP+ whitepaper, see mapping to Zano's generators below
- static const point_t& bpp_H;
- static const point_t& bpp_H2;
+ using gen_trait_t::bpp_G;
+ using gen_trait_t::bpp_H;
+ using gen_trait_t::bpp_H2;
}; // struct bpp_crypto_trait_zano
- template
- const point_t& bpp_crypto_trait_zano::bpp_G = c_point_H;
- template
- const point_t& bpp_crypto_trait_zano::bpp_H = c_point_G;
+ typedef bpp_crypto_trait_zano bpp_crypto_trait_ZC_out;
- template
- const point_t& bpp_crypto_trait_zano::bpp_H2 = c_point_X;
+ typedef bpp_crypto_trait_zano bpp_crypto_trait_Zarcanum;
// efficient multiexponentiation (naive stub implementation atm, TODO)
diff --git a/src/crypto/zarcanum.cpp b/src/crypto/zarcanum.cpp
index 285ac505..043f4113 100644
--- a/src/crypto/zarcanum.cpp
+++ b/src/crypto/zarcanum.cpp
@@ -1,22 +1,35 @@
-// Copyright (c) 2022 Zano Project
-// Copyright (c) 2022 sowle (val@zano.org, crypto.sowle@gmail.com)
+// Copyright (c) 2022-2023 Zano Project
+// Copyright (c) 2022-2023 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.
//
// Note: This file originates from tests/functional_tests/crypto_tests.cpp
#include "epee/include/misc_log_ex.h"
#include "zarcanum.h"
-#include "crypto/range_proofs.h"
-#include "../currency_core/crypto_config.h" // TODO: move it to the crypto
+#include "range_proofs.h"
+#include "../currency_core/crypto_config.h" // TODO: move it to the crypto
+#include "../common/crypto_stream_operators.h" // TODO: move it to the crypto
+
+#if 0
+# define DBG_VAL_PRINT(x) std::cout << std::setw(30) << std::left << #x ": " << x << std::endl
+# define DBG_PRINT(x) std::cout << x << std::endl
+#else
+# define DBG_VAL_PRINT(x) (void(0))
+# define DBG_PRINT(x) (void(0))
+#endif
namespace crypto
{
const scalar_t c_zarcanum_z_coeff_s = { 0, 1, 0, 0 }; // c_scalar_2p64
const mp::uint256_t c_zarcanum_z_coeff_mp = c_zarcanum_z_coeff_s.as_boost_mp_type();
- #define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << std::endl
- #define DBG_PRINT(x) (void(0)) // std::cout << x << std::endl
-
+ template
+ inline std::ostream &operator <<(std::ostream &o, const std::vector &v)
+ {
+ for(size_t i = 0, n = v.size(); i < n; ++i)
+ o << ENDL << " [" << std::setw(2) << i << "]: " << v[i];
+ return o;
+ }
mp::uint256_t zarcanum_precalculate_l_div_z_D(const mp::uint128_t& pos_difficulty)
{
@@ -50,9 +63,9 @@ namespace crypto
if (!(cond)) { LOG_PRINT_RED("zarcanum_generate_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
- bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring,
+ bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
- const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
+ const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const crypto::scalar_t& stake_out_asset_id_blinding_mask, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
zarcanum_proof& result, uint8_t* p_err /* = nullptr */)
{
DBG_PRINT("zarcanum_generate_proof");
@@ -123,10 +136,10 @@ namespace crypto
const scalar_vec_t masks = { bf }; // G component
const scalar_vec_t masks2 = { bx }; // X component
const std::vector E_1div8_vec_ptr = { &result.E };
-
- CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_gen>(values, masks, masks2, E_1div8_vec_ptr, result.E_range_proof), 10);
- // = four-layers ring signature data outline =
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_gen(values, masks, masks2, E_1div8_vec_ptr, result.E_range_proof), 10);
+
+ // = five-layers ring signature data outline =
// (j in [0, ring_size-1])
// layer 0 ring
// A[j] ( = ring[j].stealth_address)
@@ -140,25 +153,35 @@ namespace crypto
// layer 1 secret (with respect to G)
// stake_blinding_mask - pseudo_out_blinding_mask
//
- // additional layers for Zarcanum:
+ // additional layer for confidential assets:
//
// layer 2 ring
- // C - A[j] - Q[j]
+ // ring[j].blinded_asset_id - pseudo_out_blinded_asset_id
// layer 2 secret (with respect to X)
- // x0
+ // -pseudo_out_asset_id_blinding_mask ( = -r'_i )
+ //
+ // additional layers for Zarcanum:
//
// layer 3 ring
+ // C - A[j] - Q[j]
+ // layer 3 secret (with respect to X)
+ // x0
+ //
+ // layer 4 ring
// Q[j]
- // layer 3 secret (with respect to G)
+ // layer 4 secret (with respect to G)
// secret_q
- point_t pseudo_out_amount_commitment = a * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G;
+ // such pseudo_out_asset_id_blinding_mask effectively makes pseudo_out_blinded_asset_id == currency::native_coin_asset_id_pt == crypto::point_H
+ scalar_t pseudo_out_asset_id_blinding_mask = -stake_out_asset_id_blinding_mask; // T^p_i = T_i + (-r_i) * X = H_i
+
+ point_t pseudo_out_amount_commitment = a * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G; // A^p_i = a_i * H_i + f'_i * G
result.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key();
TRY_ENTRY()
- CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(generate_CLSAG_GGXG(m, ring, pseudo_out_amount_commitment, C, stake_ki,
- secret_x, stake_blinding_mask - pseudo_out_blinding_mask, x0, secret_q, secret_index,
- result.clsag_ggxg), 20);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(generate_CLSAG_GGXXG(m, ring, pseudo_out_amount_commitment, crypto::c_point_H, C, stake_ki,
+ secret_x, stake_blinding_mask - pseudo_out_blinding_mask, -pseudo_out_asset_id_blinding_mask, x0, secret_q, secret_index,
+ result.clsag_ggxxg), 20);
CATCH_ENTRY2(false);
return true;
@@ -166,61 +189,68 @@ namespace crypto
#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
-
+
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
if (!(cond)) { LOG_PRINT_RED("zarcanum_verify_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
- bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector& ring,
+ bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
const mp::uint128_t& pos_difficulty,
- const zarcanum_proof& sig, uint8_t* p_err /* = nullptr */)
+ const zarcanum_proof& sig, uint8_t* p_err /* = nullptr */) noexcept
{
- DBG_PRINT("zarcanum_verify_proof");
- bool r = false;
+ TRY_ENTRY()
+ {
+ DBG_PRINT("zarcanum_verify_proof");
+ bool r = false;
- // make sure 0 < d <= l / floor(z * D)
- const mp::uint256_t l_div_z_D_mp = crypto::zarcanum_precalculate_l_div_z_D(pos_difficulty);
- const scalar_t l_div_z_D(l_div_z_D_mp);
- CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(!sig.d.is_zero() && sig.d < l_div_z_D, 2);
- const scalar_t dz = sig.d * c_zarcanum_z_coeff_s;
+ // make sure 0 < d <= l / floor(z * D)
+ const mp::uint256_t l_div_z_D_mp = crypto::zarcanum_precalculate_l_div_z_D(pos_difficulty);
+ const scalar_t l_div_z_D(l_div_z_D_mp);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(!sig.d.is_zero() && sig.d < l_div_z_D, 2);
+ const scalar_t dz = sig.d * c_zarcanum_z_coeff_s;
- // calculate h
- const scalar_t h = scalar_t(kernel_hash);
+ // calculate h
+ const scalar_t h = scalar_t(kernel_hash);
- // calculate F
- point_t C_prime = point_t(sig.C_prime);
- C_prime.modify_mul8();
- point_t C = point_t(sig.C);
- C.modify_mul8();
- point_t E = point_t(sig.E);
- E.modify_mul8();
- point_t F = h * C_prime - dz * C + E + last_pow_block_id_hashed * h * c_point_H;
+ // calculate F
+ point_t C_prime = point_t(sig.C_prime);
+ C_prime.modify_mul8();
+ point_t C = point_t(sig.C);
+ C.modify_mul8();
+ point_t E = point_t(sig.E);
+ E.modify_mul8();
+ point_t F = h * C_prime - dz * C + E + last_pow_block_id_hashed * h * c_point_H;
- DBG_VAL_PRINT(h); DBG_VAL_PRINT(last_pow_block_id_hashed); DBG_VAL_PRINT(dz);
- DBG_VAL_PRINT(C); DBG_VAL_PRINT(C_prime); DBG_VAL_PRINT(E); DBG_VAL_PRINT(F);
+ DBG_VAL_PRINT(h); DBG_VAL_PRINT(last_pow_block_id_hashed); DBG_VAL_PRINT(dz);
+ DBG_VAL_PRINT(C); DBG_VAL_PRINT(C_prime); DBG_VAL_PRINT(E); DBG_VAL_PRINT(F);
- // check three proofs with a shared Fiat-Shamir challenge c
- point_t C_plus_C_prime = C + C_prime;
- point_t C_minus_C_prime = C - C_prime;
- hash_helper_t::hs_t hash_calc(7);
- hash_calc.add_32_chars(CRYPTO_HDS_ZARCANUM_PROOF_HASH);
- hash_calc.add_point(sig.y0 * c_point_X + sig.y1 * c_point_H_plus_G - sig.c * C_plus_C_prime); // y_0 * X + y1 (H + G) - c (C + C')
- hash_calc.add_point(sig.y2 * c_point_X + sig.y3 * c_point_H_minus_G - sig.c * C_minus_C_prime); // y_2 * X + y3 (H - G) - c (C - C')
- hash_calc.add_point(sig.y4 * c_point_X - sig.c * F); // y_4 * X - c * F
- hash_calc.add_point(C_plus_C_prime);
- hash_calc.add_point(C_minus_C_prime);
- hash_calc.add_point(F);
- scalar_t c_prime = hash_calc.calc_hash();
- CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.c == c_prime, 3);
+ // check three proofs with a shared Fiat-Shamir challenge c
+ point_t C_plus_C_prime = C + C_prime;
+ point_t C_minus_C_prime = C - C_prime;
+ hash_helper_t::hs_t hash_calc(7);
+ hash_calc.add_32_chars(CRYPTO_HDS_ZARCANUM_PROOF_HASH);
+ hash_calc.add_point(sig.y0 * c_point_X + sig.y1 * c_point_H_plus_G - sig.c * C_plus_C_prime); // y_0 * X + y1 (H + G) - c (C + C')
+ hash_calc.add_point(sig.y2 * c_point_X + sig.y3 * c_point_H_minus_G - sig.c * C_minus_C_prime); // y_2 * X + y3 (H - G) - c (C - C')
+ hash_calc.add_point(sig.y4 * c_point_X - sig.c * F); // y_4 * X - c * F
+ hash_calc.add_point(C_plus_C_prime);
+ hash_calc.add_point(C_minus_C_prime);
+ hash_calc.add_point(F);
+ scalar_t c_prime = hash_calc.calc_hash();
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.c == c_prime, 3);
- // check extended range proof for E
- std::vector E_for_range_proof = { point_t(sig.E) }; // consider changing to 8*sig.E to avoid additional conversion
- std::vector range_proofs = { bppe_sig_commit_ref_t(sig.E_range_proof, E_for_range_proof) };
- CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_verify>(range_proofs), 10);
+ // check extended range proof for E
+ std::vector E_for_range_proof = { point_t(sig.E) }; // consider changing to 8*sig.E to avoid additional conversion
+ std::vector range_proofs = { bppe_sig_commit_ref_t(sig.E_range_proof, E_for_range_proof) };
- // check extended CLSAG-GGXG ring signature
- CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(verify_CLSAG_GGXG(m, ring, sig.pseudo_out_amount_commitment, sig.C, stake_ki, sig.clsag_ggxg), 1);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_verify(range_proofs), 10);
+
+ static public_key native_coin_asset_id = (crypto::c_scalar_1div8 * crypto::c_point_H).to_public_key(); // consider making it less ugly -- sowle
+
+ // check extended CLSAG-GGXG ring signature
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(verify_CLSAG_GGXXG(m, ring, sig.pseudo_out_amount_commitment, native_coin_asset_id, sig.C, stake_ki, sig.clsag_ggxxg), 1);
+ }
+ CATCH_ENTRY_CUSTOM2({if (p_err) *p_err = 100;}, false)
return true;
}
@@ -228,5 +258,149 @@ namespace crypto
#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
+#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
+ if (!(cond)) { LOG_PRINT_RED("generate_vector_UG_aggregation_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
+ if (p_err) { *p_err = err_code; } return false; }
+
+ bool generate_vector_UG_aggregation_proof(const hash& m, const scalar_vec_t& u_secrets, const scalar_vec_t& g_secrets0, const scalar_vec_t& g_secrets1,
+ const std::vector& amount_commitments,
+ const std::vector& amount_commitments_for_rp_aggregation,
+ const std::vector& blinded_asset_ids,
+ vector_UG_aggregation_proof& result, uint8_t* p_err /* = nullptr */)
+ {
+ // w - public random weighting factor
+ // proof of knowing e_j and y'' in zero knowledge in the following eq:
+ // E_j + w * E'_j = e_j * (T'_j + w * U) + (y_j + w * y'_j) * G
+ // where:
+ // e_j -- output's amount
+ // T'_j -- output's blinded asset tag
+ // E_j == e_j * T'_j + y_j * G -- output's amount commitments
+ // E'_j == e_j * U + y'_j * G -- additional commitment to the same amount for range proof aggregation
+
+ // amount_commitments[j] + w * amount_commitments_for_rp_aggregation[j]
+ // ==
+ // u_secrets[j] * (blinded_asset_ids[j] + w * U) + (g_secrets0[j] + w * g_secrets1[j]) * G
+
+ const size_t n = u_secrets.size();
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n != 0, 1);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n == g_secrets0.size(), 2);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n == g_secrets1.size(), 3);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n == amount_commitments.size(), 4);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n == amount_commitments_for_rp_aggregation.size(), 5);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n == blinded_asset_ids.size(), 6);
+
+ crypto::hash_helper_t::hs_t hash_calculator(1 + 3 * n);
+ hash_calculator.add_hash(m);
+ hash_calculator.add_points_array(amount_commitments);
+ hash_calculator.add_points_array(amount_commitments_for_rp_aggregation);
+ scalar_t w = hash_calculator.calc_hash(false); // don't clean the buffer
+ DBG_VAL_PRINT(w);
+
+#ifndef NDEBUG
+ for(size_t j = 0; j < n; ++j)
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(amount_commitments[j] + w * amount_commitments_for_rp_aggregation[j] == u_secrets[j] * (blinded_asset_ids[j] + w * crypto::c_point_U) + (g_secrets0[j] + w * g_secrets1[j]) * c_point_G, 20);
+#endif
+
+ result.amount_commitments_for_rp_aggregation.clear();
+ result.y0s.clear();
+ result.y1s.clear();
+
+ crypto::scalar_vec_t r0, r1;
+ r0.resize_and_make_random(n);
+ r1.resize_and_make_random(n);
+
+ std::vector asset_tag_plus_U_vec(n);
+ for(size_t j = 0; j < n; ++j)
+ asset_tag_plus_U_vec[j] = blinded_asset_ids[j] + w * crypto::c_point_U;
+
+ std::vector R(n);
+ for(size_t j = 0; j < n; ++j)
+ R[j].assign_mul_plus_G(r0[j], asset_tag_plus_U_vec[j], r1[j]); // R[j] = r0[j] * asset_tag_plus_U_vec[j] + r1[j] * G
+
+ hash_calculator.add_points_array(R);
+ result.c = hash_calculator.calc_hash();
+
+ DBG_VAL_PRINT(asset_tag_plus_U_vec); DBG_VAL_PRINT(m); DBG_VAL_PRINT(amount_commitments); DBG_VAL_PRINT(amount_commitments_for_rp_aggregation); DBG_VAL_PRINT(R);
+ DBG_VAL_PRINT(result.c);
+
+ for(size_t j = 0; j < n; ++j)
+ {
+ result.y0s.emplace_back(r0[j] - result.c * u_secrets[j]);
+ result.y1s.emplace_back(r1[j] - result.c * (g_secrets0[j] + w * g_secrets1[j]));
+ result.amount_commitments_for_rp_aggregation.emplace_back((crypto::c_scalar_1div8 * amount_commitments_for_rp_aggregation[j]).to_public_key());
+ }
+
+ return true;
+ }
+#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
+
+
+#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
+ if (!(cond)) { LOG_PRINT_RED("verify_vector_UG_aggregation_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
+ if (p_err) { *p_err = err_code; } return false; }
+
+ bool verify_vector_UG_aggregation_proof(const hash& m, const std::vector amount_commitments_1div8, const std::vector blinded_asset_ids_1div8,
+ const vector_UG_aggregation_proof& sig, uint8_t* p_err /* = nullptr */) noexcept
+ {
+ TRY_ENTRY()
+ {
+ const size_t n = amount_commitments_1div8.size();
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n > 0, 1);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(blinded_asset_ids_1div8.size() == n, 2);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.amount_commitments_for_rp_aggregation.size() == n, 3);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.y0s.size() == n, 4);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.y1s.size() == n, 5);
+
+ crypto::hash_helper_t::hs_t hash_calculator(1 + 3 * n);
+ hash_calculator.add_hash(m);
+ DBG_VAL_PRINT(m);
+
+ std::vector amount_commitments_pt;
+ for(size_t j = 0; j < n; ++j)
+ {
+ point_t A = crypto::point_t(*amount_commitments_1div8[j]).modify_mul8();
+ hash_calculator.add_point(A);
+ amount_commitments_pt.emplace_back(A);
+ DBG_VAL_PRINT(A);
+ }
+
+ std::vector amount_commitments_for_rp_aggregation_pt;
+ for(size_t j = 0; j < n; ++j)
+ {
+ point_t Arpa = crypto::point_t(sig.amount_commitments_for_rp_aggregation[j]).modify_mul8();
+ hash_calculator.add_point(Arpa); // TODO @#@ performance: consider adding premultiplied by 1/8 points to the hash
+ amount_commitments_for_rp_aggregation_pt.emplace_back(Arpa);
+ DBG_VAL_PRINT(Arpa);
+ }
+
+ scalar_t w = hash_calculator.calc_hash(false); // don't clear the buffer
+ DBG_VAL_PRINT(w);
+
+ std::vector asset_tag_plus_U_vec(n);
+ for(size_t j = 0; j < n; ++j)
+ asset_tag_plus_U_vec[j] = crypto::point_t(*blinded_asset_ids_1div8[j]).modify_mul8() + w * crypto::c_point_U;
+ DBG_VAL_PRINT(asset_tag_plus_U_vec);
+
+ for(size_t j = 0; j < n; ++j)
+ {
+ hash_calculator.add_pub_key(crypto::point_t(
+ sig.y0s[j] * asset_tag_plus_U_vec[j] +
+ sig.y1s[j] * crypto::c_point_G +
+ sig.c * (amount_commitments_pt[j] + w * amount_commitments_for_rp_aggregation_pt[j])
+ ).to_public_key());
+ DBG_VAL_PRINT(hash_calculator.m_elements.back().pk);
+ }
+
+ crypto::scalar_t c = hash_calculator.calc_hash();
+ DBG_VAL_PRINT(c); DBG_VAL_PRINT(sig.c);
+ CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.c == c, 0);
+ }
+ CATCH_ENTRY_CUSTOM2({if (p_err) *p_err = 100; }, false)
+
+ return true;
+ }
+#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
+
+
} // namespace crypto
diff --git a/src/crypto/zarcanum.h b/src/crypto/zarcanum.h
index 0562efd0..9f881c28 100644
--- a/src/crypto/zarcanum.h
+++ b/src/crypto/zarcanum.h
@@ -1,13 +1,13 @@
-// Copyright (c) 2022 Zano Project
-// Copyright (c) 2022 sowle (val@zano.org, crypto.sowle@gmail.com)
+// Copyright (c) 2022-2023 Zano Project
+// Copyright (c) 2022-2023 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.
//
// Note: This file originates from tests/functional_tests/crypto_tests.cpp
#pragma once
#include "crypto-sugar.h"
-#include "crypto/range_proofs.h"
-#include "crypto/clsag.h"
+#include "range_proofs.h"
+#include "clsag.h"
#include
namespace crypto
@@ -41,18 +41,149 @@ namespace crypto
bppe_signature E_range_proof;
crypto::public_key pseudo_out_amount_commitment; // premultiplied by 1/8
- CLSAG_GGXG_signature clsag_ggxg;
+ CLSAG_GGXXG_signature clsag_ggxxg;
};
- bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring,
+ bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
- const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
+ const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const crypto::scalar_t& stake_out_asset_id_blinding_mask, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
zarcanum_proof& result, uint8_t* p_err = nullptr);
- bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector& ring,
+ bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
const mp::uint128_t& pos_difficulty,
- const zarcanum_proof& sig, uint8_t* p_err = nullptr);
+ const zarcanum_proof& sig, uint8_t* p_err = nullptr) noexcept;
+
+
+ // TODO @#@#: make sure it is used, implement, then move it to an appropriate place
+ struct linear_composition_proof
+ {
+ scalar_t c;
+ scalar_t y0;
+ scalar_t y1;
+ };
+
+ enum generator_tag { gt_void = 0, gt_G = 1, gt_H = 2, gt_H2 = 3, gt_X = 4, gt_U = 5 };
+
+ template
+ bool generate_linear_composition_proof(const hash& m, const public_key& A, const scalar_t& secret_a, const scalar_t& secret_b, linear_composition_proof& result, uint8_t* p_err = nullptr)
+ {
+ // consider embedding generators' tags into random entropy to distinguish proofs made with different generators during verification
+ return false;
+ }
+
+ template
+ bool verify_linear_composition_proof(const hash& m, const public_key& A, const linear_composition_proof& sig, uint8_t* p_err = nullptr)
+ {
+ return false;
+ }
+
+
+ struct generic_schnorr_sig
+ {
+ scalar_t c;
+ scalar_t y;
+ };
+
+ template
+ inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result);
+
+ template<>
+ inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result)
+ {
+#ifndef NDEBUG
+ if (A != secret_a * c_point_G)
+ return false;
+#endif
+ scalar_t r = scalar_t::random();
+ point_t R = r * c_point_G;
+ hash_helper_t::hs_t hsc(3);
+ hsc.add_hash(m);
+ hsc.add_point(A);
+ hsc.add_point(R);
+ result.c = hsc.calc_hash();
+ result.y.assign_mulsub(result.c, secret_a, r); // y = r - c * secret_a
+ return true;
+ }
+
+ template<>
+ inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result)
+ {
+#ifndef NDEBUG
+ if (A != secret_a * c_point_X)
+ return false;
+#endif
+ scalar_t r = scalar_t::random();
+ point_t R = r * c_point_X;
+ hash_helper_t::hs_t hsc(3);
+ hsc.add_hash(m);
+ hsc.add_point(A);
+ hsc.add_point(R);
+ result.c = hsc.calc_hash();
+ result.y.assign_mulsub(result.c, secret_a, r); // y = r - c * secret_a
+ return true;
+ }
+
+ template
+ inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept;
+
+ template<>
+ inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept
+ {
+ try
+ {
+ if (!sig.c.is_reduced() || !sig.y.is_reduced())
+ return false;
+ hash_helper_t::hs_t hsc(3);
+ hsc.add_hash(m);
+ hsc.add_pub_key(A);
+ hsc.add_point(point_t(A).mul_plus_G(sig.c, sig.y)); // sig.y * G + sig.c * A
+ return sig.c == hsc.calc_hash();
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+ template<>
+ inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept
+ {
+ try
+ {
+ if (!sig.c.is_reduced() || !sig.y.is_reduced())
+ return false;
+ hash_helper_t::hs_t hsc(3);
+ hsc.add_hash(m);
+ hsc.add_pub_key(A);
+ hsc.add_point(sig.y * c_point_X + sig.c * point_t(A));
+ return sig.c == hsc.calc_hash();
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+
+ // TODO: improve this proof using random weightning factor
+ struct vector_UG_aggregation_proof
+ {
+ std::vector amount_commitments_for_rp_aggregation; // E' = e * U + y' * G, premultiplied by 1/8
+ scalar_vec_t y0s;
+ scalar_vec_t y1s;
+ scalar_t c; // common challenge
+ };
+
+ bool generate_vector_UG_aggregation_proof(const hash& m, const scalar_vec_t& u_secrets, const scalar_vec_t& g_secrets0, const scalar_vec_t& g_secrets1,
+ const std::vector& amount_commitments,
+ const std::vector& amount_commitments_for_rp_aggregation,
+ const std::vector& blinded_asset_ids,
+ vector_UG_aggregation_proof& result, uint8_t* p_err = nullptr);
+
+ bool verify_vector_UG_aggregation_proof(const hash& m, const std::vector amount_commitments_1div8, const std::vector blinded_asset_ids_1div8,
+ const vector_UG_aggregation_proof& sig, uint8_t* p_err = nullptr) noexcept;
+
} // namespace crypto
diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp
index 589f7d22..be011871 100644
--- a/src/currency_core/blockchain_storage.cpp
+++ b/src/currency_core/blockchain_storage.cpp
@@ -1322,21 +1322,16 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t
if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms
{
- if (pos)
- {
- CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 1, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2");
- CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)");
- }
- else
- {
- CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 2, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2");
- CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)");
- CHECK_AND_ASSERT_MES(b.miner_tx.attachment[1].type() == typeid(zc_balance_proof), false, "coinbase transaction wrong attachmenttype #1 (expected: zc_balance_proof)");
- }
+ CHECK_AND_ASSERT_MES(b.miner_tx.attachment.empty(), false, "coinbase transaction has attachments; attachments are not allowed for coinbase transactions.");
+ CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 3, false, "coinbase transaction has incorrect number of proofs (" << b.miner_tx.proofs.size() << "), expected 2");
+ CHECK_AND_ASSERT_MES(b.miner_tx.proofs[0].type() == typeid(zc_asset_surjection_proof), false, "coinbase transaction has incorrect type of proof #0 (expected: zc_asset_surjection_proof)");
+ CHECK_AND_ASSERT_MES(b.miner_tx.proofs[1].type() == typeid(zc_outs_range_proof), false, "coinbase transaction has incorrect type of proof #1 (expected: zc_outs_range_proof)");
+ CHECK_AND_ASSERT_MES(b.miner_tx.proofs[2].type() == typeid(zc_balance_proof), false, "coinbase transaction has incorrect type of proof #2 (expected: zc_balance_proof)");
}
else
{
CHECK_AND_ASSERT_MES(b.miner_tx.attachment.empty(), false, "coinbase transaction has attachments; attachments are not allowed for coinbase transactions.");
+ CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 0, false, "pre-HF4 coinbase shoudn't have non-empty proofs containter");
}
return true;
@@ -1359,9 +1354,12 @@ bool blockchain_storage::validate_miner_transaction(const block& b,
return false;
}
- if (!check_tx_balance(b.miner_tx, base_reward + fee))
+ uint64_t block_reward = base_reward + fee;
+
+ crypto::hash tx_id_for_post_hf4_era = b.miner_tx.version > TRANSACTION_VERSION_PRE_HF4 ? get_transaction_hash(b.miner_tx) : null_hash;
+ if (!check_tx_balance(b.miner_tx, tx_id_for_post_hf4_era, block_reward))
{
- LOG_ERROR("coinbase transaction balance check failed. Block reward is " << print_money_brief(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee)
+ LOG_ERROR("coinbase transaction balance check failed. Block reward is " << print_money_brief(block_reward) << "(" << print_money(base_reward) << "+" << print_money(fee)
<< ", blocks_size_median = " << blocks_size_median
<< ", cumulative_block_size = " << cumulative_block_size
<< ", fee = " << fee
@@ -1371,6 +1369,15 @@ bool blockchain_storage::validate_miner_transaction(const block& b,
return false;
}
+ if (b.miner_tx.version > TRANSACTION_VERSION_PRE_HF4)
+ {
+ if (!verify_asset_surjection_proof(b.miner_tx, tx_id_for_post_hf4_era))
+ {
+ LOG_ERROR("asset surjection proof verification failed for miner tx");
+ return false;
+ }
+ }
+
LOG_PRINT_MAGENTA("Mining tx verification ok, blocks_size_median = " << blocks_size_median, LOG_LEVEL_2);
return true;
}
@@ -1425,7 +1432,7 @@ bool blockchain_storage::create_block_template(const account_public_address& min
block& b,
wide_difficulty_type& diffic,
uint64_t& height,
- crypto::scalar_t* blinding_mask_sum_ptr /* = nullptr */) const
+ outputs_generation_context* miner_tx_ogc_ptr /* = nullptr */) const
{
create_block_template_params params = AUTO_VAL_INIT(params);
params.miner_address = miner_address;
@@ -1440,8 +1447,8 @@ bool blockchain_storage::create_block_template(const account_public_address& min
b = resp.b;
diffic = resp.diffic;
height = resp.height;
- if (blinding_mask_sum_ptr)
- *blinding_mask_sum_ptr = resp.blinding_mask_sum;
+ if (miner_tx_ogc_ptr)
+ *miner_tx_ogc_ptr = resp.miner_tx_ogc;
return r;
}
@@ -1526,7 +1533,7 @@ bool blockchain_storage::create_block_template(const create_block_template_param
CURRENCY_MINER_TX_MAX_OUTS,
pos,
pe,
- &resp.blinding_mask_sum);
+ &resp.miner_tx_ogc);
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
uint64_t coinbase_size = get_object_blobsize(b.miner_tx);
// "- 100" - to reserve room for PoS additions into miner tx
@@ -2570,6 +2577,7 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU
oen.stealth_address = toz.stealth_address;
oen.amount_commitment = toz.amount_commitment;
oen.concealing_point = toz.concealing_point;
+ oen.blinded_asset_id = toz.blinded_asset_id; // TODO @#@# bad design, too much manual coping, consider redesign -- sowle
}
VARIANT_SWITCH_END();
@@ -3544,15 +3552,14 @@ bool blockchain_storage::unprocess_blockchain_tx_extra(const transaction& tx)
if (ei.m_asset_operation.operation_type != ASSET_DESCRIPTOR_OPERATION_UNDEFINED)
{
- crypto::hash asset_id = currency::null_hash;
+ crypto::public_key asset_id = currency::null_pkey;
if (ei.m_asset_operation.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER)
{
- asset_id = get_asset_id_from_descriptor(ei.m_asset_operation.descriptor);
+ calculate_asset_id(ei.m_asset_operation.descriptor.owner, nullptr, &asset_id);
}
else
{
- CHECK_AND_ASSERT_MES(ei.m_asset_operation.asset_id.size() == 1, false, "Unexpected asset_id in operation");
- asset_id = ei.m_asset_operation.asset_id.back();
+ CHECK_AND_NO_ASSERT_MES(false, false, "asset operation not implemented");
}
r = pop_asset_info(asset_id);
CHECK_AND_ASSERT_MES(r, false, "failed to pop_alias_info");
@@ -3581,7 +3588,7 @@ uint64_t blockchain_storage::get_aliases_count() const
return m_db_aliases.size();
}
//------------------------------------------------------------------
-bool blockchain_storage::get_asset_info(const crypto::hash& asset_id, asset_descriptor_base& info)const
+bool blockchain_storage::get_asset_info(const crypto::public_key& asset_id, asset_descriptor_base& result) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
auto as_ptr = m_db_assets.find(asset_id);
@@ -3589,7 +3596,7 @@ bool blockchain_storage::get_asset_info(const crypto::hash& asset_id, asset_desc
{
if (as_ptr->size())
{
- info = as_ptr->back().descriptor;
+ result = as_ptr->back().descriptor;
return true;
}
}
@@ -3784,7 +3791,7 @@ bool blockchain_storage::put_alias_info(const transaction & tx, extra_alias_entr
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::pop_asset_info(const crypto::hash& asset_id)
+bool blockchain_storage::pop_asset_info(const crypto::public_key& asset_id)
{
CRITICAL_REGION_LOCAL(m_read_lock);
@@ -3802,14 +3809,16 @@ bool blockchain_storage::pop_asset_info(const crypto::hash& asset_id)
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::put_asset_info(const transaction & tx, asset_descriptor_operation & ado)
+bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado)
{
CRITICAL_REGION_LOCAL(m_read_lock);
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER)
{
- crypto::hash asset_id = get_asset_id_from_descriptor(ado.descriptor);
+ crypto::public_key asset_id{};
+ CHECK_AND_ASSERT_MES(validate_asset_operation(tx, tx_id, ado, asset_id), false, "asset operation validation failed!");
+
auto asset_history_ptr = m_db_assets.find(asset_id);
- CHECK_AND_ASSERT_MES(!asset_history_ptr, false, "Asset id already existing");
+ CHECK_AND_ASSERT_MES(!asset_history_ptr, false, "asset with id " << asset_id << " has already been registered");
assets_container::t_value_type local_asset_history = AUTO_VAL_INIT(local_asset_history);
local_asset_history.push_back(ado);
m_db_assets.set(asset_id, local_asset_history);
@@ -3820,7 +3829,7 @@ bool blockchain_storage::put_asset_info(const transaction & tx, asset_descriptor
else
{
//TODO: implement other operations
- CHECK_AND_ASSERT_THROW(false, "not implemented yet");
+ CHECK_AND_ASSERT_THROW(false, "asset operation not implemented yet");
}
return true;
@@ -3901,7 +3910,7 @@ bool blockchain_storage::prevalidate_alias_info(const transaction& tx, const ext
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx)
+bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx, const crypto::hash& tx_id)
{
//check transaction extra
tx_extra_info ei = AUTO_VAL_INIT(ei);
@@ -3917,7 +3926,7 @@ bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx)
}
if (ei.m_asset_operation.operation_type != ASSET_DESCRIPTOR_OPERATION_UNDEFINED)
{
- r = put_asset_info(tx, ei.m_asset_operation);
+ r = put_asset_info(tx, tx_id, ei.m_asset_operation);
CHECK_AND_ASSERT_MES(r, false, "failed to put_asset_info");
}
@@ -4095,7 +4104,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const
CHECK_AND_ASSERT_MES(validate_tx_for_hardfork_specific_terms(tx, tx_id, bl_height), false, "tx " << tx_id << ": hardfork-specific validation failed");
TIME_MEASURE_START_PD(tx_process_extra);
- bool r = process_blockchain_tx_extra(tx);
+ bool r = process_blockchain_tx_extra(tx, tx_id);
CHECK_AND_ASSERT_MES(r, false, "failed to process_blockchain_tx_extra");
TIME_MEASURE_FINISH_PD_COND(need_to_profile, tx_process_extra);
@@ -4416,6 +4425,34 @@ bool blockchain_storage::print_tx_outputs_lookup(const crypto::hash& tx_id)const
return true;
}
//------------------------------------------------------------------
+bool check_tx_explicit_asset_id_rules(const transaction& tx, bool all_tx_ins_have_explicit_asset_ids)
+{
+ if (tx.version <= TRANSACTION_VERSION_PRE_HF4)
+ return true;
+
+ // ( assuming that post-HF4 txs can only have tx_out_zarcanum outs )
+
+ bool r = false;
+ // if all tx inputs have explicit asset id AND it does not emit a new asset THEN all outputs must have explicit asset id (native coin)
+ if (all_tx_ins_have_explicit_asset_ids && !is_asset_emitting_transaction(tx))
+ {
+ for(size_t j = 0, k = tx.vout.size(); j < k; ++j)
+ {
+ r = crypto::point_t(boost::get(tx.vout[j]).blinded_asset_id).modify_mul8().to_public_key() == native_coin_asset_id;
+ CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has a non-explicit asset id");
+ }
+ }
+ else // otherwise all outputs must have hidden asset id
+ {
+ for(size_t j = 0, k = tx.vout.size(); j < k; ++j)
+ {
+ r = crypto::point_t(boost::get(tx.vout[j]).blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id;
+ CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has an explicit asset id");
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------
bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const
{
// check all tx's inputs for being already spent
@@ -4456,6 +4493,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha
{
size_t sig_index = 0;
max_used_block_height = 0;
+ bool all_tx_ins_have_explicit_asset_ids = true;
auto local_check_key_image = [&](const crypto::key_image& ki) -> bool
{
@@ -4517,7 +4555,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha
if (!local_check_key_image(in_zc.k_image))
return false;
- if (!check_tx_input(tx, sig_index, in_zc, tx_prefix_hash, max_used_block_height))
+ if (!check_tx_input(tx, sig_index, in_zc, tx_prefix_hash, max_used_block_height, all_tx_ins_have_explicit_asset_ids))
{
LOG_ERROR("Failed to validate zc input #" << sig_index << " in tx: " << tx_prefix_hash);
return false;
@@ -4539,6 +4577,8 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha
bool r = validate_attachment_info(tx.extra, tx.attachment, false);
CHECK_AND_ASSERT_MES(r, false, "Failed to validate attachments in tx " << tx_prefix_hash << ": incorrect extra_attachment_info in tx.extra");
}
+
+ CHECK_AND_ASSERT_MES(check_tx_explicit_asset_id_rules(tx, all_tx_ins_have_explicit_asset_ids), false, "tx does not comply with explicit asset id rules");
}
TIME_MEASURE_FINISH_PD(tx_check_inputs_attachment_check);
return true;
@@ -4920,7 +4960,8 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index,
return check_input_signature(tx, in_index, txin.amount, txin.k_image, txin.etc_details, tx_prefix_hash, output_keys_ptrs);
}
//------------------------------------------------------------------
-bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height) const
+bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash,
+ uint64_t& max_related_block_height, bool& all_tx_ins_have_explicit_asset_ids) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
@@ -4938,7 +4979,8 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index,
return false;
}
- // @#@
+ if (m_is_in_checkpoint_zone) // TODO @#@# reconsider placement of this check
+ return true;
// here we don't need to check zc_in.k_image validity because it is checked in verify_CLSAG_GG()
@@ -4947,10 +4989,14 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index,
// TODO: consider additional checks here
// build a ring of references
- vector ring;
+ vector ring;
ring.reserve(scan_contex.zc_outs.size());
for(auto& zc_out : scan_contex.zc_outs)
- ring.emplace_back(zc_out.stealth_address, zc_out.amount_commitment);
+ {
+ ring.emplace_back(zc_out.stealth_address, zc_out.amount_commitment, zc_out.blinded_asset_id);
+ if (all_tx_ins_have_explicit_asset_ids && crypto::point_t(zc_out.blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id)
+ all_tx_ins_have_explicit_asset_ids = false;
+ }
// calculate corresponding tx prefix hash
crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, in_index, tx_prefix_hash);
@@ -4958,12 +5004,12 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index,
const ZC_sig& sig = boost::get(tx.signatures[in_index]);
- //TIME_MEASURE_START_PD(tx_input_check_clsag_gg);
+ //TIME_MEASURE_START_PD(tx_input_check_clsag_ggx);
- bool r = crypto::verify_CLSAG_GG(tx_hash_for_signature, ring, sig.pseudo_out_amount_commitment, zc_in.k_image, sig.clsags_gg);
- CHECK_AND_ASSERT_MES(r, false, "verify_CLSAG_GG failed");
+ bool r = crypto::verify_CLSAG_GGX(tx_hash_for_signature, ring, sig.pseudo_out_amount_commitment, sig.pseudo_out_blinded_asset_id, zc_in.k_image, sig.clsags_ggx);
+ CHECK_AND_ASSERT_MES(r, false, "verify_CLSAG_GGX failed");
- //TIME_MEASURE_FINISH_PD(tx_input_check_clsag_gg);
+ //TIME_MEASURE_FINISH_PD(tx_input_check_clsag_ggx);
return true;
}
@@ -5350,6 +5396,7 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti
return false;
}
+ // TODO @#@# consider: 1) tx.proofs, 2) new proof data structures
if (var_is_after_hardfork_4_zone)
{
@@ -5481,10 +5528,10 @@ bool blockchain_storage::validate_pos_block(const block& b,
CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_block_height, false, "stake input refs' max related block height is " << max_related_block_height << " while last PoW block height is " << last_pow_block_height);
// build a ring of references
- vector ring;
+ vector ring;
ring.reserve(scan_contex.zc_outs.size());
for(auto& zc_out : scan_contex.zc_outs)
- ring.emplace_back(zc_out.stealth_address, zc_out.amount_commitment, zc_out.concealing_point);
+ ring.emplace_back(zc_out.stealth_address, zc_out.amount_commitment, zc_out.blinded_asset_id, zc_out.concealing_point);
crypto::scalar_t last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, sm.last_pow_id);
@@ -5703,35 +5750,47 @@ bool get_tx_from_cache(const crypto::hash& tx_id, transactions_map& tx_cache, tr
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::collect_rangeproofs_data_from_tx(std::vector& agregated_proofs, const transaction& tx)
+bool blockchain_storage::collect_rangeproofs_data_from_tx(const transaction& tx, const crypto::hash& tx_id, std::vector& agregated_proofs)
{
if (tx.version <= TRANSACTION_VERSION_PRE_HF4)
- {
return true;
- }
- //@#@ Verify somewhere(maybe here) that all outputs are covered with associated rangeproofs
- size_t proofs_count = 0;
- size_t current_output_start = 0; //for Consolidated Transactions we'll have multiple zc_outs_range_proof entries
- for (const auto& a : tx.attachment)
+ size_t range_proofs_count = 0;
+ size_t out_index_offset = 0; //Consolidated Transactions have multiple zc_outs_range_proof entries
+ for (const auto& a : tx.proofs)
{
if (a.type() == typeid(zc_outs_range_proof))
{
const zc_outs_range_proof& zcrp = boost::get(a);
- agregated_proofs.emplace_back(zcrp);
- for (uint8_t i = 0; i != zcrp.outputs_count; i++)
+
+ // validate aggregation proof
+ std::vector amount_commitment_ptrs_1div8, blinded_asset_id_ptrs_1div8;
+ for(size_t j = out_index_offset; j < tx.vout.size(); ++j)
{
- CHECK_AND_ASSERT_MES(tx.vout[i + current_output_start].type() == typeid(tx_out_zarcanum), false, "Unexpected type of out in collect_rangeproofs_data_from_tx()");
- const tx_out_zarcanum& zc_out = boost::get(tx.vout[i + current_output_start]);
- agregated_proofs.back().amount_commitments.emplace_back(zc_out.amount_commitment);
+ CHECKED_GET_SPECIFIC_VARIANT(tx.vout[j], const tx_out_zarcanum, zcout, false);
+ amount_commitment_ptrs_1div8.push_back(&zcout.amount_commitment);
+ blinded_asset_id_ptrs_1div8.push_back(&zcout.blinded_asset_id);
}
- current_output_start += zcrp.outputs_count;
- proofs_count++;
+ uint8_t err = 0;
+ bool r = crypto::verify_vector_UG_aggregation_proof(tx_id, amount_commitment_ptrs_1div8, blinded_asset_id_ptrs_1div8, zcrp.aggregation_proof, &err);
+ CHECK_AND_ASSERT_MES(r, false, "verify_vector_UG_aggregation_proof failed with err code " << (int)err);
+
+
+ agregated_proofs.emplace_back(zcrp);
+
+ // convert amount commitments for aggregation from public_key to point_t form
+ // TODO: consider refactoring this ugly code
+ for (uint8_t i = 0; i != zcrp.aggregation_proof.amount_commitments_for_rp_aggregation.size(); i++)
+ agregated_proofs.back().amount_commitments.emplace_back(zcrp.aggregation_proof.amount_commitments_for_rp_aggregation[i]);
+
+ out_index_offset += zcrp.aggregation_proof.amount_commitments_for_rp_aggregation.size();
+ range_proofs_count++;
}
}
- CHECK_AND_ASSERT_MES(proofs_count > 0, false, "transaction " << get_transaction_hash(tx) << " don't have range_proofs");
- CHECK_AND_ASSERT_MES(proofs_count == 1 || (get_tx_flags(tx) & TX_FLAG_SIGNATURE_MODE_SEPARATE), false, "transaction " << get_transaction_hash(tx)
- << " has TX_FLAG_SIGNATURE_MODE_SEPARATE but proofs_count = " << proofs_count);
+ CHECK_AND_ASSERT_MES(out_index_offset == tx.vout.size(), false, "range proof elements count doesn't match with outputs count: " << out_index_offset << " != " << tx.vout.size());
+ CHECK_AND_ASSERT_MES(range_proofs_count > 0, false, "transaction " << get_transaction_hash(tx) << " doesn't have range proofs");
+ CHECK_AND_ASSERT_MES(range_proofs_count == 1 || (get_tx_flags(tx) & TX_FLAG_SIGNATURE_MODE_SEPARATE), false, "transaction " << get_transaction_hash(tx)
+ << " doesn't have TX_FLAG_SIGNATURE_MODE_SEPARATE but has range_proofs_count = " << range_proofs_count);
return true;
}
@@ -5885,24 +5944,27 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
append_per_block_increments_for_tx(tx, gindices);
- //If we under checkpoints, ring signatures should be pruned
+ //If we under checkpoints, attachments, ring signatures and proofs should be pruned
if(m_is_in_checkpoint_zone)
{
- tx.signatures.clear();
tx.attachment.clear();
+ tx.signatures.clear();
+ tx.proofs.clear();
}
//std::vector tx_outs_commitments;
if (!m_is_in_checkpoint_zone)
{
- if (!collect_rangeproofs_data_from_tx(range_proofs_agregated, tx/*, tx_outs_commitments*/))
- {
- LOG_PRINT_L0("Block with id: " << id << " has at least one transaction with wrong proofs, tx_id: " << tx_id << ", collect_rangeproofs_data_from_tx failed");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- //add_block_as_invalid(bl, id);
- bvc.m_verification_failed = true;
- return false;
- }
+ auto cleanup = [&](){ purge_block_data_from_blockchain(bl, tx_processed_count); bvc.m_verification_failed = true; };
+
+ CHECK_AND_ASSERT_MES_CUSTOM(collect_rangeproofs_data_from_tx(tx, tx_id, range_proofs_agregated), false, cleanup(),
+ "block " << id << ", tx " << tx_id << ": collect_rangeproofs_data_from_tx failed");
+
+ CHECK_AND_ASSERT_MES_CUSTOM(check_tx_balance(tx, tx_id), false, cleanup(),
+ "block " << id << ", tx " << tx_id << ": check_tx_balance failed");
+
+ CHECK_AND_ASSERT_MES_CUSTOM(verify_asset_surjection_proof(tx, tx_id), false, cleanup(),
+ "block " << id << ", tx " << tx_id << ": verify_asset_surjection_proof failed");
}
TIME_MEASURE_START_PD(tx_add_one_tx_time);
@@ -5969,9 +6031,21 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
return false;
}
+ boost::multiprecision::uint128_t already_generated_coins = m_db_blocks.size() ? m_db_blocks.back()->already_generated_coins:0;
+ uint64_t base_reward = get_base_block_reward(is_pos_bl, already_generated_coins, height);
+
if (!m_is_in_checkpoint_zone)
{
- if (!collect_rangeproofs_data_from_tx(range_proofs_agregated, bl.miner_tx))
+ if (!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) // TODO @#@# base_reward will be calculated once again, consider refactoring
+ {
+ LOG_PRINT_L0("Block with id: " << id
+ << " have wrong miner transaction");
+ purge_block_data_from_blockchain(bl, tx_processed_count);
+ bvc.m_verification_failed = true;
+ return false;
+ }
+
+ if (!collect_rangeproofs_data_from_tx(bl.miner_tx, get_transaction_hash(bl.miner_tx), range_proofs_agregated))
{
LOG_PRINT_L0("Block with id: " << id
<< " have wrong miner tx, failed to collect_rangeproofs_data_from_tx()");
@@ -5979,28 +6053,16 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
bvc.m_verification_failed = true;
return false;
}
- }
- uint64_t base_reward = 0;
- boost::multiprecision::uint128_t already_generated_coins = m_db_blocks.size() ? m_db_blocks.back()->already_generated_coins:0;
- if (!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins))
- {
- LOG_PRINT_L0("Block with id: " << id
- << " have wrong miner transaction");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- bvc.m_verification_failed = true;
- return false;
- }
-
-
- //validate range proofs
- if (!verify_multiple_zc_outs_range_proofs(range_proofs_agregated))
- {
- LOG_PRINT_L0("Block with id: " << id
- << " have failed to verify multiple rangeproofs");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- bvc.m_verification_failed = true;
- return false;
+ //validate range proofs
+ if (!verify_multiple_zc_outs_range_proofs(range_proofs_agregated))
+ {
+ LOG_PRINT_L0("Block with id: " << id
+ << " have failed to verify multiple rangeproofs");
+ purge_block_data_from_blockchain(bl, tx_processed_count);
+ bvc.m_verification_failed = true;
+ return false;
+ }
}
@@ -7010,7 +7072,8 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
else
{
uint64_t max_related_block_height = 0;
- r = check_tx_input(input_tx, input_index, input_zc, input_tx_hash, max_related_block_height);
+ bool all_tx_ins_have_explicit_asset_ids = true; // stub for now, TODO @#@#
+ r = check_tx_input(input_tx, input_index, input_zc, input_tx_hash, max_related_block_height, all_tx_ins_have_explicit_asset_ids);
CHECK_AND_ASSERT_MES(r, false, "check_tx_input failed");
}
VARIANT_CASE_OTHER()
diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h
index 7537919d..d7d46185 100644
--- a/src/currency_core/blockchain_storage.h
+++ b/src/currency_core/blockchain_storage.h
@@ -261,7 +261,7 @@ namespace currency
bool create_block_template(const account_public_address& miner_address, const blobdata& ex_nonce, block& b, wide_difficulty_type& di, uint64_t& height) const;
- bool create_block_template(const account_public_address& miner_address, const account_public_address& stakeholder_address, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func, block& b, wide_difficulty_type& di, uint64_t& height, crypto::scalar_t* blinding_mask_sum_ptr = nullptr) const;
+ bool create_block_template(const account_public_address& miner_address, const account_public_address& stakeholder_address, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func, block& b, wide_difficulty_type& di, uint64_t& height, outputs_generation_context* miner_tx_ogc_ptr = nullptr) const;
bool create_block_template(const create_block_template_params& params, create_block_template_response& resp) const;
bool have_block(const crypto::hash& id) const;
@@ -288,12 +288,12 @@ namespace currency
uint64_t get_aliases_count()const;
uint64_t get_block_h_older_then(uint64_t timestamp) const;
bool validate_tx_service_attachmens_in_services(const tx_service_attachment& a, size_t i, const transaction& tx)const;
- bool get_asset_info(const crypto::hash& asset_id, asset_descriptor_base& info)const;
+ bool get_asset_info(const crypto::public_key& asset_id, asset_descriptor_base& info)const;
uint64_t get_assets_count() const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_htlc& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height)const;
- bool check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height) const;
+ bool check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height, bool& tx_has_explicit_asset_ids_in_all_ins) const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height)const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash) const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height, crypto::hash& max_used_block_id)const;
@@ -492,7 +492,7 @@ namespace currency
typedef tools::db::cached_key_value_accessor solo_options_container;
typedef tools::db::basic_key_value_accessor per_block_gindex_increments_container; // height => [(amount, gindex_increment), ...]
- typedef tools::db::cached_key_value_accessor, true, false> assets_container;
+ typedef tools::db::cached_key_value_accessor, true, false> assets_container; // TODO @#@# consider storing tx_id as well for reference -- sowle
//-----------------------------------------
@@ -593,7 +593,7 @@ namespace currency
wide_difficulty_type get_next_difficulty_for_alternative_chain(const alt_chain_type& alt_chain, block_extended_info& bei, bool pos) const;
bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc);
bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc);
- bool collect_rangeproofs_data_from_tx(std::vector& agregated_proofs, const transaction& tx);
+ bool collect_rangeproofs_data_from_tx(const transaction& tx, const crypto::hash& tx_id, std::vector& agregated_proofs);
std::string print_alt_chain(alt_chain_type alt_chain);
bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
bool is_reorganize_required(const block_extended_info& main_chain_bei, const alt_chain_type& alt_chain, const crypto::hash& proof_alt);
@@ -648,14 +648,14 @@ namespace currency
uint64_t get_adjusted_time()const;
bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps);
bool update_next_comulative_size_limit();
- bool process_blockchain_tx_extra(const transaction& tx);
+ bool process_blockchain_tx_extra(const transaction& tx, const crypto::hash& tx_id);
bool unprocess_blockchain_tx_extra(const transaction& tx);
bool process_blockchain_tx_attachments(const transaction& tx, uint64_t h, const crypto::hash& bl_id, uint64_t timestamp);
bool unprocess_blockchain_tx_attachments(const transaction& tx, uint64_t h, uint64_t timestamp);
bool pop_alias_info(const extra_alias_entry& ai);
bool put_alias_info(const transaction& tx, extra_alias_entry& ai);
- bool pop_asset_info(const crypto::hash& asset_id);
- bool put_asset_info(const transaction & tx, asset_descriptor_operation & ado);
+ bool pop_asset_info(const crypto::public_key& asset_id);
+ bool put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado);
void fill_addr_to_alias_dict();
//bool resync_spent_tx_flags();
bool prune_ring_signatures_and_attachments_if_need();
diff --git a/src/currency_core/blockchain_storage_basic.h b/src/currency_core/blockchain_storage_basic.h
index c0a1a181..72170005 100644
--- a/src/currency_core/blockchain_storage_basic.h
+++ b/src/currency_core/blockchain_storage_basic.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018 Zano Project
+// Copyright (c) 2014-2023 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Copyright (c) 2012-2013 The Boolberry developers
@@ -19,6 +19,8 @@
#include "currency_basic.h"
#include "difficulty.h"
#include "currency_protocol/blobdatatype.h"
+#include "currency_format_utils_transactions.h" // only for output_generation_context
+
namespace currency
{
@@ -144,7 +146,7 @@ namespace currency
block b;
wide_difficulty_type diffic;
uint64_t height;
- crypto::scalar_t blinding_mask_sum; // sum of all the outputs' blinding masks
+ outputs_generation_context miner_tx_ogc; // bad design, a lot of copying, consider redesign -- sowle
};
typedef std::unordered_map transactions_map;
@@ -157,4 +159,4 @@ namespace currency
-}
\ No newline at end of file
+} // namespace currency
diff --git a/src/currency_core/crypto_config.h b/src/currency_core/crypto_config.h
index 087c1cfd..80a92224 100644
--- a/src/currency_core/crypto_config.h
+++ b/src/currency_core/crypto_config.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 Zano Project
+// Copyright (c) 2022-2023 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
@@ -8,21 +8,35 @@
//
#define CRYPTO_HDS_OUT_AMOUNT_MASK "ZANO_HDS_OUT_AMOUNT_MASK_______"
-#define CRYPTO_HDS_OUT_BLINDING_MASK "ZANO_HDS_OUT_BLINDING_MASK_____"
+#define CRYPTO_HDS_OUT_AMOUNT_BLINDING_MASK "ZANO_HDS_OUT_AMOUNT_BLIND_MASK_"
+#define CRYPTO_HDS_OUT_ASSET_BLINDING_MASK "ZANO_HDS_OUT_ASSET_BLIND_MASK__"
#define CRYPTO_HDS_OUT_CONCEALING_POINT "ZANO_HDS_OUT_CONCEALING_POINT__"
#define CRYPTO_HDS_CLSAG_GG_LAYER_0 "ZANO_HDS_CLSAG_GG_LAYER_ZERO___"
#define CRYPTO_HDS_CLSAG_GG_LAYER_1 "ZANO_HDS_CLSAG_GG_LAYER_ONE____"
#define CRYPTO_HDS_CLSAG_GG_CHALLENGE "ZANO_HDS_CLSAG_GG_CHALLENGE____"
+#define CRYPTO_HDS_CLSAG_GGX_LAYER_0 "ZANO_HDS_CLSAG_GGX_LAYER_ZERO__"
+#define CRYPTO_HDS_CLSAG_GGX_LAYER_1 "ZANO_HDS_CLSAG_GGX_LAYER_ONE___"
+#define CRYPTO_HDS_CLSAG_GGX_LAYER_2 "ZANO_HDS_CLSAG_GGX_LAYER_TWO___"
+#define CRYPTO_HDS_CLSAG_GGX_CHALLENGE "ZANO_HDS_CLSAG_GGX_CHALLENGE___"
+
#define CRYPTO_HDS_CLSAG_GGXG_LAYER_0 "ZANO_HDS_CLSAG_GGXG_LAYER_ZERO_"
#define CRYPTO_HDS_CLSAG_GGXG_LAYER_1 "ZANO_HDS_CLSAG_GGXG_LAYER_ONE__"
#define CRYPTO_HDS_CLSAG_GGXG_LAYER_2 "ZANO_HDS_CLSAG_GGXG_LAYER_TWO__"
#define CRYPTO_HDS_CLSAG_GGXG_LAYER_3 "ZANO_HDS_CLSAG_GGXG_LAYER_THREE"
#define CRYPTO_HDS_CLSAG_GGXG_CHALLENGE "ZANO_HDS_CLSAG_GGXG_CHALLENGE__"
+#define CRYPTO_HDS_CLSAG_GGXXG_LAYER_0 "ZANO_HDS_CLSAG_GGXXG_LAYER_ZERO"
+#define CRYPTO_HDS_CLSAG_GGXXG_LAYER_1 "ZANO_HDS_CLSAG_GGXXG_LAYER_ONE_"
+#define CRYPTO_HDS_CLSAG_GGXXG_LAYER_2 "ZANO_HDS_CLSAG_GGXXG_LAYER_TWO_"
+#define CRYPTO_HDS_CLSAG_GGXXG_LAYER_3 "ZANO_HDS_CLSAG_GGXXG_LAYER_3___"
+#define CRYPTO_HDS_CLSAG_GGXXG_LAYER_4 "ZANO_HDS_CLSAG_GGXXG_LAYER_FOUR"
+#define CRYPTO_HDS_CLSAG_GGXXG_CHALLENGE "ZANO_HDS_CLSAG_GGXXG_CHALLENGE_"
+
#define CRYPTO_HDS_ZARCANUM_LAST_POW_HASH "ZANO_HDS_ZARCANUM_LAST_POW_HASH"
#define CRYPTO_HDS_ZARCANUM_PROOF_HASH "ZANO_HDS_ZARCANUM_PROOF_HASH___"
#define CRYPTO_HDS_ASSET_CONTROL_KEY "ZANO_HDS_ASSET_CONTROL_KEY_____"
+#define CRYPTO_HDS_ASSET_CONTROL_ABM "ZANO_HDS_ASSET_CONTROL_ABM_____"
#define CRYPTO_HDS_ASSET_ID "ZANO_HDS_ASSET_ID______________"
diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h
index d79c5122..e64155c0 100644
--- a/src/currency_core/currency_basic.h
+++ b/src/currency_core/currency_basic.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2019 Zano Project
+// Copyright (c) 2014-2023 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Copyright (c) 2014-2015 The Boolberry developers
@@ -30,6 +30,7 @@
#include "serialization/stl_containers.h"
#include "serialization/serialization.h"
#include "serialization/variant.h"
+#include "serialization/boost_types.h"
#include "serialization/json_archive.h"
#include "serialization/debug_archive.h"
#include "serialization/keyvalue_serialization.h" // epee key-value serialization
@@ -56,6 +57,12 @@ namespace currency
const static crypto::hash gdefault_genesis = epee::string_tools::hex_to_pod("CC608F59F8080E2FBFE3C8C80EB6E6A953D47CF2D6AEBD345BADA3A1CAB99852");
const static crypto::hash ffff_hash = epee::string_tools::hex_to_pod("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
+ const static crypto::public_key ffff_pkey = epee::string_tools::hex_to_pod("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // TODO @#@# consider getting rid of this
+
+ // TODO: rewtire the following to a normal aggregate initialization once we move to C++17 -- sowle
+ const static crypto::public_key native_coin_asset_id = reinterpret_cast(static_cast(crypto::ec_scalar{'\xd6', '\x32', '\x9b', '\x5b', '\x1f', '\x7c', '\x08', '\x05', '\xb5', '\xc3', '\x45', '\xf4', '\x95', '\x75', '\x54', '\x00', '\x2a', '\x2f', '\x55', '\x78', '\x45', '\xf6', '\x4d', '\x76', '\x45', '\xda', '\xe0', '\xe0', '\x51', '\xa6', '\x49', '\x8a'})); // == crypto::c_point_H, checked in crypto_constants
+ const static crypto::public_key native_coin_asset_id_1div8 = reinterpret_cast(static_cast(crypto::ec_scalar{'\x74', '\xc3', '\x2d', '\x3e', '\xaa', '\xfa', '\xfc', '\x62', '\x3b', '\xf4', '\x83', '\xe8', '\x58', '\xd4', '\x2e', '\x8b', '\xf4', '\xec', '\x7d', '\xf0', '\x64', '\xad', '\xa2', '\xe3', '\x49', '\x34', '\x46', '\x9c', '\xff', '\x6b', '\x62', '\x68'})); // == 1/8 * crypto::c_point_H, checked in crypto_constants
+ const static crypto::point_t native_coin_asset_id_pt = crypto::c_point_H;
const static wide_difficulty_type global_difficulty_pow_starter = DIFFICULTY_POW_STARTER;
const static wide_difficulty_type global_difficulty_pos_starter = DIFFICULTY_POS_STARTER;
@@ -214,22 +221,8 @@ namespace currency
uint32_t n_extras;
};
- //!!!!this is temporary struct!!!
- //needed only to hold asset_id of input/output while zarcanum extension being developed
- struct open_asset_id
- {
- crypto::hash asset_id;
- BEGIN_SERIALIZE_OBJECT()
- FIELD(asset_id) // referring_input
- END_SERIALIZE()
- BEGIN_BOOST_SERIALIZATION()
- BOOST_SERIALIZE(asset_id)
- END_BOOST_SERIALIZATION()
- };
-
-
- typedef boost::variant txin_etc_details_v;
+ typedef boost::variant txin_etc_details_v;
struct referring_input
@@ -394,8 +387,6 @@ namespace currency
END_BOOST_SERIALIZATION()
};
- typedef boost::variant txout_etc_details_v;
-
struct tx_out_zarcanum
{
tx_out_zarcanum() {}
@@ -407,27 +398,26 @@ namespace currency
crypto::public_key stealth_address;
crypto::public_key concealing_point; // group element Q, see also Zarcanum paper, premultiplied by 1/8
crypto::public_key amount_commitment; // premultiplied by 1/8
- uint64_t encrypted_amount;
- uint8_t mix_attr;
- std::vector etc_details;
- //crypto::public_key token_masked_generator;
+ crypto::public_key blinded_asset_id; // group element T, premultiplied by 1/8
+ uint64_t encrypted_amount = 0;
+ uint8_t mix_attr = 0;
BEGIN_SERIALIZE_OBJECT()
FIELD(stealth_address)
FIELD(concealing_point)
FIELD(amount_commitment)
+ FIELD(blinded_asset_id)
FIELD(encrypted_amount)
FIELD(mix_attr)
- FIELD(etc_details)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(stealth_address)
BOOST_SERIALIZE(concealing_point)
BOOST_SERIALIZE(amount_commitment)
+ BOOST_SERIALIZE(blinded_asset_id)
BOOST_SERIALIZE(encrypted_amount)
BOOST_SERIALIZE(mix_attr)
- BOOST_SERIALIZE(etc_details)
END_BOOST_SERIALIZATION()
};
@@ -444,51 +434,69 @@ namespace currency
END_BOOST_SERIALIZATION()
};
- // non-consoditated txs must have one of this objects in the attachments (outputs_count == vout.size())
- // consolidated -- one pre consolidated part (sum(outputs_count) == vout.size())
+ struct zc_asset_surjection_proof
+ {
+ std::vector bge_proofs; // one per output, non-aggregated version of Groth-Bootle-Esgin yet, need to be upgraded later -- sowle
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(bge_proofs)
+ END_SERIALIZE()
+
+ BEGIN_BOOST_SERIALIZATION()
+ BOOST_SERIALIZE(bge_proofs)
+ END_BOOST_SERIALIZATION()
+ };
+
+ // non-consoditated txs must have one of this objects in the attachments (elements count == vout.size())
+ // consolidated -- one pre consolidated part (sum(elements count) == vout.size())
struct zc_outs_range_proof
{
- crypto::bpp_signature_serialized bpp;
- uint8_t outputs_count = 0; // how many outputs are included in the proof
+ crypto::bpp_signature_serialized bpp; // for commitments in form: amount * U + mask * G
+ crypto::vector_UG_aggregation_proof_serialized aggregation_proof; // E'_j = e_j * U + y'_j * G + vector Shnorr
BEGIN_SERIALIZE_OBJECT()
FIELD(bpp)
- FIELD(outputs_count)
+ FIELD(aggregation_proof)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(bpp)
- BOOST_SERIALIZE(outputs_count)
+ BOOST_SERIALIZE(aggregation_proof)
END_BOOST_SERIALIZATION()
};
- // Zarcanum-aware CLSAG signature
+ // Zarcanum-aware CLSAG signature (one per ZC input)
struct ZC_sig
{
crypto::public_key pseudo_out_amount_commitment = null_pkey; // premultiplied by 1/8
- crypto::CLSAG_GG_signature_serialized clsags_gg;
+ crypto::public_key pseudo_out_blinded_asset_id = null_pkey; // premultiplied by 1/8
+ crypto::CLSAG_GGX_signature_serialized clsags_ggx;
BEGIN_SERIALIZE_OBJECT()
FIELD(pseudo_out_amount_commitment)
- FIELD(clsags_gg)
+ FIELD(pseudo_out_blinded_asset_id)
+ FIELD(clsags_ggx)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(pseudo_out_amount_commitment)
- BOOST_SERIALIZE(clsags_gg)
+ BOOST_SERIALIZE(pseudo_out_blinded_asset_id)
+ BOOST_SERIALIZE(clsags_ggx)
END_BOOST_SERIALIZATION()
};
+ // 1) for txs without ZC inputs: proves that balance point = lin(G) (cancels out G component of outputs' amount commitments, asset tags assumed to be H (native coin) and non-blinded)
+ // 2) for txs with ZC inputs: proves that balance point = lin(X) (cancels out X component of blinded asset tags within amount commitments for both outputs and inputs (pseudo outs))
struct zc_balance_proof
{
- crypto::signature s = null_sig;
+ crypto::generic_schnorr_sig_s ss;
BEGIN_SERIALIZE_OBJECT()
- FIELD(s)
+ FIELD(ss)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
- BOOST_SERIALIZE(s)
+ BOOST_SERIALIZE(ss)
END_BOOST_SERIALIZATION()
};
@@ -506,9 +514,9 @@ namespace currency
FIELD(y2);
FIELD(y3);
FIELD(y4);
- FIELD((crypto::bppe_signature_serialized&)E_range_proof);
+ FIELD_N("E_range_proof", (crypto::bppe_signature_serialized&)E_range_proof);
FIELD(pseudo_out_amount_commitment);
- FIELD((crypto::CLSAG_GGXG_signature_serialized&)clsag_ggxg);
+ FIELD_N("clsag_ggxxg", (crypto::CLSAG_GGXXG_signature_serialized&)clsag_ggxxg);
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
@@ -524,7 +532,7 @@ namespace currency
BOOST_SERIALIZE(y4);
BOOST_SERIALIZE((crypto::bppe_signature_serialized&)E_range_proof);
BOOST_SERIALIZE(pseudo_out_amount_commitment);
- BOOST_SERIALIZE((crypto::CLSAG_GGXG_signature_serialized&)clsag_ggxg);
+ BOOST_SERIALIZE((crypto::CLSAG_GGXXG_signature_serialized&)clsag_ggxxg);
END_BOOST_SERIALIZATION()
};
@@ -740,7 +748,8 @@ namespace currency
std::string ticker;
std::string full_name;
std::string meta_info;
- crypto::public_key owner = currency::null_pkey;
+ crypto::public_key owner = currency::null_pkey; // consider premultipling by 1/8
+ bool hidden_supply = false;
BEGIN_VERSIONED_SERIALIZE()
FIELD(total_max_supply)
@@ -750,6 +759,7 @@ namespace currency
FIELD(full_name)
FIELD(meta_info)
FIELD(owner)
+ FIELD(hidden_supply)
END_SERIALIZE()
@@ -777,7 +787,7 @@ namespace currency
struct asset_descriptor_with_id: public asset_descriptor_base
{
- crypto::hash asset_id = currency::null_hash;
+ crypto::public_key asset_id = currency::null_pkey;
/*
BEGIN_VERSIONED_SERIALIZE()
@@ -797,28 +807,42 @@ namespace currency
#define ASSET_DESCRIPTOR_OPERATION_REGISTER 1
#define ASSET_DESCRIPTOR_OPERATION_EMMIT 2
#define ASSET_DESCRIPTOR_OPERATION_UPDATE 3
+#define ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN 4
struct asset_descriptor_operation
{
uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED;
- std::vector proof;
asset_descriptor_base descriptor;
- std::vector asset_id; //questionable regarding form of optional fields
-
+ boost::optional opt_amount_commitment; // premultiplied by 1/8
BEGIN_VERSIONED_SERIALIZE()
FIELD(operation_type)
- FIELD(proof)
FIELD(descriptor)
- FIELD(asset_id)
+ FIELD(opt_amount_commitment)
END_SERIALIZE()
-
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(operation_type)
- BOOST_SERIALIZE(proof)
BOOST_SERIALIZE(descriptor)
+ BOOST_SERIALIZE(opt_amount_commitment)
+ END_BOOST_SERIALIZATION()
+ };
+
+ struct asset_operation_proof
+ {
+ // linear composition proof for the fact amount_commitment = lin(asset_id, G)
+ boost::optional opt_amount_commitment_composition_proof; // for hidden supply
+ boost::optional opt_amount_commitment_g_proof; // for non-hidden supply, proofs that amount_commitment - supply * asset_id = lin(G)
+
+ BEGIN_VERSIONED_SERIALIZE()
+ FIELD(opt_amount_commitment_composition_proof)
+ FIELD(opt_amount_commitment_g_proof)
+ END_SERIALIZE()
+
+ BEGIN_BOOST_SERIALIZATION()
+ BOOST_SERIALIZE(opt_amount_commitment_composition_proof)
+ BOOST_SERIALIZE(opt_amount_commitment_g_proof)
END_BOOST_SERIALIZATION()
};
@@ -889,10 +913,10 @@ namespace currency
END_SERIALIZE()
};
- typedef boost::mpl::vector25<
+ typedef boost::mpl::vector23<
tx_service_attachment, tx_comment, tx_payer_old, tx_receiver_old, tx_derivation_hint, std::string, tx_crypto_checksum, etc_tx_time, etc_tx_details_unlock_time, etc_tx_details_expiration_time,
etc_tx_details_flags, crypto::public_key, extra_attachment_info, extra_alias_entry_old, extra_user_data, extra_padding, etc_tx_flags16_t, etc_tx_details_unlock_time2,
- tx_payer, tx_receiver, extra_alias_entry, zarcanum_tx_data_v1, zc_outs_range_proof, zc_balance_proof, asset_descriptor_operation
+ tx_payer, tx_receiver, extra_alias_entry, zarcanum_tx_data_v1, asset_descriptor_operation
> all_payload_types;
typedef boost::make_variant_over::type payload_items_v;
@@ -943,7 +967,7 @@ namespace currency
typedef boost::variant signature_v;
-
+ typedef boost::variant proof_v;
//include backward compatibility defintions
@@ -952,11 +976,9 @@ namespace currency
class transaction_prefix
{
public:
- // tx version information
- uint64_t version{};
- //extra
- std::vector extra;
+ uint64_t version = 0;
std::vector vin;
+ std::vector extra;
std::vector vout;
BEGIN_SERIALIZE()
@@ -965,12 +987,9 @@ namespace currency
CHAIN_TRANSITION_VER(TRANSACTION_VERSION_PRE_HF4, transaction_prefix_v1)
if(CURRENT_TRANSACTION_VERSION < version) return false;
FIELD(vin)
- FIELD(vout)
FIELD(extra)
+ FIELD(vout)
END_SERIALIZE()
-
- protected:
- transaction_prefix(){}
};
/*
@@ -986,55 +1005,23 @@ namespace currency
class transaction: public transaction_prefix
{
public:
- std::vector signatures;
std::vector attachment;
-
- transaction();
+ std::vector signatures;
+ std::vector proofs;
BEGIN_SERIALIZE_OBJECT()
FIELDS(*static_cast(this))
CHAIN_TRANSITION_VER(TRANSACTION_VERSION_INITAL, transaction_v1)
CHAIN_TRANSITION_VER(TRANSACTION_VERSION_PRE_HF4, transaction_v1)
- FIELD(signatures)
FIELD(attachment)
+ FIELD(signatures)
+ FIELD(proofs)
END_SERIALIZE()
};
- inline
- transaction::transaction()
- {
- version = 0;
- vin.clear();
- vout.clear();
- extra.clear();
- signatures.clear();
- attachment.clear();
-
- }
- /*
- inline
- transaction::~transaction()
- {
- //set_null();
- }
-
- inline
- void transaction::set_null()
- {
- version = 0;
- unlock_time = 0;
- vin.clear();
- vout.clear();
- extra.clear();
- signatures.clear();
- }
- */
-
-
-
/************************************************************************/
/* */
@@ -1096,8 +1083,6 @@ namespace currency
*/
//-------------------------------------------------------------------------------------------------------------------
-
-
#pragma pack(push, 1)
struct stake_modifier_type
{
@@ -1107,7 +1092,6 @@ namespace currency
struct stake_kernel
{
-
stake_modifier_type stake_modifier;
uint64_t block_timestamp; //this block timestamp
crypto::key_image kimage;
@@ -1140,6 +1124,15 @@ namespace currency
END_KV_SERIALIZE_MAP()
};
+ bool operator ==(const currency::transaction& a, const currency::transaction& b);
+ bool operator ==(const currency::block& a, const currency::block& b);
+ bool operator ==(const currency::extra_attachment_info& a, const currency::extra_attachment_info& b);
+ bool operator ==(const currency::NLSAG_sig& a, const currency::NLSAG_sig& b);
+ bool operator ==(const currency::void_sig& a, const currency::void_sig& b);
+ bool operator ==(const currency::ZC_sig& a, const currency::ZC_sig& b);
+ bool operator ==(const currency::zarcanum_sig& a, const currency::zarcanum_sig& b);
+ bool operator ==(const currency::ref_by_id& a, const currency::ref_by_id& b);
+
} // namespace currency
POD_MAKE_HASHABLE(currency, account_public_address);
@@ -1215,12 +1208,12 @@ SET_VARIANT_TAGS(currency::NLSAG_sig, 42, "NLSAG_sig");
SET_VARIANT_TAGS(currency::ZC_sig, 43, "ZC_sig");
SET_VARIANT_TAGS(currency::void_sig, 44, "void_sig");
SET_VARIANT_TAGS(currency::zarcanum_sig, 45, "zarcanum_sig");
-SET_VARIANT_TAGS(currency::zc_outs_range_proof, 46, "zc_outs_range_proof");
-SET_VARIANT_TAGS(currency::zc_balance_proof, 47, "zc_balance_proof");
+SET_VARIANT_TAGS(currency::zc_asset_surjection_proof, 46, "zc_asset_surjection_proof");
+SET_VARIANT_TAGS(currency::zc_outs_range_proof, 47, "zc_outs_range_proof");
+SET_VARIANT_TAGS(currency::zc_balance_proof, 48, "zc_balance_proof");
-SET_VARIANT_TAGS(currency::open_asset_id, 48, "asset_id");
-
-SET_VARIANT_TAGS(currency::asset_descriptor_operation, 48, "asset_descriptor_base");
+SET_VARIANT_TAGS(currency::asset_descriptor_operation, 49, "asset_descriptor_base");
+SET_VARIANT_TAGS(currency::asset_operation_proof, 50, "asset_operation_proof");
diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp
index fd93da34..a2d564df 100644
--- a/src/currency_core/currency_format_utils.cpp
+++ b/src/currency_core/currency_format_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018 Zano Project
+// Copyright (c) 2014-2023 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Copyright (c) 2012-2013 The Boolberry developers
@@ -14,6 +14,7 @@ using namespace epee;
#include "print_fixed_point_helper.h"
#include "currency_format_utils.h"
+#include "currency_format_utils_transactions.h"
#include "serialization/binary_utils.h"
#include "serialization/stl_containers.h"
#include "currency_core/currency_config.h"
@@ -33,6 +34,8 @@ using namespace epee;
#include "crypto/bitcoin/sha256_helper.h"
#include "crypto_config.h"
+#define DBG_VAL_PRINT(x) ((void)0) // LOG_PRINT_CYAN(std::setw(42) << std::left << #x ":" << x, LOG_LEVEL_0)
+
namespace currency
{
@@ -42,57 +45,211 @@ namespace currency
tx.extra.push_back(alinfo);
return true;
}
-
- //---------------------------------------------------------------
- /*
- bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins,
- size_t current_block_size,
- uint64_t fee,
- const account_public_address &miner_address,
- transaction& tx,
- const blobdata& extra_nonce,
- size_t max_outs)
- {
-
- alias_info alias = AUTO_VAL_INIT(alias);
- return construct_miner_tx(height, median_size, already_generated_coins, current_block_size,
- fee,
- miner_address,
- tx,
- extra_nonce,
- max_outs,
- alias,
- false,
- pos_entry());
- }*/
-
-
//--------------------------------------------------------------------------------
- bool generate_zc_outs_range_proof(size_t out_index_start, size_t outs_count, const crypto::scalar_vec_t& amounts, const crypto::scalar_vec_t& blinding_masks,
- const std::vector& vouts, zc_outs_range_proof& result)
+ bool generate_asset_surjection_proof(const crypto::hash& context_hash, bool has_non_zc_inputs, outputs_generation_context& ogc, zc_asset_surjection_proof& result)
{
- //TODO: review for Andre
- CHECK_AND_ASSERT_MES(amounts.size() == outs_count, false, "");
- CHECK_AND_ASSERT_MES(blinding_masks.size() == outs_count, false, "");
- CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, "");
+ bool r = false;
+ size_t outs_count = ogc.blinded_asset_ids.size();
+ CHECK_AND_ASSERT_MES(outs_count > 0, false, "blinded_asset_ids shouldn't be empty");
+ CHECK_AND_ASSERT_MES(outs_count == ogc.asset_id_blinding_masks.size(), false, "asset_id_blinding_masks != outs_count");
- std::vector commitments_1div8;
- for (size_t out_index = out_index_start, i = 0; i < outs_count; ++out_index, ++i)
+ size_t zc_ins_count = ogc.pseudo_outs_blinded_asset_ids.size();
+ if (zc_ins_count == 0)
{
- const tx_out_zarcanum& toz = boost::get(vouts[out_index]); // may throw an exception, only zarcanum outputs are exprected
- const crypto::public_key* p = &toz.amount_commitment;
- commitments_1div8.push_back(p);
+ // if there's no ZC inputs all the outputs must be native coins with explicit asset ids
+ for(size_t j = 0; j < ogc.blinded_asset_ids.size(); ++j)
+ {
+ CHECK_AND_ASSERT_MES(ogc.blinded_asset_ids[j] == currency::native_coin_asset_id_pt, false, "no ZC ins: out #" << j << " has a non-explicit asset id");
+ CHECK_AND_ASSERT_MES(ogc.asset_id_blinding_masks[j] == 0, false, "no ZC ins: out #" << j << " has non-zero asset id blinding mask");
+ }
+ return true;
+ }
+
+ // okay, have some ZC inputs
+
+ CHECK_AND_ASSERT_MES(zc_ins_count == ogc.pseudo_outs_plus_real_out_blinding_masks.size(), false, "zc_ins_count != pseudo_outs_plus_real_out_blinding_masks");
+ CHECK_AND_ASSERT_MES(zc_ins_count == ogc.real_zc_ins_asset_ids.size(), false, "zc_ins_count != real_zc_ins_asset_ids");
+
+ // ins
+ //ogc.pseudo_outs_blinded_asset_ids; // T^p_i = T_real + r'_i * X
+ //ogc.pseudo_outs_plus_real_out_blinding_masks; // r_pi + r'_j
+
+ // outs
+ //ogc.blinded_asset_ids; // T'_j = H_j + s_j * X
+ //ogc.asset_id_blinding_masks; // s_j
+
+ for(size_t j = 0; j < outs_count; ++j)
+ {
+ const crypto::public_key H = ogc.asset_ids[j].to_public_key();
+ const crypto::point_t& T = ogc.blinded_asset_ids[j];
+
+ std::vector ring;
+ ring.reserve(zc_ins_count);
+ size_t secret_index = SIZE_MAX;
+ crypto::scalar_t secret = -ogc.asset_id_blinding_masks[j];
+
+ for(size_t i = 0; i < zc_ins_count; ++i)
+ {
+ ring.emplace_back(ogc.pseudo_outs_blinded_asset_ids[i] - T);
+ if (secret_index == SIZE_MAX && ogc.real_zc_ins_asset_ids[i] == H)
+ {
+ secret_index = i;
+ secret += ogc.pseudo_outs_plus_real_out_blinding_masks[secret_index];
+ }
+ }
+
+ // additional ring member for native coins in txs with non-zc inputs
+ if (has_non_zc_inputs)
+ {
+ ring.emplace_back(currency::native_coin_asset_id_pt - T);
+ if (secret_index == SIZE_MAX && H == native_coin_asset_id)
+ secret_index = ring.size() - 1;
+ }
+
+ // additional ring member for asset emitting operation
+ if (!ogc.ao_amount_blinding_mask.is_zero())
+ {
+ ring.emplace_back(ogc.ao_asset_id_pt - T);
+ if (secret_index == SIZE_MAX && H == ogc.ao_asset_id)
+ secret_index = ring.size() - 1;
+ }
+
+ CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "out #" << j << ": can't find a corresponding asset id in inputs");
+
+ result.bge_proofs.emplace_back(crypto::BGE_proof_s{});
+ uint8_t err = 0;
+ r = crypto::generate_BGE_proof(context_hash, ring, secret, secret_index, result.bge_proofs.back(), &err);
+ CHECK_AND_ASSERT_MES(r, false, "out #" << j << ": generate_BGE_proof failed with err=" << (int)err);
}
- result.outputs_count = outs_count;
+ return true;
+ }
+ //--------------------------------------------------------------------------------
+ bool verify_asset_surjection_proof(const transaction& tx, const crypto::hash& tx_id)
+ {
+ bool r = false;
+ if (tx.version <= TRANSACTION_VERSION_PRE_HF4)
+ return true;
+
+ size_t outs_count = tx.vout.size();
+
+ bool has_ZC_inputs = false;
+ bool has_non_ZC_inputs = false;
+ for(const auto& in : tx.vin)
+ {
+ if (in.type() == typeid(txin_zc_input))
+ has_ZC_inputs = true;
+ else
+ has_non_ZC_inputs = true;
+ }
+
+ if (!has_ZC_inputs)
+ {
+ // no ZC ins -- just make sure there's only native outputs with explicit asset ids
+ for(size_t j = 0; j < outs_count; ++j)
+ {
+ CHECK_AND_ASSERT_MES(boost::get(tx.vout[j]).blinded_asset_id == native_coin_asset_id_1div8, false, "output #" << j << " has a non explicitly native asset id");
+ }
+ return true;
+ }
+
+ // tx has ZC inputs
+ const zc_asset_surjection_proof& sig = get_type_in_variant_container_by_ref(tx.proofs); // order of proofs and uniqueness of zc_asset_surjection_proof should be check before on prevalidation
+ CHECK_AND_ASSERT_MES(sig.bge_proofs.size() == outs_count, false, "ASP count: " << sig.bge_proofs.size() << ", outputs: " << outs_count << " => missmatch");
+
+ // make a ring
+ std::vector pseudo_outs_blinded_asset_ids;
+ for(const auto& sig : tx.signatures)
+ {
+ if (sig.type() == typeid(ZC_sig))
+ pseudo_outs_blinded_asset_ids.emplace_back(crypto::point_t(boost::get(sig).pseudo_out_blinded_asset_id).modify_mul8());
+ }
+ if (has_non_ZC_inputs)
+ pseudo_outs_blinded_asset_ids.emplace_back(currency::native_coin_asset_id_pt); // additional ring member for txs with non-zc inputs
+
+ asset_descriptor_operation ado{};
+ if (is_asset_emitting_transaction(tx, &ado))
+ {
+ crypto::point_t asset_id_pt = crypto::c_point_0;
+ calculate_asset_id(ado.descriptor.owner, &asset_id_pt, nullptr); // TODO @#@# optimization: this expensive calculation should be done only once
+ pseudo_outs_blinded_asset_ids.emplace_back(asset_id_pt); // additional ring member for asset emitting tx
+ }
+
+ for(size_t j = 0; j < outs_count; ++j)
+ {
+ crypto::point_t blinded_asset_id(boost::get(tx.vout[j]).blinded_asset_id);
+ blinded_asset_id.modify_mul8();
+
+ // TODO @#@# remove this redundant conversion to pubkey and back
+ std::vector ring(pseudo_outs_blinded_asset_ids.size());
+ std::vector ring_pointers(pseudo_outs_blinded_asset_ids.size());
+ for(size_t i = 0, n = pseudo_outs_blinded_asset_ids.size(); i < n; ++i)
+ {
+ ring[i] = ((crypto::c_scalar_1div8 * (pseudo_outs_blinded_asset_ids[i] - blinded_asset_id)).to_public_key());
+ ring_pointers[i] = &ring[i];
+ }
+
+ uint8_t err = 0;
+ CHECK_AND_ASSERT_MES(crypto::verify_BGE_proof(tx_id, ring_pointers, sig.bge_proofs[j], &err), false, "verify_BGE_proof failed, err = " << (int)err);
+ }
+
+ return true;
+ }
+ //--------------------------------------------------------------------------------
+ bool generate_zc_outs_range_proof(const crypto::hash& context_hash, size_t out_index_start, const outputs_generation_context& outs_gen_context,
+ const std::vector& vouts, zc_outs_range_proof& result)
+ {
+ size_t outs_count = outs_gen_context.amounts.size();
+ // TODO @#@# reconsider this check CHECK_AND_ASSERT_MES(outs_gen_context.check_sizes(outs_count), false, "");
+ CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, "");
+
+ // prepare data for aggregation proof
+ std::vector amount_commitments_for_rp_aggregation; // E' = amount * U + y' * G
+ crypto::scalar_vec_t y_primes; // y'
+ for (size_t out_index = out_index_start, i = 0; i < outs_count; ++out_index, ++i)
+ {
+ crypto::scalar_t y_prime = crypto::scalar_t::random();
+ amount_commitments_for_rp_aggregation.emplace_back(outs_gen_context.amounts[i] * crypto::c_point_U + y_prime * crypto::c_point_G); // E'_j = e_j * U + y'_j * G
+ y_primes.emplace_back(std::move(y_prime));
+ }
+
+ // aggregation proof
uint8_t err = 0;
- bool r = crypto::bpp_gen<>(amounts, blinding_masks, commitments_1div8, result.bpp, &err);
+ bool r = crypto::generate_vector_UG_aggregation_proof(context_hash, outs_gen_context.amounts, outs_gen_context.amount_blinding_masks, y_primes,
+ outs_gen_context.amount_commitments,
+ amount_commitments_for_rp_aggregation,
+ outs_gen_context.blinded_asset_ids, result.aggregation_proof, &err);
+ CHECK_AND_ASSERT_MES(r, false, "generate_vector_UG_aggregation_proof failed with error " << (int)err);
+
+ // aggregated range proof
+ std::vector commitments_1div8(result.aggregation_proof.amount_commitments_for_rp_aggregation.size());
+ for(size_t i = 0, sz = result.aggregation_proof.amount_commitments_for_rp_aggregation.size(); i < sz; ++i)
+ commitments_1div8[i] = &result.aggregation_proof.amount_commitments_for_rp_aggregation[i];
+
+ err = 0;
+ r = crypto::bpp_gen(outs_gen_context.amounts, y_primes, commitments_1div8, result.bpp, &err);
CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << (int)err);
return true;
}
-
//---------------------------------------------------------------
+ bool verify_multiple_zc_outs_range_proofs(const std::vector& range_proofs)
+ {
+ if (range_proofs.empty())
+ return true;
+
+ std::vector sigs;
+ sigs.reserve(range_proofs.size());
+ for(auto& el : range_proofs)
+ sigs.emplace_back(el.range_proof.bpp, el.amount_commitments);
+
+ uint8_t err = 0;
+ bool r = crypto::bpp_verify(sigs, &err);
+ CHECK_AND_ASSERT_MES(r, false, "bpp_verify failed with error " << (int)err);
+
+ return true;
+ }
+ //--------------------------------------------------------------------------------
wide_difficulty_type correct_difficulty_with_sequence_factor(size_t sequence_factor, wide_difficulty_type diff)
{
//delta=delta*(0.75^n)
@@ -103,54 +260,72 @@ namespace currency
return diff;
}
//------------------------------------------------------------------
- // for txs with no zc inputs (and thus no zc signatures) but with zc outputs
- bool generate_tx_balance_proof(transaction &tx, const crypto::scalar_t& outputs_blinding_masks_sum, uint64_t block_reward_for_miner_tx = 0)
+ bool generate_tx_balance_proof(const transaction &tx, const crypto::hash& tx_id, const outputs_generation_context& ogc, uint64_t block_reward_for_miner_tx, currency::zc_balance_proof& proof)
{
CHECK_AND_ASSERT_MES(tx.version > TRANSACTION_VERSION_PRE_HF4, false, "unsupported tx.version: " << tx.version);
- CHECK_AND_ASSERT_MES(count_type_in_variant_container(tx.signatures) == 0, false, "");
+ CHECK_AND_ASSERT_MES(count_type_in_variant_container(tx.proofs) == 0, false, "zc_balance_proof is already present");
+ bool r = false;
uint64_t bare_inputs_sum = block_reward_for_miner_tx;
- // TODO: condider remove the followin cycle
+ size_t zc_inputs_count = 0;
for(auto& vin : tx.vin)
{
VARIANT_SWITCH_BEGIN(vin);
- VARIANT_CASE(txin_to_key, tk)
+ VARIANT_CASE_CONST(txin_to_key, tk)
bare_inputs_sum += tk.amount;
- VARIANT_CASE(txin_htlc, foo);
+ VARIANT_CASE_CONST(txin_htlc, foo);
CHECK_AND_ASSERT_MES(false, false, "unexpected txin_htlc input");
- VARIANT_CASE(txin_multisig, ms);
- bare_inputs_sum += ms.amount;
- VARIANT_CASE(txin_zc_input, foo);
- CHECK_AND_ASSERT_MES(false, false, "unexpected txin_zc_input input");
+ VARIANT_CASE_CONST(txin_multisig, ms);
+ //bare_inputs_sum += ms.amount;
+ CHECK_AND_ASSERT_MES(false, false, "unexpected txin_multisig input"); // TODO @#@# check support for multisig inputs
+ VARIANT_CASE_CONST(txin_zc_input, foo);
+ ++zc_inputs_count;
VARIANT_SWITCH_END();
}
- crypto::point_t outs_commitments_sum = crypto::c_point_0;
- for(auto& vout : tx.vout)
- {
- CHECK_AND_ASSERT_MES(vout.type() == typeid(tx_out_zarcanum), false, "unexpected type in outs: " << vout.type().name());
- const tx_out_zarcanum& ozc = boost::get(vout);
- outs_commitments_sum += crypto::point_t(ozc.amount_commitment); // amount_commitment premultiplied by 1/8
- }
- outs_commitments_sum.modify_mul8();
-
uint64_t fee = 0;
CHECK_AND_ASSERT_MES(get_tx_fee(tx, fee), false, "unable to get tx fee");
- // sum(bare inputs' amounts) * H + sum(pseudo outs commitments for ZC inputs) + residual * G = sum(outputs' commitments) + fee * H
- // <=>
- // (fee - sum(bare inputs' amounts)) * H - sum(pseudo outs commitments for ZC inputs) + sum(outputs' commitments) = residual * G
+ if (zc_inputs_count == 0)
+ {
+ // no ZC inputs => all inputs are bare inputs; all outputs have explicit asset_id = native_coin_asset_id; in main balance equation we only need to cancel out G-component
+ CHECK_AND_ASSERT_MES(count_type_in_variant_container(tx.signatures) == 0, false, "ZC_sig is unexpected");
+ CHECK_AND_ASSERT_MES(ogc.asset_id_blinding_mask_x_amount_sum.is_zero(), false, "it's expected that all asset ids for this tx are obvious and thus explicit"); // because this tx has no ZC inputs => all outs clearly have native asset id
+ CHECK_AND_ASSERT_MES(ogc.ao_amount_blinding_mask.is_zero(), false, "asset emmission is not allowed for txs without ZC inputs");
- // tx doesn't have any zc inputs --> add Schnorr proof for commitment to zero
- CHECK_AND_ASSERT_MES(count_type_in_variant_container(tx.attachment) == 0, false, "");
- zc_balance_proof balance_proof = AUTO_VAL_INIT(balance_proof);
+ // (sum(bare inputs' amounts) - fee) * H + sum(pseudo out amount commitments) - sum(outputs' commitments) = lin(G)
- crypto::point_t commitment_to_zero = outs_commitments_sum + (crypto::scalar_t(fee) - crypto::scalar_t(bare_inputs_sum)) * crypto::c_point_H;
- //crypto::scalar_t witness = outputs_blinding_masks_sum;
+ crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * currency::native_coin_asset_id_pt - ogc.amount_commitments_sum;
+ crypto::scalar_t secret_x = -ogc.amount_blinding_masks_sum;
+#ifndef NDEBUG
+ CHECK_AND_ASSERT_MES(commitment_to_zero == secret_x * crypto::c_point_G, false, "internal error: commitment_to_zero is malformed (G)");
+#endif
+ r = crypto::generate_schnorr_sig(tx_id, commitment_to_zero, secret_x, proof.ss);
+ CHECK_AND_ASSERT_MES(r, false, "generate_schnorr_sig (G) failed");
+ }
+ else // i.e. zc_inputs_count != 0
+ {
+ // there're ZC inputs => in main balance equation we only need to cancel out X-component, because G-component cancelled out by choosing blinding mask for the last pseudo out amount commitment
- // TODO: consider adding more data to message
- crypto::generate_signature(null_hash, commitment_to_zero.to_public_key(), outputs_blinding_masks_sum.as_secret_key(), balance_proof.s);
- tx.attachment.push_back(balance_proof);
+ crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * currency::native_coin_asset_id_pt + ogc.pseudo_out_amount_commitments_sum + ogc.ao_amount_commitment - ogc.amount_commitments_sum;
+ crypto::scalar_t secret_x = ogc.real_in_asset_id_blinding_mask_x_amount_sum - ogc.asset_id_blinding_mask_x_amount_sum;
+
+ DBG_VAL_PRINT(bare_inputs_sum);
+ DBG_VAL_PRINT(fee);
+ DBG_VAL_PRINT(ogc.pseudo_out_amount_commitments_sum);
+ DBG_VAL_PRINT(ogc.ao_amount_commitment);
+ DBG_VAL_PRINT(ogc.amount_commitments_sum);
+ DBG_VAL_PRINT(ogc.real_in_asset_id_blinding_mask_x_amount_sum);
+ DBG_VAL_PRINT(ogc.asset_id_blinding_mask_x_amount_sum);
+ DBG_VAL_PRINT(commitment_to_zero);
+ DBG_VAL_PRINT(secret_x);
+
+#ifndef NDEBUG
+ CHECK_AND_ASSERT_MES(commitment_to_zero == secret_x * crypto::c_point_X, false, "internal error: commitment_to_zero is malformed (X)");
+#endif
+ r = crypto::generate_schnorr_sig(tx_id, commitment_to_zero, secret_x, proof.ss);
+ CHECK_AND_ASSERT_MES(r, false, "generate_schnorr_sig (X) failed");
+ }
return true;
}
@@ -187,7 +362,7 @@ namespace currency
size_t max_outs /* = CURRENCY_MINER_TX_MAX_OUTS */,
bool pos /* = false */,
const pos_entry& pe /* = pos_entry() */, // only pe.stake_unlock_time and pe.stake_amount are used now, TODO: consider refactoring -- sowle
- crypto::scalar_t* blinding_masks_sum_ptr /* = nullptr */,
+ outputs_generation_context* ogc_ptr /* = nullptr */,
const keypair* tx_one_time_key_to_use /* = nullptr */
)
{
@@ -231,6 +406,7 @@ namespace currency
tx_destination_entry de = AUTO_VAL_INIT(de);
de.addr.push_back(miner_address);
de.amount = a;
+ de.explicit_native_asset_id = true; // don't use asset id blinding as it's obvious which asset it is
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
{
//this means that block is creating after hardfork_1 and unlock_time is needed to set for every destination separately
@@ -245,6 +421,7 @@ namespace currency
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
stake_lock_time = pe.stake_unlock_time;
destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address, stake_lock_time));
+ destinations.back().explicit_native_asset_id = true; // don't use asset id blinding as it's obvious which asset it is
}
CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS || height == 0, false, "Too many outs (" << destinations.size() << ")! Miner tx can't be constructed.");
@@ -265,6 +442,8 @@ namespace currency
//we always add extra_padding with 2 bytes length to make possible for get_block_template to adjust cumulative size
tx.extra.push_back(extra_padding());
+ size_t zc_ins_count = 0;
+
// input #0: txin_gen
txin_gen in;
in.height = height;
@@ -278,6 +457,7 @@ namespace currency
// just placeholders, they will be filled in wallet2::prepare_and_sign_pos_block()
tx.vin.emplace_back(std::move(txin_zc_input()));
tx.signatures.emplace_back(std::move(zarcanum_sig()));
+ ++zc_ins_count;
}
else
{
@@ -289,37 +469,25 @@ namespace currency
}
// fill outputs
- crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret blinging masks for each output. For range proof generation
- crypto::scalar_vec_t amounts(destinations.size()); // vector of amounts, converted to scalars. For ranage proof generation
- crypto::scalar_t blinding_masks_sum = 0;
+ outputs_generation_context outs_gen_context(zc_ins_count, destinations.size()); // auxiliary data for each output
uint64_t output_index = 0;
for (auto& d : destinations)
{
std::set deriv_cache;
finalized_tx result = AUTO_VAL_INIT(result);
uint8_t tx_outs_attr = 0;
- r = construct_tx_out(d, txkey.sec, output_index, tx, deriv_cache, account_keys(), blinding_masks[output_index], result, tx_outs_attr);
+ r = construct_tx_out(d, txkey.sec, output_index, tx, deriv_cache, account_keys(),
+ outs_gen_context.asset_id_blinding_masks[output_index], outs_gen_context.amount_blinding_masks[output_index],
+ outs_gen_context.blinded_asset_ids[output_index], outs_gen_context.amount_commitments[output_index], result, tx_outs_attr);
CHECK_AND_ASSERT_MES(r, false, "construct_tx_out failed, output #" << output_index << ", amount: " << print_money_brief(d.amount));
- amounts[output_index] = d.amount;
- blinding_masks_sum += blinding_masks[output_index];
+ outs_gen_context.amounts[output_index] = d.amount;
+ outs_gen_context.asset_ids[output_index] = crypto::point_t(d.asset_id);
+ outs_gen_context.asset_id_blinding_mask_x_amount_sum += outs_gen_context.asset_id_blinding_masks[output_index] * d.amount;
+ outs_gen_context.amount_blinding_masks_sum += outs_gen_context.amount_blinding_masks[output_index];
+ outs_gen_context.amount_commitments_sum += outs_gen_context.amount_commitments[output_index];
++output_index;
}
- if (tx.version > TRANSACTION_VERSION_PRE_HF4)
- {
- //add range proofs
- currency::zc_outs_range_proof range_proofs = AUTO_VAL_INIT(range_proofs);
- bool r = generate_zc_outs_range_proof(0, amounts.size(), amounts, blinding_masks, tx.vout, range_proofs);
- CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()");
- tx.attachment.push_back(range_proofs);
-
- if (!pos)
- {
- r = generate_tx_balance_proof(tx, blinding_masks_sum, block_reward);
- CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
- }
- }
-
if (tx.attachment.size())
add_attachments_info_to_extra(tx.extra, tx.attachment);
@@ -329,26 +497,103 @@ namespace currency
set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
}
- if (blinding_masks_sum_ptr)
- *blinding_masks_sum_ptr = blinding_masks_sum;
+ if (ogc_ptr)
+ *ogc_ptr = outs_gen_context; // TODO @#@# consider refactoring (a lot of copying) -- sowle
+
+
+ if (tx.version > TRANSACTION_VERSION_PRE_HF4 && !pos)
+ {
+ // This is for PoW blocks only, because PoS blocks proofs are handled in wallet2::prepare_and_sign_pos_block() due to the necessity of making Zarcanum proofs first
+ //
+ // tx hash should be sealed by now
+ crypto::hash tx_id = get_transaction_hash(tx);
+
+ // asset surjection proof
+ currency::zc_asset_surjection_proof asp{};
+ bool r = generate_asset_surjection_proof(tx_id, true, outs_gen_context, asp);
+ CHECK_AND_ASSERT_MES(r, false, "generete_asset_surjection_proof failed");
+ tx.proofs.emplace_back(std::move(asp));
+
+ // range proofs
+ currency::zc_outs_range_proof range_proofs{};
+ r = generate_zc_outs_range_proof(tx_id, 0, outs_gen_context, tx.vout, range_proofs);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()");
+ tx.proofs.emplace_back(std::move(range_proofs));
+
+ // balance proof
+ currency::zc_balance_proof balance_proof{};
+ r = generate_tx_balance_proof(tx, tx_id, outs_gen_context, block_reward, balance_proof);
+ CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
+ tx.proofs.emplace_back(std::move(balance_proof));
+ }
+
+
return true;
}
//------------------------------------------------------------------
- bool check_tx_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx /* = 0 */)
+ bool check_tx_bare_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx /* = 0 */)
+ {
+ // legacy checks for old fashioned tx with non-hidden amounts
+ CHECK_AND_ASSERT_MES(tx.version <= TRANSACTION_VERSION_PRE_HF4, false, "check_tx_bare_balance can't check post-HF4 txs");
+ uint64_t bare_outputs_sum = get_outs_money_amount(tx);
+ uint64_t bare_inputs_sum = get_inputs_money_amount(tx);
+
+ if (additional_inputs_amount_and_fees_for_mining_tx == 0)
+ {
+ // normal tx
+ CHECK_AND_ASSERT_MES(bare_inputs_sum >= bare_outputs_sum, false, "tx balance error: sum of inputs (" << print_money_brief(bare_inputs_sum)
+ << ") is less than or equal to sum of outputs(" << print_money_brief(bare_outputs_sum) << ")");
+ }
+ else
+ {
+ // miner tx
+ CHECK_AND_ASSERT_MES(bare_inputs_sum + additional_inputs_amount_and_fees_for_mining_tx == bare_outputs_sum, false,
+ "tx balance error: sum of inputs (" << print_money_brief(bare_inputs_sum) <<
+ ") + additional inputs and fees (" << print_money_brief(additional_inputs_amount_and_fees_for_mining_tx) <<
+ ") is less than or equal to sum of outputs(" << print_money_brief(bare_outputs_sum) << ")");
+ }
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool validate_asset_operation(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, crypto::public_key& asset_id)
+ {
+ crypto::point_t asset_id_pt = crypto::c_point_0;
+ calculate_asset_id(ado.descriptor.owner, &asset_id_pt, &asset_id);
+
+ CHECK_AND_ASSERT_MES(count_type_in_variant_container(tx.proofs) == 1, false, "asset_operation_proof not present or present more than once");
+ const asset_operation_proof& aop = get_type_in_variant_container_by_ref(tx.proofs);
+
+ if (ado.descriptor.hidden_supply)
+ {
+ CHECK_AND_ASSERT_MES(aop.opt_amount_commitment_composition_proof.has_value(), false, "opt_amount_commitment_composition_proof is absent");
+ // TODO @#@# if asset is hidden -- theck composition proof
+ return false;
+ }
+ else
+ {
+ // make sure that amount commitment corresponds to opt_amount_commitment_g_proof
+ CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is absent");
+ CHECK_AND_ASSERT_MES(aop.opt_amount_commitment_g_proof.has_value(), false, "opt_amount_commitment_g_proof is absent");
+ crypto::point_t A = crypto::point_t(ado.opt_amount_commitment.get()).modify_mul8() - ado.descriptor.current_supply * asset_id_pt;
+
+ bool r = crypto::check_signature(tx_id, A.to_public_key(), aop.opt_amount_commitment_g_proof.get());
+ CHECK_AND_ASSERT_MES(r, false, "opt_amount_commitment_g_proof check failed");
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------
+ bool check_tx_balance(const transaction& tx, const crypto::hash& tx_id, uint64_t additional_inputs_amount_and_fees_for_mining_tx /* = 0 */)
{
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
{
- //@#@ TODO: This is just a temporary code
- uint64_t assets_emmited = 0;
- asset_descriptor_operation ado = AUTO_VAL_INIT(ado);
- if (get_type_in_variant_container(tx.extra, ado) && ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER)
- {
- assets_emmited += ado.descriptor.current_supply;
- }
+ zc_balance_proof balance_proof = AUTO_VAL_INIT(balance_proof);
+ bool r = get_type_in_variant_container(tx.proofs, balance_proof);
+ CHECK_AND_ASSERT_MES(r, false, "zc_balance_proof is missing in tx proofs");
size_t zc_inputs_count = 0;
- uint64_t bare_inputs_sum = additional_inputs_amount_and_fees_for_mining_tx + assets_emmited;
+ uint64_t bare_inputs_sum = additional_inputs_amount_and_fees_for_mining_tx;
for(auto& vin : tx.vin)
{
VARIANT_SWITCH_BEGIN(vin);
@@ -363,7 +608,7 @@ namespace currency
VARIANT_SWITCH_END();
}
- crypto::point_t outs_commitments_sum = crypto::c_point_0;
+ crypto::point_t outs_commitments_sum = crypto::c_point_0; // TODO: consider adding additional commitments / spends / burns here
for(auto& vout : tx.vout)
{
CHECK_AND_ASSERT_MES(vout.type() == typeid(tx_out_zarcanum), false, "unexpected type in outs: " << vout.type().name());
@@ -378,8 +623,16 @@ namespace currency
CHECK_AND_ASSERT_MES(additional_inputs_amount_and_fees_for_mining_tx == 0 || fee == 0, false, "invalid tx: fee = " << print_money_brief(fee) <<
", additional inputs + fees = " << print_money_brief(additional_inputs_amount_and_fees_for_mining_tx));
- size_t zc_sigs_count = 0;
crypto::point_t sum_of_pseudo_out_amount_commitments = crypto::c_point_0;
+ // take into account newly emitted assets
+ asset_descriptor_operation ado = AUTO_VAL_INIT(ado);
+ if (get_type_in_variant_container(tx.extra, ado) && ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) // @#@ TODO: Support other asset operations
+ {
+ // opt_amount_commitment supposed to be validated earlier in validate_asset_operation()
+ CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is not set");
+ sum_of_pseudo_out_amount_commitments += crypto::point_t(ado.opt_amount_commitment.get()); // *1/8
+ }
+ size_t zc_sigs_count = 0;
for(auto& sig_v : tx.signatures)
{
VARIANT_SWITCH_BEGIN(sig_v);
@@ -393,53 +646,25 @@ namespace currency
}
sum_of_pseudo_out_amount_commitments.modify_mul8();
- CHECK_AND_ASSERT_MES(zc_inputs_count == zc_sigs_count, false, "zc inputs count (" << zc_inputs_count << ") and zc sigs count (" << zc_sigs_count << ") missmatch");
+ // (sum(bare inputs' amounts) - fee) * H + sum(pseudo outs commitments for ZC inputs) - sum(outputs' commitments) = lin(X) OR = lin(G)
+ crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * currency::native_coin_asset_id_pt + sum_of_pseudo_out_amount_commitments - outs_commitments_sum;
+ CHECK_AND_ASSERT_MES(zc_inputs_count == zc_sigs_count, false, "zc inputs count (" << zc_inputs_count << ") and zc sigs count (" << zc_sigs_count << ") missmatch");
if (zc_inputs_count > 0)
{
- // no need for additional Schnorr proof for commitment to zero
-
- // sum(bare inputs' amounts) * H + sum(pseudo outs commitments for ZC inputs) = sum(outputs' commitments) + fee * H
- // <=>
- // (sum(bare inputs' amounts) - fee) * H + sum(pseudo outs commitments for ZC inputs) - sum(outputs' commitments) = 0
- crypto::point_t Z = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * crypto::c_point_H + sum_of_pseudo_out_amount_commitments - outs_commitments_sum;
- CHECK_AND_ASSERT_MES(Z.is_zero(), false, "balace equation does not hold");
+ r = crypto::verify_schnorr_sig(tx_id, commitment_to_zero.to_public_key(), balance_proof.ss);
+ CHECK_AND_ASSERT_MES(r, false, "zc_balance_proof (X) is invalid");
}
else
{
- // no zc inputs -- there should be Schnorr proof for commitment to zero
- zc_balance_proof balance_proof = AUTO_VAL_INIT(balance_proof);
- bool r = get_type_in_variant_container(tx.attachment, balance_proof);
- CHECK_AND_ASSERT_MES(r, false, "no zc inputs are present, but at the same time there's no zc_balance_proof in attachment");
-
- // (fee - sum(bare inputs' amounts)) * H + sum(outputs' commitments) = residual * G
- crypto::point_t commitment_to_zero = (crypto::scalar_t(fee) - crypto::scalar_t(bare_inputs_sum)) * crypto::c_point_H + outs_commitments_sum;
- r = crypto::check_signature(null_hash, commitment_to_zero.to_public_key(), balance_proof.s);
- CHECK_AND_ASSERT_MES(r, false, "zc_balance_proof is invalid");
+ r = crypto::verify_schnorr_sig(tx_id, commitment_to_zero.to_public_key(), balance_proof.ss);
+ CHECK_AND_ASSERT_MES(r, false, "zc_balance_proof (G) is invalid");
}
+ return true;
}
- else
- {
- // old fashioned tx with non-hidden amounts
- uint64_t bare_outputs_sum = get_outs_money_amount(tx);
- uint64_t bare_inputs_sum = get_inputs_money_amount(tx);
-
- if (additional_inputs_amount_and_fees_for_mining_tx == 0)
- {
- // normal tx
- CHECK_AND_ASSERT_MES(bare_inputs_sum >= bare_outputs_sum, false, "tx balance error: sum of inputs (" << print_money_brief(bare_inputs_sum)
- << ") is less than or equal to sum of outputs(" << print_money_brief(bare_outputs_sum) << ")");
- }
- else
- {
- // miner tx
- CHECK_AND_ASSERT_MES(bare_inputs_sum + additional_inputs_amount_and_fees_for_mining_tx == bare_outputs_sum, false,
- "tx balance error: sum of inputs (" << print_money_brief(bare_inputs_sum) <<
- ") + additional inputs and fees (" << print_money_brief(additional_inputs_amount_and_fees_for_mining_tx) <<
- ") is less than or equal to sum of outputs(" << print_money_brief(bare_outputs_sum) << ")");
- }
- }
- return true;
+
+ // pre-HF4 txs
+ return check_tx_bare_balance(tx, additional_inputs_amount_and_fees_for_mining_tx);
}
//------------------------------------------------------------------
bool derive_ephemeral_key_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral)
@@ -727,7 +952,7 @@ namespace currency
template
bool operator()(const t_extra_typename& k) const
{
- //do notheing for rest
+ //do nothing for rest
return true;
}
};
@@ -809,6 +1034,8 @@ namespace currency
return derive_public_key_from_target_address(destination_addr, tx_sec_key, index, out_eph_public_key, derivation);
}
//---------------------------------------------------------------
+ // derived_sec_key = Hs(domain, 8 * src_sec_key * src_pub_key, index)
+ // derived_pub_key = derived_sec_key * G
bool derive_key_pair_from_key_pair(const crypto::public_key& src_pub_key, const crypto::secret_key& src_sec_key, crypto::secret_key& derived_sec_key, crypto::public_key& derived_pub_key, const char(&hs_domain)[32], uint64_t index)
{
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
@@ -878,30 +1105,25 @@ namespace currency
return origin_blob;
}
//---------------------------------------------------------------
- bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, uint8_t tx_outs_attr)
+ bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, uint8_t tx_outs_attr /* = CURRENCY_TO_KEY_OUT_RELAXED */)
{
finalized_tx result = AUTO_VAL_INIT(result);
- crypto::scalar_t out_blinding_mask = AUTO_VAL_INIT(out_blinding_mask);
- return construct_tx_out(de, tx_sec_key, output_index, tx, deriv_cache, self, out_blinding_mask, result, tx_outs_attr);
+ crypto::scalar_t asset_blinding_mask{}, amount_blinding_mask{};
+ crypto::point_t blinded_asset_id{}, amount_commitment{};
+ return construct_tx_out(de, tx_sec_key, output_index, tx, deriv_cache, self, asset_blinding_mask, amount_blinding_mask, blinded_asset_id, amount_commitment, result, tx_outs_attr);
}
//---------------------------------------------------------------
bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache,
- const account_keys& self, crypto::scalar_t& out_blinding_mask, finalized_tx& result, uint8_t tx_outs_attr)
+ const account_keys& self, crypto::scalar_t& asset_blinding_mask, crypto::scalar_t& amount_blinding_mask, crypto::point_t& blinded_asset_id, crypto::point_t& amount_commitment,
+ finalized_tx& result, uint8_t tx_outs_attr)
{
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
{
// create tx_out_zarcanum
- CHECK_AND_ASSERT_MES(de.addr.size() == 1, false, "zarcanum multisig not implemented yet");
+ CHECK_AND_ASSERT_MES(de.addr.size() == 1, false, "zarcanum multisig not implemented for tx_out_zarcanum yet");
// TODO @#@# implement multisig support
tx_out_zarcanum out = AUTO_VAL_INIT(out);
- //@#@
- //TODO: TEMPORARY
- if (de.asset_id != currency::null_hash)
- {
- out.etc_details.push_back(open_asset_id{ de.asset_id });
- }
- //@#@
const account_public_address& apa = de.addr.front();
if (apa.spend_public_key == null_pkey && apa.view_public_key == null_pkey)
@@ -916,16 +1138,22 @@ namespace currency
crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h);
out.encrypted_amount = de.amount ^ amount_mask.m_u64[0];
- out_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
- out.amount_commitment = (crypto::c_scalar_1div8 * de.amount * crypto::c_point_H + crypto::c_scalar_1div8 * out_blinding_mask * crypto::c_point_G).to_public_key(); // A = 1/8 * a * H + 1/8 * f * G
+ asset_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
+ blinded_asset_id = crypto::point_t(de.asset_id) + asset_blinding_mask * crypto::c_point_X;
+ out.blinded_asset_id = (crypto::c_scalar_1div8 * blinded_asset_id).to_public_key(); // T = 1/8 * (H_asset + s * X)
+ CHECK_AND_ASSERT_MES(!de.explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id");
+ asset_blinding_mask = de.explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
+ amount_commitment = de.amount * blinded_asset_id + amount_blinding_mask * crypto::c_point_G;
+ out.amount_commitment = (crypto::c_scalar_1div8 * amount_commitment).to_public_key(); // E = 1/8 * e * T + 1/8 * y * G
+
out.mix_attr = tx_outs_attr; // TODO @#@# @CZ check this
}
else
{
// normal output
crypto::public_key derivation = (crypto::scalar_t(tx_sec_key) * crypto::point_t(apa.view_public_key)).modify_mul8().to_public_key(); // d = 8 * r * V
- crypto::scalar_t h; // = crypto::hash_helper_t::hs(derivation, output_index);
+ crypto::scalar_t h = 0;
crypto::derivation_to_scalar((const crypto::key_derivation&)derivation, output_index, h.as_secret_key()); // h = Hs(8 * r * V, i)
out.stealth_address = (h * crypto::c_point_G + crypto::point_t(apa.spend_public_key)).to_public_key();
@@ -934,8 +1162,19 @@ namespace currency
crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h);
out.encrypted_amount = de.amount ^ amount_mask.m_u64[0];
- out_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, Hs(8 * r * V, i) )
- out.amount_commitment = (crypto::c_scalar_1div8 * de.amount * crypto::c_point_H + crypto::c_scalar_1div8 * out_blinding_mask * crypto::c_point_G).to_public_key(); // A = 1/8 * a * H + 1/8 * f * G
+ CHECK_AND_ASSERT_MES(!de.explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id");
+ asset_blinding_mask = de.explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
+ blinded_asset_id = crypto::point_t(de.asset_id) + asset_blinding_mask * crypto::c_point_X;
+ out.blinded_asset_id = (crypto::c_scalar_1div8 * blinded_asset_id).to_public_key(); // T = 1/8 * (H_asset + s * X)
+
+ amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
+ amount_commitment = de.amount * blinded_asset_id + amount_blinding_mask * crypto::c_point_G;
+ out.amount_commitment = (crypto::c_scalar_1div8 * amount_commitment).to_public_key(); // E = 1/8 * e * T + 1/8 * y * G
+
+ //LOG_PRINT_CYAN("OUT " << std::setw(2) << output_index << ": " << de.asset_id << " + " << asset_blinding_mask << " x X", LOG_LEVEL_0);
+ //LOG_PRINT_CYAN(" == " << blinded_asset_id, LOG_LEVEL_0);
+ //LOG_PRINT_CYAN("OUT " << std::setw(2) << output_index << ": " << crypto::scalar_t(de.amount) << " x " << blinded_asset_id << " + " << amount_blinding_mask << " x G", LOG_LEVEL_0);
+ //LOG_PRINT_CYAN(" == " << amount_commitment << ", x 1/8 == " << out.amount_commitment, LOG_LEVEL_0);
if (de.addr.front().is_auditable())
out.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX; // override mix_attr to 1 for auditable target addresses
@@ -956,6 +1195,7 @@ namespace currency
{
// create tx_out_bare, this section can be removed after HF4
CHECK_AND_ASSERT_MES(de.addr.size() == 1 || (de.addr.size() > 1 && de.minimum_sigs <= de.addr.size()), false, "Invalid destination entry: amount: " << de.amount << " minimum_sigs: " << de.minimum_sigs << " addr.size(): " << de.addr.size());
+ CHECK_AND_ASSERT_MES(de.asset_id == currency::native_coin_asset_id, false, "assets are not allowed prior to HF4");
std::vector target_keys;
target_keys.reserve(de.addr.size());
@@ -1602,10 +1842,10 @@ namespace currency
};
//--------------------------------------------------------------------------------
bool generate_ZC_sig(const crypto::hash& tx_hash_for_signature, size_t input_index, const tx_source_entry& se, const input_generation_context_data& in_context,
- const account_keys& sender_account_keys, const crypto::scalar_t& blinding_masks_sum, const uint64_t tx_flags, crypto::scalar_t& local_blinding_masks_sum, transaction& tx, bool last_output)
+ const account_keys& sender_account_keys, const uint64_t tx_flags, outputs_generation_context& ogc, transaction& tx, bool last_output)
{
bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey;
- CHECK_AND_ASSERT_MES(se.is_zarcanum(), false, "sources contains a non-zarcanum input");
+ CHECK_AND_ASSERT_MES(se.is_zc(), false, "sources contains a non-zc input");
CHECK_AND_ASSERT_MES(input_index < tx.vin.size(), false, "input_index (" << input_index << ") is out-of-bounds, vin.size = " << tx.vin.size());
CHECK_AND_ASSERT_MES(tx.vin[input_index].type() == typeid(txin_zc_input), false, "Unexpected type of input #" << input_index);
@@ -1616,30 +1856,47 @@ namespace currency
if (watch_only_mode)
return true; // in this mode just append empty signatures
+ crypto::point_t asset_id_pt(se.asset_id);
+ crypto::point_t source_blinded_asset_id = asset_id_pt + se.real_out_asset_id_blinding_mask * crypto::c_point_X; // T_i = H_i + r_i * X
+ CHECK_AND_ASSERT_MES(crypto::point_t(in_context.outputs[in_context.real_out_index].blinded_asset_id).modify_mul8() == source_blinded_asset_id, false, "real output blinded asset id check failed");
+ ogc.real_zc_ins_asset_ids.emplace_back(asset_id_pt);
+
#ifndef NDEBUG
{
- crypto::point_t source_amount_commitment = crypto::c_scalar_1div8 * se.amount * crypto::c_point_H + crypto::c_scalar_1div8 * se.real_out_amount_blinding_mask * crypto::c_point_G;
- CHECK_AND_ASSERT_MES(in_context.outputs[in_context.real_out_index].amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed");
+ crypto::point_t source_amount_commitment = se.amount * source_blinded_asset_id + se.real_out_amount_blinding_mask * crypto::c_point_G;
+ CHECK_AND_ASSERT_MES(crypto::point_t(in_context.outputs[in_context.real_out_index].amount_commitment).modify_mul8() == source_amount_commitment, false, "real output amount commitment check failed");
}
#endif
- crypto::scalar_t blinding_mask = 0;
+ crypto::scalar_t pseudo_out_amount_blinding_mask = 0;
+ crypto::scalar_t pseudo_out_asset_id_blinding_mask = crypto::scalar_t::random();
if ((last_output && (tx_flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) == 0) || se.separately_signed_tx_complete)
{
// either normal tx or the last signature of consolidated tx -- in both cases we need to calculate non-random blinding mask for pseudo output commitment
- blinding_mask = blinding_masks_sum + local_blinding_masks_sum;
- // @#@ TODO additional check for the last iteration ?
+ pseudo_out_amount_blinding_mask = ogc.amount_blinding_masks_sum - ogc.pseudo_out_amount_blinding_masks_sum - ogc.ao_amount_blinding_mask; // A_1 - A^p_0 = (f_1 - f'_1) * G => f'_{i-1} = sum{y_j} - sum{f'_i}
}
else
{
- blinding_mask.make_random();
- local_blinding_masks_sum -= blinding_mask; // pseudo out masks are taken into account with negative sign
+ pseudo_out_amount_blinding_mask.make_random();
+ ogc.pseudo_out_amount_blinding_masks_sum += pseudo_out_amount_blinding_mask;
}
- crypto::point_t pseudo_out_amount_commitment = se.amount * crypto::c_point_H + blinding_mask * crypto::c_point_G;
- sig.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key();
+ crypto::point_t pseudo_out_blinded_asset_id = source_blinded_asset_id + pseudo_out_asset_id_blinding_mask * crypto::c_point_X; // T^p_i = T_i + r'_i * X
+ sig.pseudo_out_blinded_asset_id = (crypto::c_scalar_1div8 * pseudo_out_blinded_asset_id).to_public_key();
+ ogc.real_in_asset_id_blinding_mask_x_amount_sum += se.real_out_asset_id_blinding_mask * se.amount; // += r_i * a_i
+ ogc.pseudo_outs_blinded_asset_ids.emplace_back(pseudo_out_blinded_asset_id);
+ ogc.pseudo_outs_plus_real_out_blinding_masks.emplace_back(pseudo_out_asset_id_blinding_mask + se.real_out_asset_id_blinding_mask);
- // = two-layers ring signature data outline =
+ crypto::point_t pseudo_out_amount_commitment = se.amount * source_blinded_asset_id + pseudo_out_amount_blinding_mask * crypto::c_point_G; // A^p_i = a_i * T_i + f'_i * G
+ sig.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key();
+ ogc.pseudo_out_amount_commitments_sum += pseudo_out_amount_commitment;
+
+ //LOG_PRINT_CYAN("SBAID " << ": " << asset_id_pt << " + " << se.real_out_asset_id_blinding_mask << " x X", LOG_LEVEL_0);
+ //LOG_PRINT_CYAN(" == " << source_blinded_asset_id, LOG_LEVEL_0);
+ //LOG_PRINT_CYAN("POAM " << ": " << crypto::scalar_t(se.amount) << " x " << source_blinded_asset_id << " + " << pseudo_out_amount_blinding_mask << " x G", LOG_LEVEL_0);
+ //LOG_PRINT_CYAN(" == " << pseudo_out_amount_commitment << ", x 1/8 == " << sig.pseudo_out_amount_commitment, LOG_LEVEL_0);
+
+ // = three-layers ring signature data outline =
// (j in [0, ring_size-1])
// layer 0 ring
// se.outputs[j].stealth_address;
@@ -1651,13 +1908,20 @@ namespace currency
// layer 1 ring
// crypto::point_t(se.outputs[j].amount_commitment) - pseudo_out_amount_commitment;
// layer 1 secret (with respect to G)
- // se.real_out_amount_blinding_mask - blinding_mask;
+ // se.real_out_amount_blinding_mask - pseudo_out_amount_blinding_mask;
+ //
+ // layer 2 ring
+ // crypto::point_t(se.outputs[j].blinded_asset_id) - pseudo_out_asset_id;
+ // layer 2 secret (with respect to X)
+ // -pseudo_out_asset_id_blinding_mask;
- std::vector ring;
+ std::vector ring;
for(size_t j = 0; j < in_context.outputs.size(); ++j)
- ring.emplace_back(in_context.outputs[j].stealth_address, in_context.outputs[j].amount_commitment);
+ ring.emplace_back(in_context.outputs[j].stealth_address, in_context.outputs[j].amount_commitment, in_context.outputs[j].blinded_asset_id);
- return crypto::generate_CLSAG_GG(tx_hash_for_signature, ring, pseudo_out_amount_commitment, in.k_image, in_context.in_ephemeral.sec, se.real_out_amount_blinding_mask - blinding_mask, in_context.real_out_index, sig.clsags_gg);
+ return crypto::generate_CLSAG_GGX(tx_hash_for_signature, ring, pseudo_out_amount_commitment, pseudo_out_blinded_asset_id, in.k_image, in_context.in_ephemeral.sec,
+ se.real_out_amount_blinding_mask - pseudo_out_amount_blinding_mask,
+ -pseudo_out_asset_id_blinding_mask, in_context.real_out_index, sig.clsags_ggx);
}
//--------------------------------------------------------------------------------
bool generate_NLSAG_sig(const crypto::hash& tx_hash_for_signature, const crypto::hash& tx_prefix_hash, size_t input_index, const tx_source_entry& src_entr,
@@ -1714,10 +1978,21 @@ namespace currency
return true;
}
-
- crypto::hash get_asset_id_from_descriptor(const asset_descriptor_base& adb)
+#define CRYPTO_HASH_ASSET_ID_ITERATIONS 1024
+ void calculate_asset_id(const crypto::public_key& asset_owner, crypto::point_t* p_result_point, crypto::public_key* p_result_pub_key)
{
- return get_hash_from_POD_objects(CRYPTO_HDS_ASSET_ID, adb.owner);
+ crypto::hash h = get_hash_from_POD_objects(CRYPTO_HDS_ASSET_ID, asset_owner);
+
+ // this hash function needs to be computationally expensive (s.e. the whitepaper)
+ for(uint64_t i = 0; i < CRYPTO_HASH_ASSET_ID_ITERATIONS; ++i)
+ h = get_hash_from_POD_objects(CRYPTO_HDS_ASSET_ID, h, i);
+
+ crypto::point_t local_point{};
+ if (!p_result_point)
+ p_result_point = &local_point;
+ *p_result_point = crypto::hash_helper_t::hp(&h, sizeof h);
+ if (p_result_pub_key)
+ p_result_point->to_public_key(*p_result_pub_key);
}
@@ -1736,7 +2011,7 @@ namespace currency
bool r = false;
transaction& tx = result.tx;
- crypto::secret_key& one_time_secret_key = result.one_time_key;
+ crypto::secret_key& one_time_tx_secret_key = result.one_time_key;
result.ftp = ftp;
CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS, false, "Too many outs (" << destinations.size() << ")! Tx can't be constructed.");
@@ -1767,7 +2042,7 @@ namespace currency
txkey = keypair::generate();
add_tx_pub_key_to_extra(tx, txkey.pub);
- one_time_secret_key = txkey.sec;
+ one_time_tx_secret_key = txkey.sec;
//add flags
etc_tx_flags16_t e = AUTO_VAL_INIT(e);
@@ -1781,7 +2056,7 @@ namespace currency
else
{
txkey.pub = get_tx_pub_key_from_extra(tx);
- txkey.sec = one_time_secret_key;
+ txkey.sec = one_time_tx_secret_key;
CHECK_AND_ASSERT_MES(txkey.pub != null_pkey && txkey.sec != null_skey, false, "In append mode both public and secret keys must be provided");
//separately encrypt attachments without putting extra
@@ -1803,34 +2078,17 @@ namespace currency
}
- uint64_t summary_inputs_money = 0;
-
- crypto::hash asset_id_for_destinations = currency::null_hash;
- asset_descriptor_operation* pado = nullptr;
- if (tx.version > TRANSACTION_VERSION_PRE_HF4)
- {
- pado = get_type_in_variant_container(tx.extra);
- if (pado)
- {
- crypto::secret_key stub = AUTO_VAL_INIT(stub);
- bool r = derive_key_pair_from_key_pair(sender_account_keys.account_address.spend_public_key, one_time_secret_key, stub, pado->descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY);
- CHECK_AND_ASSERT_MES(r, false, "Failed to derive_public_key_from_tx_and_account_pub_key()");
- //also assign this asset id to destinations
- asset_id_for_destinations = get_asset_id_from_descriptor(pado->descriptor);
- }
- }
-
-
+ //
+ // INs
+ //
+ uint64_t native_coins_input_sum = 0;
std::vector in_contexts;
-
- //we'll aggregate Zarcanum outs into one txin_zarcanum_inputs
- //txin_zarcanum_inputs ins_zc = AUTO_VAL_INIT(ins_zc);
std::vector inputs_mapping;
size_t current_index = 0;
inputs_mapping.resize(sources.size());
size_t input_starter_index = tx.vin.size();
- bool has_zc_inputs = false;
- //fill inputs NLSAG and Zarcanum
+ size_t zc_inputs_count = 0;
+ bool all_inputs_are_obviously_native_coins = true;
for (const tx_source_entry& src_entr : sources)
{
inputs_mapping[current_index] = current_index;
@@ -1838,12 +2096,13 @@ namespace currency
in_contexts.push_back(input_generation_context_data{});
input_generation_context_data& in_context = in_contexts.back();
+ // sort src_entr.outputs entries by global out index, put it to in_context.outputs, convert to relative gindices, and put new real out index into in_context.real_out_index
in_context.outputs = prepare_outputs_entries_for_key_offsets(src_entr.outputs, src_entr.real_output, in_context.real_out_index);
if(src_entr.is_multisig())
{//multisig input
txin_multisig input_multisig = AUTO_VAL_INIT(input_multisig);
- summary_inputs_money += input_multisig.amount = src_entr.amount;
+ input_multisig.amount = src_entr.amount;
input_multisig.multisig_out_id = src_entr.multisig_id;
input_multisig.sigs_count = src_entr.ms_sigs_count;
tx.vin.push_back(input_multisig);
@@ -1858,7 +2117,6 @@ namespace currency
LOG_ERROR("htlc in: wrong output src_entr.outputs.size() = " << src_entr.outputs.size());
return false;
}
- summary_inputs_money += src_entr.amount;
//key_derivation recv_derivation;
crypto::key_image img;
@@ -1889,9 +2147,6 @@ namespace currency
CHECK_AND_ASSERT_MES(in_context.real_out_index < in_context.outputs.size(), false,
"real_output index (" << in_context.real_out_index << ") greater than or equal to in_context.outputs.size()=" << in_context.outputs.size());
- summary_inputs_money += src_entr.amount;
-
- //key_derivation recv_derivation;
crypto::key_image img;
if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_context.in_ephemeral, img))
return false;
@@ -1912,19 +2167,13 @@ namespace currency
//TODO: Might need some refactoring since this scheme is not the clearest one(did it this way for now to keep less changes to not broke anything)
//potentially this approach might help to support htlc and multisig without making to complicated code
- if (src_entr.is_zarcanum())
+ if (src_entr.is_zc())
{
- has_zc_inputs = true;
+ ++zc_inputs_count;
txin_zc_input zc_in = AUTO_VAL_INIT(zc_in);
zc_in.k_image = img;
zc_in.key_offsets = std::move(key_offsets);
- //TEMPORARY
- if (src_entr.asset_id != currency::null_hash)
- {
- zc_in.etc_details.push_back(open_asset_id{ src_entr.asset_id });
- }
tx.vin.push_back(zc_in);
-
}
else
{
@@ -1935,65 +2184,101 @@ namespace currency
tx.vin.push_back(input_to_key);
}
}
- }
- uint64_t amount_of_assets = 0;
- std::vector shuffled_dsts(destinations);
- if (asset_id_for_destinations != currency::null_hash)
- {
- //must be asset publication
- for (auto& item : shuffled_dsts)
+ if (src_entr.is_native_coin())
{
- if (item.asset_id == currency::ffff_hash)
+ native_coins_input_sum += src_entr.amount;
+ }
+ else
+ {
+ // if at least one decoy output of a ZC input has a non-explicit asset id, then we can't say that all inputs are obviously native coins
+ for(const tx_source_entry::output_entry& oe : in_context.outputs)
{
- item.asset_id = asset_id_for_destinations;
- amount_of_assets += item.amount;
+ if (crypto::point_t(oe.blinded_asset_id).modify_mul8() != currency::native_coin_asset_id_pt)
+ {
+ all_inputs_are_obviously_native_coins = false;
+ break;
+ }
}
}
- CHECK_AND_ASSERT_MES(pado, false, "pado is null ??");
- pado->descriptor.current_supply = amount_of_assets;
- //TODO: temporary
- summary_inputs_money += amount_of_assets;
}
+ bool has_non_zc_inputs = zc_inputs_count != sources.size(); // TODO @#@# reconsider this for consilidated txs
+
+ //
+ // OUTs
+ //
+ std::vector shuffled_dsts(destinations);
+ size_t outputs_to_be_constructed = shuffled_dsts.size();
+ outputs_generation_context outs_gen_context(zc_inputs_count, outputs_to_be_constructed); // auxiliary data for each output
+ // ASSET oprations handling
+ if (tx.version > TRANSACTION_VERSION_PRE_HF4)
+ {
+ asset_descriptor_operation* pado = nullptr;
+ pado = get_type_in_variant_container(tx.extra);
+ if (pado)
+ {
+ // only operation register is supported atm
+ CHECK_AND_ASSERT_MES(pado->operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER, false, "unsupported asset operation: " << (int)pado->operation_type);
+ crypto::secret_key asset_control_key{};
+ bool r = derive_key_pair_from_key_pair(sender_account_keys.account_address.spend_public_key, one_time_tx_secret_key, asset_control_key, pado->descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY);
+ CHECK_AND_ASSERT_MES(r, false, "derive_key_pair_from_key_pair failed");
+
+ calculate_asset_id(pado->descriptor.owner, &outs_gen_context.ao_asset_id_pt, &outs_gen_context.ao_asset_id);
+
+ // calculate amount blinding mask
+ outs_gen_context.ao_amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_ASSET_CONTROL_ABM, asset_control_key);
+
+ // set correct asset_id to the corresponding destination entries
+ uint64_t amount_of_emitted_asset = 0;
+ for (auto& item : shuffled_dsts)
+ {
+ if (item.asset_id == currency::ffff_pkey)
+ {
+ item.asset_id = outs_gen_context.ao_asset_id; // set calculated asset_id to the asset's outputs, if this asset is being emitted within this tx
+ amount_of_emitted_asset += item.amount;
+ }
+ }
+ pado->descriptor.current_supply = amount_of_emitted_asset; // TODO: consider setting current_supply beforehand, not setting it hear in ad-hoc manner -- sowle
+
+ outs_gen_context.ao_amount_commitment = amount_of_emitted_asset * outs_gen_context.ao_asset_id_pt + outs_gen_context.ao_amount_blinding_mask * crypto::c_point_G;
+ pado->opt_amount_commitment = (crypto::c_scalar_1div8 * outs_gen_context.ao_amount_commitment).to_public_key();
+ //LOG_PRINT_CYAN("AO " << ": " << crypto::scalar_t(amount_of_emitted_asset) << " x " << outs_gen_context.ao_asset_id_pt << " + " << outs_gen_context.ao_amount_blinding_mask << " x G", LOG_LEVEL_0);
+ //LOG_PRINT_CYAN(" == " << outs_gen_context.ao_amount_commitment << ", x 1/8 == " << pado->opt_amount_commitment.get(), LOG_LEVEL_0);
+ }
+ }
// "Shuffle" outs
if (shuffle)
std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; });
-
// TODO: consider "Shuffle" inputs
- uint64_t summary_outs_money = 0;
- //fill outputs
+ // construct outputs
+ uint64_t native_coins_output_sum = 0;
size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1
uint64_t range_proof_start_index = output_index;
std::set deriv_cache;
- crypto::scalar_vec_t blinding_masks(tx.vout.size() + destinations.size()); // vector of secret blinging masks for each output. For range proof generation
- crypto::scalar_vec_t amounts(tx.vout.size() + destinations.size()); // vector of amounts, converted to scalars. For ranage proof generation
- crypto::scalar_t blinding_masks_sum = 0;
- for(const tx_destination_entry& dst_entr : shuffled_dsts)
+ for(size_t j = 0; j < outputs_to_be_constructed; ++j, ++output_index)
{
+ tx_destination_entry& dst_entr = shuffled_dsts[j];
+ if (all_inputs_are_obviously_native_coins && outs_gen_context.ao_asset_id == currency::null_pkey)
+ dst_entr.explicit_native_asset_id = true; // all inputs are obviously native coins -- all outputs must have explicit asset ids (unless there's an asset emission)
+
CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); // <<-- TODO @#@# consider removing this check
- r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, blinding_masks[output_index], result, tx_outs_attr);
+ r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys,
+ outs_gen_context.asset_id_blinding_masks[j], outs_gen_context.amount_blinding_masks[j],
+ outs_gen_context.blinded_asset_ids[j], outs_gen_context.amount_commitments[j], result, tx_outs_attr);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct tx out");
- amounts[output_index - range_proof_start_index] = dst_entr.amount;
- summary_outs_money += dst_entr.amount;
- blinding_masks_sum += blinding_masks[output_index];
- output_index++;
+ outs_gen_context.amounts[j] = dst_entr.amount;
+ outs_gen_context.asset_ids[j] = crypto::point_t(dst_entr.asset_id);
+ outs_gen_context.asset_id_blinding_mask_x_amount_sum += outs_gen_context.asset_id_blinding_masks[j] * dst_entr.amount;
+ outs_gen_context.amount_blinding_masks_sum += outs_gen_context.amount_blinding_masks[j];
+ outs_gen_context.amount_commitments_sum += outs_gen_context.amount_commitments[j];
+ if (dst_entr.is_native_coin())
+ native_coins_output_sum += dst_entr.amount;
}
- //check money
- if (!(flags&TX_FLAG_SIGNATURE_MODE_SEPARATE))
- {
- if (summary_outs_money > summary_inputs_money)
- {
- LOG_ERROR("Transaction inputs money (" << print_money_brief(summary_inputs_money) << ") is less than outputs money (" << print_money_brief(summary_outs_money) << ")");
- return false;
- }
- }
-
-
//process offers and put there offers derived keys
uint64_t att_count = 0;
for (auto& o : tx.attachment)
@@ -2005,7 +2290,7 @@ namespace currency
{
CHECK_AND_ASSERT_MES(tsa.security.size() == 1, false, "Wrong tsa.security.size() = " << tsa.security.size());
- r = derive_public_key_from_target_address(sender_account_keys.account_address, one_time_secret_key, att_count, tsa.security.back());
+ r = derive_public_key_from_target_address(sender_account_keys.account_address, one_time_tx_secret_key, att_count, tsa.security.back());
CHECK_AND_ASSERT_MES(r, false, "Failed to derive_public_key_from_target_address");
}
att_count++;
@@ -2022,27 +2307,14 @@ namespace currency
//2 sort the inputs in given range
std::sort(tx.vin.begin() + input_starter_index, tx.vin.end(), less_txin_v);
- }
-
- if (tx.version > TRANSACTION_VERSION_PRE_HF4)
- {
- // add range proofs
- currency::zc_outs_range_proof range_proofs = AUTO_VAL_INIT(range_proofs);
- bool r = generate_zc_outs_range_proof(range_proof_start_index, amounts.size(), amounts, blinding_masks, tx.vout, range_proofs);
- CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()");
- tx.attachment.push_back(range_proofs);
// add explicit fee info
- r = add_tx_fee_amount_to_extra(tx, summary_inputs_money - summary_outs_money);
+ r = add_tx_fee_amount_to_extra(tx, native_coins_input_sum - native_coins_output_sum);
CHECK_AND_ASSERT_MES(r, false, "add_tx_fee_amount_to_extra failed");
-
- if (!has_zc_inputs)
- {
- r = generate_tx_balance_proof(tx, blinding_masks_sum, amount_of_assets);
- CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
- }
}
+ // attachments container should be sealed by now
+
if (flags & TX_FLAG_SIGNATURE_MODE_SEPARATE)
{
// for separately signed tx each input has to contain information about corresponding outputs, extra entries and attachments
@@ -2064,14 +2336,15 @@ namespace currency
if (tx.attachment.size())
add_attachments_info_to_extra(tx.extra, tx.attachment);
}
+
+
//
- // generate ring signatures
+ // generate proofs and signatures
+ // (any changes made below should only affect the signatures/proofs and should not impact the prefix hash calculation)
//
- crypto::hash tx_prefix_hash;
- get_transaction_prefix_hash(tx, tx_prefix_hash);
- //size_t input_index = input_starter_index;
- //size_t in_context_index = 0;
- crypto::scalar_t local_blinding_masks_sum = 0; // ZC only
+ crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
+
+ // ring signatures (per-input proofs)
r = false;
for (size_t i_ = 0; i_ != sources.size(); i_++)
{
@@ -2082,12 +2355,11 @@ namespace currency
CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "prepare_prefix_hash_for_sign failed");
std::stringstream ss_ring_s;
- if (source_entry.is_zarcanum())
+ if (source_entry.is_zc())
{
// ZC
// blinding_masks_sum is supposed to be sum(mask of all tx output) - sum(masks of all pseudo out commitments)
- r = generate_ZC_sig(tx_hash_for_signature, i_ + input_starter_index, source_entry, in_contexts[i_mapped], sender_account_keys, blinding_masks_sum, flags,
- local_blinding_masks_sum, tx, i_ + 1 == sources.size());
+ r = generate_ZC_sig(tx_hash_for_signature, i_ + input_starter_index, source_entry, in_contexts[i_mapped], sender_account_keys, flags, outs_gen_context, tx, i_ + 1 == sources.size());
CHECK_AND_ASSERT_MES(r, false, "generate_ZC_sigs failed");
}
else
@@ -2100,6 +2372,42 @@ namespace currency
LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3);
}
+ //
+ // proofs (transaction-wise, not pre-input)
+ //
+ if (tx.version > TRANSACTION_VERSION_PRE_HF4)
+ {
+ // asset surjection proof
+ currency::zc_asset_surjection_proof asp{};
+ bool r = generate_asset_surjection_proof(tx_prefix_hash, has_non_zc_inputs, outs_gen_context, asp);
+ CHECK_AND_ASSERT_MES(r, false, "generete_asset_surjection_proof failed");
+ tx.proofs.emplace_back(std::move(asp));
+
+ // range proofs
+ currency::zc_outs_range_proof range_proofs{};
+ r = generate_zc_outs_range_proof(tx_prefix_hash, range_proof_start_index, outs_gen_context, tx.vout, range_proofs);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()");
+ tx.proofs.emplace_back(std::move(range_proofs));
+
+ // balance proof
+ currency::zc_balance_proof balance_proof{};
+ r = generate_tx_balance_proof(tx, tx_prefix_hash, outs_gen_context, 0, balance_proof);
+ CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
+ tx.proofs.emplace_back(std::move(balance_proof));
+
+ // asset operation proof (if necessary)
+ if (outs_gen_context.ao_asset_id != currency::null_pkey)
+ {
+ // construct the asset operation proof
+ // TODO @#@# add support for hidden supply
+ crypto::signature aop_g_sig{};
+ crypto::generate_signature(tx_prefix_hash, crypto::point_t(outs_gen_context.ao_amount_blinding_mask * crypto::c_point_G).to_public_key(), outs_gen_context.ao_amount_blinding_mask, aop_g_sig);
+ asset_operation_proof aop{};
+ aop.opt_amount_commitment_g_proof = aop_g_sig;
+ tx.proofs.emplace_back(std::move(aop));
+ }
+ }
+
//size_t prefix_size = get_object_blobsize(static_cast(tx));
//size_t full_blob_size = t_serializable_object_to_blob(tx).size();
//size_t estimated_blob_size = get_object_blobsize(tx);
@@ -2378,12 +2686,16 @@ namespace currency
VARIANT_SWITCH_END();
}
VARIANT_CASE_CONST(tx_out_zarcanum, o)
- if (!check_key(o.amount_commitment))
+ {
+ if (!check_key(o.stealth_address))
return false;
if (!check_key(o.concealing_point))
return false;
- if (!check_key(o.stealth_address))
+ if (!check_key(o.amount_commitment))
return false;
+ if (!check_key(o.blinded_asset_id))
+ return false;
+ }
VARIANT_SWITCH_END();
}
return true;
@@ -2471,6 +2783,7 @@ namespace currency
return res;
}
//---------------------------------------------------------------
+ // out_key.key =?= Hs(derivation || output_index) * G + addr.spend_public_key
bool is_out_to_acc(const account_public_address& addr, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index)
{
crypto::public_key pk;
@@ -2490,7 +2803,8 @@ namespace currency
return true;
}
- bool is_out_to_acc(const account_public_address& addr, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::scalar_t& blinding_mask)
+ bool is_out_to_acc(const account_public_address& addr, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::public_key& decoded_asset_id,
+ crypto::scalar_t& amount_blinding_mask, crypto::scalar_t& asset_id_blinding_mask)
{
crypto::scalar_t h; // = crypto::hash_helper_t::hs(reinterpret_cast(derivation), output_index); // h = Hs(8 * r * V, i)
crypto::derivation_to_scalar(derivation, output_index, h.as_secret_key()); // h = Hs(8 * r * V, i)
@@ -2506,23 +2820,35 @@ namespace currency
crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h);
decoded_amount = zo.encrypted_amount ^ amount_mask.m_u64[0];
- blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, h)
+ amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_BLINDING_MASK, h); // f = Hs(domain_sep, h)
- crypto::point_t A_prime;
- A_prime.assign_mul_plus_G(decoded_amount, crypto::c_point_H, blinding_mask); // A' * 8 =? a * H + f * G
+ crypto::point_t blinded_asset_id = crypto::point_t(zo.blinded_asset_id).modify_mul8();
+ crypto::point_t A_prime = decoded_amount * blinded_asset_id + amount_blinding_mask * crypto::c_point_G; // A' * 8 =? a * T + f * G
if (A_prime != crypto::point_t(zo.amount_commitment).modify_mul8())
return false;
+ if (blinded_asset_id == currency::native_coin_asset_id_pt)
+ {
+ asset_id_blinding_mask = 0;
+ decoded_asset_id = currency::native_coin_asset_id;
+ }
+ else
+ {
+ asset_id_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
+ crypto::point_t asset_id = blinded_asset_id - asset_id_blinding_mask * crypto::c_point_X; // H = T - s * X
+ decoded_asset_id = asset_id.to_public_key();
+ }
+
return true;
- }
+ }
//---------------------------------------------------------------
- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation)
+ bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& sum_of_native_outs, crypto::key_derivation& derivation)
{
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
if (null_pkey == tx_pub_key)
return false;
- return lookup_acc_outs(acc, tx, get_tx_pub_key_from_extra(tx), outs, money_transfered, derivation);
+ return lookup_acc_outs(acc, tx, get_tx_pub_key_from_extra(tx), outs, sum_of_native_outs, derivation);
}
//---------------------------------------------------------------
bool check_tx_derivation_hint(const transaction& tx, const crypto::key_derivation& derivation)
@@ -2571,22 +2897,22 @@ namespace currency
return true;
}
//---------------------------------------------------------------
- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation)
+ bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& sum_of_native_outs, crypto::key_derivation& derivation)
{
std::list htlc_info_list;
- return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered, derivation, htlc_info_list);
+ return lookup_acc_outs(acc, tx, tx_pub_key, outs, sum_of_native_outs, derivation, htlc_info_list);
}
//---------------------------------------------------------------
- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation, std::list& htlc_info_list)
+ bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& sum_of_native_outs, crypto::key_derivation& derivation, std::list& htlc_info_list)
{
- money_transfered = 0;
+ sum_of_native_outs = 0;
bool r = generate_key_derivation(tx_pub_key, acc.view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "unable to generate derivation from tx_pub = " << tx_pub_key << " * view_sec, invalid tx_pub?");
if (is_coinbase(tx) && get_block_height(tx) == 0 && tx_pub_key == ggenesis_tx_pub_key)
{
//genesis coinbase
- return lookup_acc_outs_genesis(acc, tx, tx_pub_key, outs, money_transfered, derivation);
+ return lookup_acc_outs_genesis(acc, tx, tx_pub_key, outs, sum_of_native_outs, derivation);
}
if (!check_tx_derivation_hint(tx, derivation))
@@ -2603,13 +2929,13 @@ namespace currency
if (is_out_to_acc(acc.account_address, t, derivation, output_index))
{
outs.emplace_back(output_index, o.amount);
- money_transfered += o.amount;
+ sum_of_native_outs += o.amount;
}
VARIANT_CASE_CONST(txout_multisig, t)
if (is_out_to_acc(acc.account_address, t, derivation, output_index))
{
outs.emplace_back(output_index, o.amount); // TODO: @#@# consider this
- //don't cout this money
+ //don't cout this money in sum_of_native_outs
}
VARIANT_CASE_CONST(txout_htlc, htlc)
htlc_info hi = AUTO_VAL_INIT(hi);
@@ -2623,6 +2949,11 @@ namespace currency
hi.hltc_our_out_is_before_expiration = false;
htlc_info_list.push_back(hi);
}
+ else
+ {
+ LOG_ERROR("lookup_acc_outs: handling txout_htlc went wrong, output_index: " << output_index);
+ return false;
+ }
outs.emplace_back(output_index, o.amount);
VARIANT_CASE_OTHER()
LOG_ERROR("Wrong type at lookup_acc_outs, unexpected type is: " << o.target.type().name());
@@ -2630,18 +2961,19 @@ namespace currency
VARIANT_SWITCH_END();
}
VARIANT_CASE_CONST(tx_out_zarcanum, zo)
+ {
uint64_t amount = 0;
- crypto::scalar_t blinding_mask = 0;
- if (is_out_to_acc(acc.account_address, zo, derivation, output_index, amount, blinding_mask))
+ crypto::public_key asset_id{};
+ crypto::scalar_t amount_blinding_mask = 0, asset_id_blinding_mask = 0;
+ if (is_out_to_acc(acc.account_address, zo, derivation, output_index, amount, asset_id, amount_blinding_mask, asset_id_blinding_mask))
{
- outs.emplace_back(output_index, amount, blinding_mask);
- open_asset_id v = AUTO_VAL_INIT(v);
- if (get_type_in_variant_container(zo.etc_details, v))
- {
- outs.back().asset_id = v.asset_id;
- }
- money_transfered += amount;
+ crypto::point_t asset_id_pt = crypto::point_t(zo.blinded_asset_id).modify_mul8() - asset_id_blinding_mask * crypto::c_point_X;
+ crypto::public_key asset_id = asset_id_pt.to_public_key();
+ outs.emplace_back(output_index, amount, amount_blinding_mask, asset_id_blinding_mask, asset_id);
+ if (asset_id == currency::native_coin_asset_id)
+ sum_of_native_outs += amount;
}
+ }
VARIANT_SWITCH_END();
output_index++;
}
@@ -3379,7 +3711,8 @@ namespace currency
bool operator()(const zc_outs_range_proof& rp)
{
tv.type = "zc_outs_range_proof";
- tv.short_view = "outputs_count = " + std::to_string(rp.outputs_count);
+ // TODO @#@#
+ //tv.short_view = "outputs_count = " + std::to_string(rp.outputs_count);
return true;
}
bool operator()(const zc_balance_proof& bp)
@@ -3965,18 +4298,21 @@ namespace currency
bool operator ==(const currency::void_sig& a, const currency::void_sig& b)
{
//@#@
+ ASSERT_MES_AND_THROW("not implemented yet");
return false;
}
//--------------------------------------------------------------------------------
bool operator ==(const currency::ZC_sig& a, const currency::ZC_sig& b)
{
//@#@ TODO
+ ASSERT_MES_AND_THROW("not implemented yet");
return false;
}
//--------------------------------------------------------------------------------
bool operator ==(const currency::zarcanum_sig& a, const currency::zarcanum_sig& b)
{
//@#@ TODO
+ ASSERT_MES_AND_THROW("not implemented yet");
return false;
}
//--------------------------------------------------------------------------------
@@ -3985,23 +4321,6 @@ namespace currency
return a.n == b.n && a.tx_id == b.tx_id;
}
//--------------------------------------------------------------------------------
- bool verify_multiple_zc_outs_range_proofs(const std::vector& range_proofs)
- {
- if (range_proofs.empty())
- return true;
-
- std::vector sigs;
- sigs.reserve(range_proofs.size());
- for(auto& el : range_proofs)
- sigs.emplace_back(el.range_proof.bpp, el.amount_commitments);
-
- uint8_t err = 0;
- bool r = crypto::bpp_verify<>(sigs, &err);
- CHECK_AND_ASSERT_MES(r, false, "bpp_verify failed with error " << (int)err);
-
- return true;
- }
- //--------------------------------------------------------------------------------
boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point,
diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h
index db3f690a..9ffbf6f3 100644
--- a/src/currency_core/currency_format_utils.h
+++ b/src/currency_core/currency_format_utils.h
@@ -53,15 +53,6 @@
namespace currency
{
- bool operator ==(const currency::transaction& a, const currency::transaction& b);
- bool operator ==(const currency::block& a, const currency::block& b);
- bool operator ==(const currency::extra_attachment_info& a, const currency::extra_attachment_info& b);
- bool operator ==(const currency::NLSAG_sig& a, const currency::NLSAG_sig& b);
- bool operator ==(const currency::void_sig& a, const currency::void_sig& b);
- bool operator ==(const currency::ZC_sig& a, const currency::ZC_sig& b);
- bool operator ==(const currency::zarcanum_sig& a, const currency::zarcanum_sig& b);
- bool operator ==(const currency::ref_by_id& a, const currency::ref_by_id& b);
-
typedef boost::multiprecision::uint128_t uint128_tl;
@@ -205,19 +196,25 @@ namespace currency
: index(index)
, amount(amount)
{}
- wallet_out_info(size_t index, uint64_t amount, const crypto::scalar_t& blinding_mask)
+ wallet_out_info(size_t index, uint64_t amount, const crypto::scalar_t& amount_blinding_mask, const crypto::scalar_t& asset_id_blinding_mask, const crypto::public_key& asset_id)
: index(index)
, amount(amount)
- , blinding_mask(blinding_mask)
+ , amount_blinding_mask(amount_blinding_mask)
+ , asset_id_blinding_mask(asset_id_blinding_mask)
+ , asset_id(asset_id)
{}
- size_t index = SIZE_MAX;
- uint64_t amount = 0;
- crypto::scalar_t blinding_mask = 0;
- crypto::hash asset_id = currency::null_hash;
+ size_t index = SIZE_MAX;
+ uint64_t amount = 0;
+ crypto::scalar_t amount_blinding_mask = 0;
+ crypto::scalar_t asset_id_blinding_mask = 0;
+ crypto::public_key asset_id = currency::native_coin_asset_id; // use point_t instead as this is for internal use only?
+
+ bool is_native_coin() const { return asset_id == currency::native_coin_asset_id; }
};
+ // TODO @#@# consider refactoring to eliminate redundant coping and to imporve performance
struct zc_outs_range_proofs_with_commitments
{
zc_outs_range_proofs_with_commitments(const zc_outs_range_proof& range_proof, const std::vector& amount_commitments)
@@ -232,7 +229,14 @@ namespace currency
};
bool verify_multiple_zc_outs_range_proofs(const std::vector& range_proofs);
- bool check_tx_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0);
+ bool generate_asset_surjection_proof(const crypto::hash& context_hash, bool has_non_zc_inputs, outputs_generation_context& ogc, zc_asset_surjection_proof& result);
+ bool verify_asset_surjection_proof(const transaction& tx, const crypto::hash& tx_id);
+ bool generate_tx_balance_proof(const transaction &tx, const crypto::hash& tx_id, const outputs_generation_context& ogc, uint64_t block_reward_for_miner_tx, zc_balance_proof& proof);
+ bool generate_zc_outs_range_proof(const crypto::hash& context_hash, size_t out_index_start, const outputs_generation_context& outs_gen_context,
+ const std::vector& vouts, zc_outs_range_proof& result);
+ bool check_tx_bare_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0);
+ bool check_tx_balance(const transaction& tx, const crypto::hash& tx_id, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0);
+ bool validate_asset_operation(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, crypto::public_key& asset_id);
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins,
size_t current_block_size,
@@ -241,16 +245,17 @@ namespace currency
const account_public_address &stakeholder_address,
transaction& tx,
uint64_t tx_version,
- const blobdata& extra_nonce = blobdata(),
- size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
- bool pos = false,
- const pos_entry& pe = pos_entry(),
- crypto::scalar_t* blinding_masks_sum_ptr = nullptr,
- const keypair* tx_one_time_key_to_use = nullptr);
+ const blobdata& extra_nonce = blobdata(),
+ size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
+ bool pos = false,
+ const pos_entry& pe = pos_entry(),
+ outputs_generation_context* ogc_ptr = nullptr,
+ const keypair* tx_one_time_key_to_use = nullptr);
//---------------------------------------------------------------
uint64_t get_string_uint64_hash(const std::string& str);
- bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, crypto::scalar_t& out_blinding_mask, finalized_tx& result, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED);
+ bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, crypto::scalar_t& asset_blinding_mask, crypto::scalar_t& amount_blinding_mask, crypto::point_t& blinded_asset_id, crypto::point_t& amount_commitment, finalized_tx& result, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED);
bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED);
+
bool validate_alias_name(const std::string& al);
bool validate_password(const std::string& password);
void get_attachment_extra_info_details(const std::vector& attachment, extra_attachment_info& eai);
@@ -293,7 +298,8 @@ namespace currency
uint64_t get_tx_version(uint64_t tx_expected_block_height, const hard_forks_descriptor& hfd); // returns tx version based on the height of the block where the transaction is expected to be
bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& param, finalized_tx& result);
- crypto::hash get_asset_id_from_descriptor(const asset_descriptor_base& adb);
+ void calculate_asset_id(const crypto::public_key& asset_owner, crypto::point_t* p_result_point, crypto::public_key* p_result_pub_key);
+
bool sign_multisig_input_in_tx(currency::transaction& tx, size_t ms_input_index, const currency::account_keys& keys, const currency::transaction& source_tx, bool *p_is_input_fully_signed = nullptr);
@@ -311,10 +317,10 @@ namespace currency
crypto::hash get_multisig_out_id(const transaction& tx, size_t n);
bool is_out_to_acc(const account_public_address& addr, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index);
bool is_out_to_acc(const account_public_address& addr, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index);
- bool is_out_to_acc(const account_public_address& addr, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::scalar_t& blinding_mask);
- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation);
- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation, std::list& htlc_info_list);
- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation);
+ bool is_out_to_acc(const account_public_address& addr, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::public_key& decoded_asset_id, crypto::scalar_t& amount_blinding_mask, crypto::scalar_t& asset_id_blinding_mask);
+ bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& sum_of_native_outs, crypto::key_derivation& derivation);
+ bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& sum_of_native_outs, crypto::key_derivation& derivation, std::list& htlc_info_list);
+ bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& sum_of_native_outs, crypto::key_derivation& derivation);
bool get_tx_fee(const transaction& tx, uint64_t & fee);
uint64_t get_tx_fee(const transaction& tx);
bool derive_ephemeral_key_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral);
@@ -602,36 +608,6 @@ namespace currency
return get_or_add_field_to_variant_vector(extra);
}
//---------------------------------------------------------------
- template
- void update_or_add_field_to_extra(std::vector& variant_container, const variant_type_t& v)
- {
- for (auto& ev : variant_container)
- {
- if (ev.type() == typeid(variant_type_t))
- {
- boost::get(ev) = v;
- return;
- }
- }
- variant_container.push_back(v);
- }
- //---------------------------------------------------------------
- template
- void remove_field_of_type_from_extra(std::vector& variant_container)
- {
- for (size_t i = 0; i != variant_container.size();)
- {
- if (variant_container[i].type() == typeid(variant_type_t))
- {
- variant_container.erase(variant_container.begin()+i);
- }
- else
- {
- i++;
- }
- }
- }
- //---------------------------------------------------------------
template
bool get_payment_id_from_tx(const t_container& att, std::string& payment_id)
{
diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp
index c0e06a3b..e7071e0c 100644
--- a/src/currency_core/currency_format_utils_transactions.cpp
+++ b/src/currency_core/currency_format_utils_transactions.cpp
@@ -33,11 +33,6 @@ namespace currency
return expiration_time <= expiration_ts_median + TX_EXPIRATION_MEDIAN_SHIFT;
}
//---------------------------------------------------------------
-
-
-
-
-
uint64_t get_burned_amount(const transaction& tx)
{
uint64_t res = 0;
@@ -51,7 +46,7 @@ namespace currency
res += o.amount;
}
VARIANT_CASE_CONST(tx_out_zarcanum, o)
- //@#@
+ //@#@# TODO obtain info about public burn of native coins in ZC outputs
VARIANT_CASE_THROW_ON_OTHER();
VARIANT_SWITCH_END();
}
@@ -324,6 +319,25 @@ namespace currency
return true;
}
//---------------------------------------------------------------
+ bool is_asset_emitting_transaction(const transaction& tx, asset_descriptor_operation* p_ado /* = nullptr */)
+ {
+ if (tx.version <= TRANSACTION_VERSION_PRE_HF4)
+ return false;
+
+ asset_descriptor_operation local_ado{};
+ if (p_ado == nullptr)
+ p_ado = &local_ado;
+
+ if (!get_type_in_variant_container(tx.extra, *p_ado))
+ return false;
+
+ // TODO @#@# change to ASSET_DESCRIPTOR_OPERATION_EMMIT !
+ if (p_ado->operation_type != ASSET_DESCRIPTOR_OPERATION_REGISTER)
+ return false;
+
+ return true;
+ }
+ //---------------------------------------------------------------
// Prepapres vector of output_entry to be used in key_offsets in a transaction input:
// 1) sort all entries by gindex (while moving all ref_by_id to the end, keeping they relative order)
// 2) convert absolute global indices to relative key_offsets
diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h
index d123a54a..a2ba6080 100644
--- a/src/currency_core/currency_format_utils_transactions.h
+++ b/src/currency_core/currency_format_utils_transactions.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2022 Zano Project
+// Copyright (c) 2018-2023 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -22,13 +22,14 @@ namespace currency
output_entry(const output_entry &) = default;
output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address)
: out_reference(out_reference), stealth_address(stealth_address), concealing_point(null_pkey), amount_commitment(null_pkey) {}
- output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address, const crypto::public_key& concealing_point, const crypto::public_key& amount_commitment)
- : out_reference(out_reference), stealth_address(stealth_address), concealing_point(concealing_point), amount_commitment(amount_commitment) {}
+ output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address, const crypto::public_key& concealing_point, const crypto::public_key& amount_commitment, const crypto::public_key& blinded_asset_id)
+ : out_reference(out_reference), stealth_address(stealth_address), concealing_point(concealing_point), amount_commitment(amount_commitment), blinded_asset_id(blinded_asset_id) {}
txout_ref_v out_reference; // either global output index or ref_by_id
crypto::public_key stealth_address; // a.k.a output's one-time public key
- crypto::public_key concealing_point; // only for zarcaum outputs
- crypto::public_key amount_commitment; // only for zarcaum outputs
+ crypto::public_key concealing_point; // only for ZC outputs
+ crypto::public_key amount_commitment; // only for ZC outputs
+ crypto::public_key blinded_asset_id; // only for ZC outputs
bool operator==(const output_entry& rhs) const { return out_reference == rhs.out_reference; } // used in prepare_outputs_entries_for_key_offsets, it's okay to do partially comparison
@@ -37,6 +38,7 @@ namespace currency
FIELD(stealth_address)
FIELD(concealing_point)
FIELD(amount_commitment)
+ FIELD(blinded_asset_id)
END_SERIALIZE()
};
@@ -45,7 +47,8 @@ namespace currency
std::vector outputs;
uint64_t real_output = 0; //index in outputs vector of real output_entry
crypto::public_key real_out_tx_key = currency::null_pkey; //real output's transaction's public key
- crypto::scalar_t real_out_amount_blinding_mask; //blinding mask of real out's amount committment (only for zarcanum inputs, otherwise must be 0)
+ crypto::scalar_t real_out_amount_blinding_mask = 0; //blinding mask of real out's amount committment (only for ZC inputs, otherwise must be 0)
+ crypto::scalar_t real_out_asset_id_blinding_mask = 0; //blinding mask of real out's asset_od (only for ZC inputs, otherwise must be 0)
size_t real_output_in_tx_index = 0; //index in transaction outputs vector
uint64_t amount = 0; //money
uint64_t transfer_index = 0; //index in m_transfers
@@ -54,16 +57,18 @@ namespace currency
size_t ms_keys_count = 0; //if txin_multisig: must be equal to size of output's keys container
bool separately_signed_tx_complete = false; //for separately signed tx only: denotes the last source entry in complete tx to explicitly mark the final step of tx creation
std::string htlc_origin; //for htlc, specify origin
- crypto::hash asset_id = currency::null_hash; //asset id
+ crypto::public_key asset_id = currency::native_coin_asset_id; //asset id (not blinded, not premultiplied by 1/8) TODO @#@# consider changing to crypto::point_t
- bool is_multisig() const { return ms_sigs_count > 0; }
- bool is_zarcanum() const { return !real_out_amount_blinding_mask.is_zero(); }
+ bool is_multisig() const { return ms_sigs_count > 0; }
+ bool is_zc() const { return !real_out_amount_blinding_mask.is_zero(); }
+ bool is_native_coin() const { return asset_id == currency::native_coin_asset_id; }
BEGIN_SERIALIZE_OBJECT()
FIELD(outputs)
FIELD(real_output)
FIELD(real_out_tx_key)
FIELD(real_out_amount_blinding_mask)
+ FIELD(real_out_asset_id_blinding_mask)
FIELD(real_output_in_tx_index)
FIELD(amount)
FIELD(transfer_index)
@@ -91,21 +96,23 @@ namespace currency
struct tx_destination_entry
{
- uint64_t amount = 0; //money
- std::list addr; //destination address, in case of 1 address - txout_to_key, in case of more - txout_multisig
- size_t minimum_sigs = 0; //if txout_multisig: minimum signatures that are required to spend this output (minimum_sigs <= addr.size()) IF txout_to_key - not used
- uint64_t amount_to_provide = 0; //amount money that provided by initial creator of tx, used with partially created transactions
+ uint64_t amount = 0; // money
+ std::list addr; // destination address, in case of 1 address - txout_to_key, in case of more - txout_multisig
+ size_t minimum_sigs = 0; // if txout_multisig: minimum signatures that are required to spend this output (minimum_sigs <= addr.size()) IF txout_to_key - not used
+ uint64_t amount_to_provide = 0; // amount money that provided by initial creator of tx, used with partially created transactions
uint64_t unlock_time = 0;
- destination_option_htlc_out htlc_options; //htlc options
- crypto::hash asset_id = currency::null_hash;
-
+ destination_option_htlc_out htlc_options; // htlc options
+ crypto::public_key asset_id = currency::native_coin_asset_id; // not blinded, not premultiplied
+ bool explicit_native_asset_id = false;
tx_destination_entry() = default;
tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad) {}
- tx_destination_entry(uint64_t a, const account_public_address& ad, const crypto::hash& aid) : amount(a), addr(1, ad), asset_id(aid) {}
+ tx_destination_entry(uint64_t a, const account_public_address& ad, const crypto::public_key& aid) : amount(a), addr(1, ad), asset_id(aid) {}
tx_destination_entry(uint64_t a, const account_public_address& ad, uint64_t ut) : amount(a), addr(1, ad), unlock_time(ut) {}
tx_destination_entry(uint64_t a, const std::list& addr) : amount(a), addr(addr), minimum_sigs(addr.size()){}
- tx_destination_entry(uint64_t a, const std::list& addr, const crypto::hash& aid) : amount(a), addr(addr), minimum_sigs(addr.size()), asset_id(aid) {}
+ tx_destination_entry(uint64_t a, const std::list& addr, const crypto::public_key& aid) : amount(a), addr(addr), minimum_sigs(addr.size()), asset_id(aid) {}
+
+ bool is_native_coin() const { return asset_id == currency::native_coin_asset_id; }
BEGIN_SERIALIZE_OBJECT()
@@ -116,9 +123,40 @@ namespace currency
FIELD(unlock_time)
FIELD(htlc_options)
FIELD(asset_id)
+ FIELD(explicit_native_asset_id)
END_SERIALIZE()
};
-
+ //---------------------------------------------------------------
+ template