diff --git a/.gitmodules b/.gitmodules index 97a855bd..57896bbb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "contrib/jwt-cpp"] path = contrib/jwt-cpp url = https://github.com/Thalhammer/jwt-cpp.git +[submodule "contrib/bitcoin-secp256k1"] + path = contrib/bitcoin-secp256k1 + url = https://github.com/bitcoin-core/secp256k1.git diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index abfc4885..c1a5535f 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -5,6 +5,14 @@ add_subdirectory(zlib) add_subdirectory(db) add_subdirectory(ethereum) +option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." OFF) +option(SECP256K1_BUILD_TESTS "Build tests." OFF) +option(SECP256K1_BUILD_EXHAUSTIVE_TESTS "Build exhaustive tests." OFF) +option(SECP256K1_BUILD_CTIME_TESTS "Build constant-time tests." OFF) +option(SECP256K1_BUILD_EXAMPLES "Build examples." OFF) +set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) +add_subdirectory(bitcoin-secp256k1) + if( NOT DISABLE_TOR) add_subdirectory(tor-connect) endif() @@ -23,6 +31,9 @@ set_property(TARGET libminiupnpc-static PROPERTY FOLDER "contrib") set_property(TARGET zlibstatic PROPERTY FOLDER "contrib") set_property(TARGET mdbx PROPERTY FOLDER "contrib") set_property(TARGET lmdb PROPERTY FOLDER "contrib") +set_property(TARGET secp256k1 PROPERTY FOLDER "contrib") +set_property(TARGET secp256k1_precomputed PROPERTY FOLDER "contrib") + if( NOT DISABLE_TOR) set_property(TARGET tor-connect PROPERTY FOLDER "contrib") endif() diff --git a/contrib/bitcoin-secp256k1 b/contrib/bitcoin-secp256k1 new file mode 160000 index 00000000..a5269373 --- /dev/null +++ b/contrib/bitcoin-secp256k1 @@ -0,0 +1 @@ +Subproject commit a5269373fa13ff845f654d81b90629dd78495641 diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h index 3ee7001e..c200a3d0 100644 --- a/contrib/epee/include/misc_language.h +++ b/contrib/epee/include/misc_language.h @@ -84,9 +84,9 @@ namespace misc_utils { template - void cast_assign_a_to_b(t_type_a& a, const t_type_b& b) + void cast_assign_a_to_b(const t_type_a& a, t_type_b& b) { - *static_cast(&a) = b; + *static_cast(&b) = a; } template + bool kv_serialize(const std::optional& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if(d.has_value()) + { + return kv_serialize(*d, stg, hparent_section, pname); + } + return true; + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_unserialize(std::optional& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + d = t_type{}; + bool r = kv_unserialize(*d, stg, hparent_section, pname); + if (!r) + { + d = std::nullopt; + } + return r; + } + //------------------------------------------------------------------------------------------------------------------- //boost::shared_ptr template bool kv_serialize(const boost::shared_ptr& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) @@ -496,6 +520,30 @@ namespace epee } return r; } + //------------------------------------------------------------------------------------------------------------------- + //std::shared_ptr + template + bool kv_serialize(const std::shared_ptr& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if (d.get()) + { + return kv_serialize(*d, stg, hparent_section, pname); + } + return true; + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_unserialize(std::shared_ptr& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + d.reset(); + t_type* ptr = new t_type(); + bool r = kv_unserialize(*ptr, stg, hparent_section, pname); + if (!r) + { + d.reset(ptr); + } + return r; + } } } \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 200096f3..563b4266 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -116,6 +116,8 @@ else() endif() add_library(crypto ${CRYPTO}) +add_dependencies(crypto secp256k1) +target_link_libraries(crypto secp256k1) add_library(currency_core ${CURRENCY_CORE}) add_dependencies(currency_core version ${PCH_LIB_NAME}) diff --git a/src/common/boost_serialization_maps.h b/src/common/boost_serialization_maps.h index 7f44e4d2..5fbecea0 100644 --- a/src/common/boost_serialization_maps.h +++ b/src/common/boost_serialization_maps.h @@ -4,7 +4,7 @@ #pragma once -#define BEGIN_BOOST_SERIALIZATION() template inline void serialize(t_archive &_arch, const unsigned int ver) { +#define BEGIN_BOOST_SERIALIZATION() template void serialize(t_archive &_arch, const unsigned int ver) { template struct TAssertEquality { static_assert(A == B, "Serialization map is not updated, sizeof() missmatch"); diff --git a/src/common/crypto_serialization.h b/src/common/crypto_serialization.h index dd988a68..481b3ff2 100644 --- a/src/common/crypto_serialization.h +++ b/src/common/crypto_serialization.h @@ -21,6 +21,7 @@ #include "crypto/clsag.h" #include "crypto/zarcanum.h" #include "crypto/one_out_of_many_proofs.h" +#include "crypto/eth_signature.h" #include "boost_serialization_maps.h" #include "serialization/keyvalue_enable_POD_serialize_as_string.h" // @@ -230,6 +231,8 @@ BLOB_SERIALIZER(crypto::key_image); BLOB_SERIALIZER(crypto::signature); BLOB_SERIALIZER(crypto::scalar_t); BLOB_SERIALIZER(crypto::point_t); +BLOB_SERIALIZER(crypto::eth_public_key); +BLOB_SERIALIZER(crypto::eth_signature); VARIANT_TAG(debug_archive, crypto::hash, "hash"); VARIANT_TAG(debug_archive, crypto::public_key, "public_key"); @@ -237,6 +240,8 @@ VARIANT_TAG(debug_archive, crypto::secret_key, "secret_key"); VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation"); VARIANT_TAG(debug_archive, crypto::key_image, "key_image"); VARIANT_TAG(debug_archive, crypto::signature, "signature"); +VARIANT_TAG(debug_archive, crypto::eth_public_key, "eth_public_key"); +VARIANT_TAG(debug_archive, crypto::eth_signature, "eth_signature"); // @@ -245,6 +250,8 @@ VARIANT_TAG(debug_archive, crypto::signature, "signature"); KV_ENABLE_POD_SERIALIZATION_AS_HEX(crypto::scalar_t); KV_ENABLE_POD_SERIALIZATION_AS_HEX(crypto::hash); +KV_ENABLE_POD_SERIALIZATION_AS_HEX(crypto::eth_public_key); +KV_ENABLE_POD_SERIALIZATION_AS_HEX(crypto::eth_signature); // // Boost serialization @@ -296,5 +303,15 @@ namespace boost { a & reinterpret_cast(x); } + template + inline void serialize(Archive &a, crypto::eth_public_key &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } + template + inline void serialize(Archive &a, crypto::eth_signature &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } } // namespace serialization } // namespace boost diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h index 846e4572..b74edc4f 100644 --- a/src/crypto/crypto-sugar.h +++ b/src/crypto/crypto-sugar.h @@ -8,6 +8,7 @@ #include #include #include "crypto.h" +#include "eth_signature.h" namespace crypto { @@ -1209,6 +1210,16 @@ namespace crypto m_elements.emplace_back(pk); } + void add_eth_pub_key(const crypto::eth_public_key& epk) + { + static_assert(sizeof(item_t) == 32, "unexpected size of hs_t::item_t"); + static_assert(sizeof epk.data == 33, "unexpected size of eth_public_key"); + m_elements.emplace_back(c_scalar_0); + m_elements.emplace_back(c_scalar_0); + char* p = m_elements[m_elements.size() - 2].c; // pointer to the first of the two added items + memcpy(p, &epk.data, sizeof epk.data); + } + void add_key_image(const crypto::key_image& ki) { m_elements.emplace_back(ki); diff --git a/src/crypto/eth_signature.cpp b/src/crypto/eth_signature.cpp new file mode 100644 index 00000000..0e33c47f --- /dev/null +++ b/src/crypto/eth_signature.cpp @@ -0,0 +1,162 @@ +// Copyright (c) 2024 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "eth_signature.h" +#include "crypto.h" +#include "bitcoin-secp256k1/include/secp256k1.h" +#include "random.h" +#include "misc_language.h" +#include + + +namespace crypto +{ + bool generate_eth_key_pair(eth_secret_key& sec_key, eth_public_key& pub_key) noexcept + { + try + { + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){ + secp256k1_context_destroy(ctx); + ctx = nullptr; + }); + + uint8_t randomness[32]; + crypto::generate_random_bytes(sizeof randomness, randomness); + if (!secp256k1_context_randomize(ctx, randomness)) + return false; + + for(size_t i = 1024; i != 0; --i) + { + crypto::generate_random_bytes(sizeof sec_key, sec_key.data); + if (secp256k1_ec_seckey_verify(ctx, sec_key.data)) + break; + if (i == 1) + return false; + } + + secp256k1_pubkey uncompressed_pub_key{}; + if (!secp256k1_ec_pubkey_create(ctx, &uncompressed_pub_key, sec_key.data)) + return false; + + size_t output_len = sizeof pub_key; + if (!secp256k1_ec_pubkey_serialize(ctx, pub_key.data, &output_len, &uncompressed_pub_key, SECP256K1_EC_COMPRESSED)) + return false; + + return true; + } + catch(...) + { + return false; + } + } + + bool eth_secret_key_to_public_key(const eth_secret_key& sec_key, eth_public_key& pub_key) noexcept + { + try + { + // TODO: do we need this? consider using static context + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){ + secp256k1_context_destroy(ctx); + ctx = nullptr; + }); + + secp256k1_pubkey uncompressed_pub_key{}; + if (!secp256k1_ec_pubkey_create(ctx, &uncompressed_pub_key, sec_key.data)) + return false; + + size_t output_len = sizeof pub_key; + if (!secp256k1_ec_pubkey_serialize(ctx, pub_key.data, &output_len, &uncompressed_pub_key, SECP256K1_EC_COMPRESSED)) + return false; + + return true; + } + catch(...) + { + return false; + } + } + + // generates secp256k1 ECDSA signature + bool generate_eth_signature(const hash& m, const eth_secret_key& sec_key, eth_signature& sig) noexcept + { + try + { + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){ + secp256k1_context_destroy(ctx); + ctx = nullptr; + }); + + uint8_t randomness[32]; + crypto::generate_random_bytes(sizeof randomness, randomness); + if (!secp256k1_context_randomize(ctx, randomness)) + return false; + + secp256k1_ecdsa_signature secp256k1_ecdsa_sig{}; + if (!secp256k1_ecdsa_sign(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)m.data, sec_key.data, NULL, NULL)) + return false; + + if (!secp256k1_ecdsa_signature_serialize_compact(ctx, sig.data, &secp256k1_ecdsa_sig)) + return false; + + return true; + } + catch(...) + { + return false; + } + } + + // verifies secp256k1 ECDSA signature + bool verify_eth_signature(const hash& m, const eth_public_key& pub_key, const eth_signature& sig) noexcept + { + try + { + // TODO (performance) consider using secp256k1_context_static for verification -- sowle + + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){ + secp256k1_context_destroy(ctx); + ctx = nullptr; + }); + + uint8_t randomness[32]; + crypto::generate_random_bytes(sizeof randomness, randomness); + if (!secp256k1_context_randomize(ctx, randomness)) + return false; + + secp256k1_ecdsa_signature secp256k1_ecdsa_sig{}; + secp256k1_pubkey uncompressed_pub_key{}; + + if (!secp256k1_ecdsa_signature_parse_compact(ctx, &secp256k1_ecdsa_sig, sig.data)) + return false; + + if (!secp256k1_ec_pubkey_parse(ctx, &uncompressed_pub_key, pub_key.data, sizeof pub_key)) + return false; + + // verify a signature + if (!secp256k1_ecdsa_verify(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)m.data, &uncompressed_pub_key)) + return false; + + return true; + } + catch(...) + { + return false; + } + } + + std::ostream& operator<<(std::ostream& o, const eth_secret_key& v) + { + return o << epee::string_tools::pod_to_hex(v); + } + + std::ostream& operator<<(std::ostream& o, const eth_public_key& v) + { + return o << epee::string_tools::pod_to_hex(v); + } + + +} // namespace crypto diff --git a/src/crypto/eth_signature.h b/src/crypto/eth_signature.h new file mode 100644 index 00000000..b186114a --- /dev/null +++ b/src/crypto/eth_signature.h @@ -0,0 +1,66 @@ +// Copyright (c) 2024 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once +#include +#include +#include "hash.h" + +namespace crypto +{ + + // secp256k1 public key in serialized (compressed) form that is used in Etherium + struct eth_public_key + { + uint8_t data[33]; + }; + + // secp256k1 secret key + struct eth_secret_key + { + uint8_t data[32]; + }; + + // secp256k1 ECDSA signature is serialized (compressed) form that is used in Etherium + struct eth_signature + { + uint8_t data[64]; + }; + + // generates secp256k1 keypair + bool generate_eth_key_pair(eth_secret_key& sec_key, eth_public_key& pub_key) noexcept; + + // converts eth_secret_key to eth_public_key + bool eth_secret_key_to_public_key(const eth_secret_key& sec_key, eth_public_key& pub_key) noexcept; + + // generates secp256k1 ECDSA signature + bool generate_eth_signature(const hash& m, const eth_secret_key& sec_key, eth_signature& sig) noexcept; + + // verifies secp256k1 ECDSA signature + bool verify_eth_signature(const hash& m, const eth_public_key& pub_key, const eth_signature& sig) noexcept; + + + inline bool operator==(const eth_public_key& lhs, const eth_public_key& rhs) + { + return memcmp(lhs.data, rhs.data, sizeof lhs.data) == 0; + } + + inline bool operator!=(const eth_public_key& lhs, const eth_public_key& rhs) + { + return !(lhs == rhs); + } + + inline bool operator==(const eth_secret_key& lhs, const eth_secret_key& rhs) + { + return memcmp(lhs.data, rhs.data, sizeof lhs.data) == 0; + } + + inline bool operator!=(const eth_secret_key& lhs, const eth_secret_key& rhs) + { + return !(lhs == rhs); + } + + std::ostream& operator<<(std::ostream& o, const eth_secret_key& v); + std::ostream& operator<<(std::ostream& o, const eth_public_key& v); + +} // namespace crypto diff --git a/src/crypto/zarcanum.h b/src/crypto/zarcanum.h index 3fe4b5c1..55e6b202 100644 --- a/src/crypto/zarcanum.h +++ b/src/crypto/zarcanum.h @@ -123,6 +123,15 @@ namespace crypto return generate_schnorr_sig(m, point_t(A), scalar_t(secret_a), result); } + inline bool generate_schnorr_sig(const hash& m, const secret_key& secret_a, generic_schnorr_sig& result) + { + scalar_t secret_a_s(secret_a); + if (!secret_a_s.is_reduced()) + return false; + point_t A = secret_a_s * c_point_G; + return generate_schnorr_sig_custom_generator(m, A, secret_a_s, result, c_point_G); + } + template inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept; diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index eec4e0df..c72b8d2d 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -4008,7 +4008,8 @@ bool blockchain_storage::put_alias_info(const transaction & tx, extra_alias_entr //@@ remove get_tx_fee_median(); LOG_PRINT_MAGENTA("[ALIAS_REGISTERED]: " << ai.m_alias << ": " << get_account_address_as_str(ai.m_address) << ", fee median: " << get_tx_fee_median(), LOG_LEVEL_1); rise_core_event(CORE_EVENT_ADD_ALIAS, alias_info_to_rpc_alias_info(ai)); - }else + } + else { //update procedure CHECK_AND_ASSERT_MES(ai.m_sign.size() == 1, false, "alias " << ai.m_alias << " can't be update, wrong ai.m_sign.size() count: " << ai.m_sign.size()); @@ -4103,14 +4104,25 @@ bool blockchain_storage::pop_asset_info(const crypto::public_key& asset_id) //------------------------------------------------------------------ bool validate_ado_ownership(asset_op_verification_context& avc) { - asset_operation_ownership_proof aoop = AUTO_VAL_INIT(aoop); - bool r = get_type_in_variant_container(avc.tx.proofs, aoop); - CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed - missing signature (asset_operation_ownership_proof)"); + bool r = false; + CHECK_AND_ASSERT_MES(avc.asset_op_history->size() != 0, false, "asset with id " << avc.asset_id << " has empty history record"); + const asset_descriptor_operation& last_ado = avc.asset_op_history->back(); - CHECK_AND_ASSERT_MES(avc.asset_op_history->size() != 0, false, "asset with id " << avc.asset_id << " has invalid history size() == 0"); + if (last_ado.descriptor.owner_eth_pub_key.has_value()) + { + CHECK_AND_ASSERT_MES(last_ado.descriptor.owner == null_pkey, false, "owner_eth_pub_key is set but owner pubkey is nonzero"); + asset_operation_ownership_proof_eth aoop_eth{}; + r = get_type_in_variant_container(avc.tx.proofs, aoop_eth); + CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed: asset_operation_ownership_proof_eth is missing"); + return crypto::verify_eth_signature(avc.tx_id, last_ado.descriptor.owner_eth_pub_key.value(), aoop_eth.eth_sig); + } - crypto::public_key owner_key = avc.asset_op_history->back().descriptor.owner; - return crypto::verify_schnorr_sig(avc.tx_id, owner_key, aoop.gss); + // owner_eth_pub_key has no value -- fallback to default + asset_operation_ownership_proof aoop{}; + r = get_type_in_variant_container(avc.tx.proofs, aoop); + CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed: asset_operation_ownership_proof is missing"); + + return crypto::verify_schnorr_sig(avc.tx_id, last_ado.descriptor.owner, aoop.gss); } //------------------------------------------------------------------ bool blockchain_storage::validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const @@ -4122,11 +4134,12 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state const asset_descriptor_operation& ado = avc.ado; + bool need_to_validate_ao_amount_commitment = true; + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) { CHECK_AND_ASSERT_MES(!avc.asset_op_history, false, "asset with id " << avc.asset_id << " has already been registered"); avc.amount_to_validate = ado.descriptor.current_supply; - CHECK_AND_ASSERT_MES(validate_asset_operation_amount_commitment(avc), false, "validate_asset_operation_amount_commitment failed!"); if(this->is_hardfork_active(ZANO_HARDFORK_05)) { CHECK_AND_ASSERT_MES(validate_ado_initial(ado.descriptor), false, "validate_ado_initial failed!"); @@ -4134,48 +4147,48 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state } else { - CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size(), false, "asset with id " << avc.asset_id << " has not been registered"); + CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size() > 0, false, "asset with id " << avc.asset_id << " has not been registered"); + const asset_descriptor_operation& last_ado = avc.asset_op_history->back(); // check ownership permission - if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE /*|| ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN*/) + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) { bool r = validate_ado_ownership(avc); - CHECK_AND_ASSERT_MES(r, false, "Faild to validate ownership of asset_descriptor_operation, rejecting"); + CHECK_AND_ASSERT_MES(r, false, "Failed to validate ownership of asset_descriptor_operation, rejecting"); } avc.amount_to_validate = 0; - bool need_to_validate_balance_proof = true; if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) { //check that total current_supply haven't changed - CHECK_AND_ASSERT_MES(ado.descriptor.current_supply == avc.asset_op_history->back().descriptor.current_supply, false, "update operation attempted to change emission, failed"); - CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "update operation attempted to change fileds that shouldn't be modified, failed"); - need_to_validate_balance_proof = false; + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply == last_ado.descriptor.current_supply, false, "update operation attempted to change emission, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "update operation modifies asset descriptor in a prohibited manner"); + need_to_validate_ao_amount_commitment = false; } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) { - CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > avc.asset_op_history->back().descriptor.current_supply, false, "emit operation does not increase the current supply, failed"); - CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "emit operation is not allowed to update fields"); - CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == avc.asset_op_history->back().descriptor.meta_info, false, "emit operation is not allowed to update meta info"); - avc.amount_to_validate = ado.descriptor.current_supply - avc.asset_op_history->back().descriptor.current_supply; + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > last_ado.descriptor.current_supply, false, "emit operation does not increase the current supply, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "emit operation modifies asset descriptor in a prohibited manner"); + CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_ado.descriptor.meta_info, false, "emit operation is not allowed to update meta info"); + avc.amount_to_validate = ado.descriptor.current_supply - last_ado.descriptor.current_supply; } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) { - CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < avc.asset_op_history->back().descriptor.current_supply, false, "burn operation does not decrease the current supply, failed"); - CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "burn operation is not allowed to update fields"); - CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == avc.asset_op_history->back().descriptor.meta_info, false, "burn operation is not allowed to update meta info"); - avc.amount_to_validate = avc.asset_op_history->back().descriptor.current_supply - ado.descriptor.current_supply; + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < last_ado.descriptor.current_supply, false, "burn operation does not decrease the current supply, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "burn operation modifies asset descriptor in a prohibited manner"); + CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_ado.descriptor.meta_info, false, "burn operation is not allowed to update meta info"); + avc.amount_to_validate = last_ado.descriptor.current_supply - ado.descriptor.current_supply; } else { LOG_ERROR("Unknown operation type: " << (int)ado.operation_type); return false; } + } - if (need_to_validate_balance_proof) - { - bool r = validate_asset_operation_amount_commitment(avc); - CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation"); - } + if (need_to_validate_ao_amount_commitment) + { + bool r = validate_asset_operation_amount_commitment(avc); + CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation"); } return true; @@ -5932,6 +5945,10 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti } } + + // TODO @#@#: add check for descriptor.owner_eth_pub_key + + return true; } //------------------------------------------------------------------ diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index d189c2cd..976207ad 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -40,6 +40,7 @@ #include "crypto/hash.h" #include "crypto/range_proofs.h" #include "crypto/zarcanum.h" +#include "crypto/eth_signature.h" #include "misc_language.h" #include "block_flags.h" #include "etc_custom_serialization.h" @@ -695,6 +696,9 @@ namespace currency } }; +#define ASSET_DESCRIPTOR_BASE_STRUCTURE_VER 1 + + typedef boost::variant asset_owner_pub_key_v; struct asset_descriptor_base { @@ -706,9 +710,11 @@ namespace currency std::string meta_info; crypto::public_key owner = currency::null_pkey; // consider premultipling by 1/8 bool hidden_supply = false; - uint8_t version = 0; + boost::optional owner_eth_pub_key; // note: the size is 33 bytes (if present) // NOTE: using boost::optional instead of std::optional because of the Boost compilation issue: https://github.com/boostorg/serialization/issues/319 -- sowle - BEGIN_VERSIONED_SERIALIZE(0, version) + uint8_t version = ASSET_DESCRIPTOR_BASE_STRUCTURE_VER; + + BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_BASE_STRUCTURE_VER, version) FIELD(total_max_supply) FIELD(current_supply) FIELD(decimal_point) @@ -717,9 +723,10 @@ namespace currency FIELD(meta_info) FIELD(owner) FIELD(hidden_supply) + END_VERSION_UNDER(1) + FIELD(owner_eth_pub_key) END_SERIALIZE() - BEGIN_BOOST_SERIALIZATION() BOOST_SERIALIZE(total_max_supply) BOOST_SERIALIZE(current_supply) @@ -729,17 +736,20 @@ namespace currency BOOST_SERIALIZE(meta_info) BOOST_SERIALIZE(owner) BOOST_SERIALIZE(hidden_supply) + BOOST_END_VERSION_UNDER(1) + BOOST_SERIALIZE(owner_eth_pub_key) END_BOOST_SERIALIZATION() BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(total_max_supply) DOC_DSCR("Maximum possible supply for given asset, can't be changed after deployment") DOC_EXMP(1000000000000000000) DOC_END - KV_SERIALIZE(current_supply) DOC_DSCR("Currently emitted supply for given asset (ignored for REGISTER operation)") DOC_EXMP(500000000000000000) DOC_END - KV_SERIALIZE(decimal_point) DOC_DSCR("Decimal point") DOC_EXMP(12) DOC_END - KV_SERIALIZE(ticker) DOC_DSCR("Ticker associated with asset") DOC_EXMP("ZUSD") DOC_END - KV_SERIALIZE(full_name) DOC_DSCR("Full name of the asset") DOC_EXMP("Zano wrapped USD") DOC_END - KV_SERIALIZE(meta_info) DOC_DSCR("Any other information assetiaded with asset in a free form") DOC_EXMP("Stable and private") DOC_END - KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Owner's key, used only for EMIT and UPDATE validation, could be changed by transferring asset ownership") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END - KV_SERIALIZE(hidden_supply) DOC_DSCR("This one reserved for future use, will be documented later") DOC_END + KV_SERIALIZE(total_max_supply) DOC_DSCR("Maximum possible supply for a given asset, cannot be changed after deployment.") DOC_EXMP(1000000000000000000) DOC_END + KV_SERIALIZE(current_supply) DOC_DSCR("Currently emitted supply for the given asset (ignored for REGISTER operation).") DOC_EXMP(500000000000000000) DOC_END + KV_SERIALIZE(decimal_point) DOC_DSCR("Decimal point.") DOC_EXMP(12) DOC_END + KV_SERIALIZE(ticker) DOC_DSCR("Ticker associated with the asset.") DOC_EXMP("ZABC") DOC_END + KV_SERIALIZE(full_name) DOC_DSCR("Full name of the asset.") DOC_EXMP("Zano wrapped ABC") DOC_END + KV_SERIALIZE(meta_info) DOC_DSCR("Any other information associated with the asset in free form.") DOC_EXMP("Stable and private") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Owner's key, used only for EMIT and UPDATE validation, can be changed by transferring asset ownership.") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE(hidden_supply) DOC_DSCR("This field is reserved for future use and will be documented later.") DOC_END + KV_SERIALIZE(owner_eth_pub_key) DOC_DSCR("[Optional] Owner's key in the case when ETH signature is used.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -840,6 +850,26 @@ namespace currency }; + struct asset_operation_ownership_proof_eth + { + crypto::eth_signature eth_sig; // 64 bytes + uint8_t version = 0; + + BEGIN_VERSIONED_SERIALIZE(0, version) + FIELD(eth_sig) + END_SERIALIZE() + + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(eth_sig) + BOOST_SERIALIZE(version) + END_BOOST_SERIALIZATION() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(eth_sig) DOC_DSCR("HEX-encoded ETH signature (64 bytes)") DOC_EXMP("674bb56a5b4fa562e679ccacc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6add697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE(version) DOC_DSCR("Structure version") DOC_EXMP(0) DOC_END + END_KV_SERIALIZE_MAP() + }; + struct extra_padding { std::vector buff; //stub @@ -943,7 +973,7 @@ namespace currency typedef boost::variant signature_v; - typedef boost::variant proof_v; + typedef boost::variant proof_v; //include backward compatibility defintions @@ -1202,6 +1232,10 @@ SET_VARIANT_TAGS(currency::zc_balance_proof, 48, "zc_balance_proof"); SET_VARIANT_TAGS(currency::asset_descriptor_operation, 49, "asset_descriptor_base"); SET_VARIANT_TAGS(currency::asset_operation_proof, 50, "asset_operation_proof"); SET_VARIANT_TAGS(currency::asset_operation_ownership_proof, 51, "asset_operation_ownership_proof"); +SET_VARIANT_TAGS(currency::asset_operation_ownership_proof_eth, 52, "asset_operation_ownership_proof_eth"); + +SET_VARIANT_TAGS(crypto::eth_public_key, 60, "eth_public_key"); +//SET_VARIANT_TAGS(crypto::eth_signature, 61, "eth_signature");s diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index a2b05e2b..9dfae001 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -2196,6 +2196,8 @@ namespace currency hsc.add_scalar(crypto::scalar_t(ado.descriptor.total_max_supply)); hsc.add_scalar(crypto::scalar_t(ado.descriptor.decimal_point)); hsc.add_pub_key(ado.descriptor.owner); + if (ado.descriptor.owner_eth_pub_key.has_value()) + hsc.add_eth_pub_key(ado.descriptor.owner_eth_pub_key.value()); crypto::hash h = hsc.calc_hash_no_reduce(); // this hash function needs to be computationally expensive (s.a. the whitepaper) @@ -2248,7 +2250,8 @@ namespace currency // asset_control_key = Hs(CRYPTO_HDS_ASSET_CONTROL_KEY, 8 * tx_key.sec * sender_account_keys.account_address.spend_public_key, 0) // ado.descriptor.owner = asset_control_key * G - ado.descriptor.owner = sender_account_keys.account_address.spend_public_key; + if (!ado.descriptor.owner_eth_pub_key.has_value()) + ado.descriptor.owner = sender_account_keys.account_address.spend_public_key; CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(ado, &gen_context.ao_asset_id_pt, &gen_context.ao_asset_id), false, "get_or_calculate_asset_id failed"); @@ -2337,25 +2340,6 @@ namespace currency } if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_handle_asset_descriptor_operation_before_seal{ &ado }); - ftp.need_to_generate_ado_proof = true; - /* - //seal it with owners signature - crypto::signature sig = currency::null_sig; - crypto::hash h = get_signature_hash_for_asset_operation(ado); - if (ftp.pthirdparty_sign_handler) - { - bool r = ftp.pthirdparty_sign_handler->sign(h, ftp.ado_current_asset_owner, sig); - CHECK_AND_ASSERT_MES(r, false, "asset thirparty sign failed"); - } - else - { - crypto::public_key pub_k = currency::null_pkey; - crypto::secret_key_to_public_key(sender_account_keys.spend_secret_key, pub_k); - CHECK_AND_ASSERT_MES(ftp.ado_current_asset_owner == pub_k, false, "asset owner key not matched with provided private key for asset operation signing"); - crypto::generate_signature(h, pub_k, account_keys.spend_secret_key, sig); - } - ado.opt_proof = sig; - */ } return true; } @@ -2605,8 +2589,7 @@ namespace currency // ASSET oprations handling if (tx.version > TRANSACTION_VERSION_PRE_HF4) { - asset_descriptor_operation* pado = nullptr; - pado = get_type_in_variant_container(tx.extra); + asset_descriptor_operation* pado = get_type_in_variant_container(tx.extra); if (pado) { bool r = construct_tx_handle_ado(sender_account_keys, ftp, *pado, gen_context, gen_context.tx_key, shuffled_dsts); @@ -2719,7 +2702,8 @@ namespace currency // 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); + result.tx_id = get_transaction_prefix_hash(tx); + const crypto::hash &tx_prefix_hash = result.tx_id; // ring signatures (per-input proofs) r = false; @@ -2775,35 +2759,30 @@ namespace currency CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed"); tx.proofs.emplace_back(std::move(balance_proof)); - // asset operation proof (if necessary) + // optional asset operation proofs: amount commitment proof (required for register, emit, public burn) if (gen_context.ao_asset_id != currency::null_pkey) { - // construct the asset operation proof - // TODO @#@# add support for hidden supply + // asset amount commitment g proof (TODO @#@# add support for hidden supply) crypto::signature aop_g_sig{}; crypto::generate_signature(tx_prefix_hash, crypto::point_t(gen_context.ao_amount_blinding_mask * crypto::c_point_G).to_public_key(), 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)); } - if(ftp.need_to_generate_ado_proof) - { - asset_operation_ownership_proof aoop = AUTO_VAL_INIT(aoop); - if (ftp.pthirdparty_sign_handler) + // optional asset operation proofs: ownership proof for standard (non-eth) owner (using generic Shnorr signature with the spend secret key) + const asset_descriptor_operation* pado = get_type_in_variant_container(tx.extra); + if (pado != nullptr) + { + if ((pado->operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || pado->operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) && + !pado->descriptor.owner_eth_pub_key.has_value()) { - //ask third party to generate proof - r = ftp.pthirdparty_sign_handler->sign(tx_prefix_hash, ftp.ado_current_asset_owner, aoop.gss); - CHECK_AND_ASSERT_MES(r, false, "Failed to sign ado by thirdparty"); - } - else - { - //generate signature by wallet account - r = crypto::generate_schnorr_sig(tx_prefix_hash, ftp.ado_current_asset_owner, sender_account_keys.spend_secret_key, aoop.gss); + asset_operation_ownership_proof aoop{}; + r = crypto::generate_schnorr_sig(tx_prefix_hash, sender_account_keys.spend_secret_key, aoop.gss); CHECK_AND_ASSERT_MES(r, false, "Failed to sign ado proof"); + if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_after_asset_ownership_proof_generated{ &aoop }); + tx.proofs.emplace_back(aoop); } - if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_after_asset_ownership_proof_generated{ &aoop }); - tx.proofs.emplace_back(aoop); } } diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 78668536..e1899ea1 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -139,11 +139,13 @@ namespace currency bool hltc_our_out_is_before_expiration; }; - struct thirdparty_sign_handler + struct asset_eth_signer_i { - virtual bool sign(const crypto::hash& h, const crypto::public_key& owner_public_key, crypto::generic_schnorr_sig& sig); + virtual bool sign(const crypto::hash& h, const crypto::eth_public_key& asset_owner, crypto::eth_signature& sig) = 0; }; + typedef boost::variant asset_owner_key_v; + struct finalize_tx_param { uint64_t unlock_time; @@ -165,10 +167,6 @@ namespace currency epee::misc_utils::events_dispatcher* pevents_dispatcher; tx_generation_context gen_context{}; // solely for consolidated txs - //crypto::secret_key asset_control_key = currency::null_skey; - crypto::public_key ado_current_asset_owner = null_pkey; - thirdparty_sign_handler* pthirdparty_sign_handler = nullptr; - mutable bool need_to_generate_ado_proof = false; BEGIN_SERIALIZE_OBJECT() @@ -191,14 +189,13 @@ namespace currency { FIELD(gen_context); } - FIELD(ado_current_asset_owner) - FIELD(need_to_generate_ado_proof) END_SERIALIZE() }; struct finalized_tx { currency::transaction tx; + crypto::hash tx_id; crypto::secret_key one_time_key; finalize_tx_param ftp; std::string htlc_origin; @@ -208,6 +205,7 @@ namespace currency BEGIN_SERIALIZE_OBJECT() FIELD(tx) + FIELD(tx_id) FIELD(one_time_key) FIELD(ftp) FIELD(htlc_origin) @@ -546,7 +544,7 @@ namespace currency { assets_list.push_back(currency::asset_descriptor_with_id()); assets_list.back().asset_id = pr.first; - epee::misc_utils::cast_assign_a_to_b(assets_list.back(), static_cast(pr.second)); + epee::misc_utils::cast_assign_a_to_b(static_cast(pr.second), assets_list.back()); //*static_cast(&assets_list.back()) = pr.second; } } @@ -948,6 +946,12 @@ namespace currency } } //--------------------------------------------------------------- + template + typename std::enable_if_t, std::ostream&> operator<<(std::ostream& o, invocable_t callee) + { + callee(o); + return o; + } //--------------------------------------------------------------- std::ostream& operator <<(std::ostream& o, const ref_by_id& r); std::ostream& operator <<(std::ostream& o, const std::type_info& ti); diff --git a/src/gui/qt-daemon/layout b/src/gui/qt-daemon/layout index 6d212d4e..748e8e96 160000 --- a/src/gui/qt-daemon/layout +++ b/src/gui/qt-daemon/layout @@ -1 +1 @@ -Subproject commit 6d212d4eefaf6d13c72799cb89be2c80b1813d38 +Subproject commit 748e8e96d8f2653e6e698a11f67c172c1f84c2b2 diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 57a5a2e8..44712f8b 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -75,7 +75,8 @@ namespace currency return true; } #define check_core_ready() check_core_ready_(LOCAL_FUNCTION_DEF__) -#define CHECK_CORE_READY() if(!check_core_ready()){res.status = API_RETURN_CODE_BUSY;return true;} +#define CHECK_CORE_READY() if (!check_core_ready()) {res.status = API_RETURN_CODE_BUSY; return true; } +#define CHECK_CORE_READY_WE() if (!check_core_ready()) {error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; error_resp.message = "Core is busy."; return false; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, connection_context& cntx) { @@ -750,6 +751,74 @@ namespace currency return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_decrypt_tx_details(const COMMAND_RPC_DECRYPT_TX_DETAILS::request& req, COMMAND_RPC_DECRYPT_TX_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) + { +#define LOCAL_CHECK(cond, msg) if (!(cond)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; error_resp.message = msg; LOG_PRINT_L1("on_decrypt_tx_details: " << error_resp.message); return false; } +#define LOCAL_CHECK_INT_ERR(cond, msg) if (!(cond)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = msg; LOG_PRINT_L1("on_decrypt_tx_details: " << error_resp.message); return false; } + + LOCAL_CHECK(req.tx_id.empty() != req.tx_blob.empty(), "One of either tx_id or tx_blob must be specified."); + + transaction tx{}; + if (!req.tx_id.empty()) + { + CHECK_CORE_READY_WE(); + + crypto::hash tx_id{}; + LOCAL_CHECK(crypto::parse_tpod_from_hex_string(req.tx_id, tx_id), "tx_id is given, but it's invalid"); + LOCAL_CHECK(m_core.get_transaction(tx_id, tx), "tx with the given tx_id could be found in the blockchain"); + } + else + { + blobdata decoded_blob = string_encoding::base64_decode(req.tx_blob); + if (!t_unserializable_object_from_blob(tx, decoded_blob)) + { + // unable to decode tx_blob as base64, try once again as hex-encoding + decoded_blob.clear(); + string_tools::parse_hexstr_to_binbuff(req.tx_blob, decoded_blob); + LOCAL_CHECK(t_unserializable_object_from_blob(tx, decoded_blob), "tx_id is not given, and tx_blob is invalid"); + } + } + + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); + crypto::point_t R{}; + LOCAL_CHECK(tx_pub_key != null_pkey && R.from_public_key(tx_pub_key) && R.is_in_main_subgroup(), "unsigned_tx: tx public key is missing or invalid"); + + LOCAL_CHECK(tx_pub_key == (crypto::scalar_t(req.tx_secret_key) * crypto::c_point_G).to_public_key(), "tx_secret_key doesn't match the transaction public key"); + + LOCAL_CHECK(req.outputs_addresses.size() == tx.vout.size(), "outputs_addresses count (" + epee::string_tools::num_to_string_fast(req.outputs_addresses.size()) + " doesn't match tx.vout size (" + epee::string_tools::num_to_string_fast(tx.vout.size()) + ")"); + + for(size_t i = 0; i < req.outputs_addresses.size(); ++i) + { + if (req.outputs_addresses[i].empty()) + continue; // skip this output if the given address is empty string + + account_public_address addr{}; + payment_id_t payment_id{}; + LOCAL_CHECK(currency::get_account_address_and_payment_id_from_str(addr, payment_id, req.outputs_addresses[i]) && payment_id.empty(), "output address #" + epee::string_tools::num_to_string_fast(i) + " couldn't be parsed or it is an integrated address (which is not supported)"); + + tx_out_v& out_v = tx.vout[i]; + LOCAL_CHECK(out_v.type() == typeid(tx_out_zarcanum), "tx output #" + epee::string_tools::num_to_string_fast(i) + " has wrong type"); + const tx_out_zarcanum& zo = boost::get(out_v); + + crypto::key_derivation derivation{}; + LOCAL_CHECK_INT_ERR(crypto::generate_key_derivation(addr.view_public_key, req.tx_secret_key, derivation), "output #" + epee::string_tools::num_to_string_fast(i) + ": generate_key_derivation failed"); + + auto& decoded_out = res.decoded_outputs.emplace_back(); + decoded_out.out_index = i; + decoded_out.address = req.outputs_addresses[i]; + crypto::scalar_t amount_blinding_mask{}, asset_id_blinding_mask{}; + LOCAL_CHECK(currency::decode_output_amount_and_asset_id(zo, derivation, i, decoded_out.amount, decoded_out.asset_id, amount_blinding_mask, asset_id_blinding_mask), "output #" + epee::string_tools::num_to_string_fast(i) + ": cannot be decoded"); + } + + res.tx_in_json = currency::obj_to_json_str(tx); + res.verified_tx_id = get_transaction_hash(tx); + + res.status = API_RETURN_CODE_OK; + return true; +#undef LOCAL_CHECK +#undef LOCAL_CHECK_INT_ERR + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_main_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { if (!m_core.get_blockchain_storage().get_main_block_rpc_details(req.id, res.block_details)) @@ -1001,7 +1070,7 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { - CHECK_CORE_READY(); + CHECK_CORE_READY_WE(); if(req.size()!=1) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; @@ -1044,8 +1113,7 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_submitblock2(const COMMAND_RPC_SUBMITBLOCK2::request& req, COMMAND_RPC_SUBMITBLOCK2::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { - CHECK_CORE_READY(); - + CHECK_CORE_READY_WE(); block b = AUTO_VAL_INIT(b); if (!parse_and_validate_block_from_blob(req.b, b)) diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 8e44a31a..e95f5a41 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2024 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 @@ -89,6 +89,7 @@ namespace currency bool on_get_votes(const COMMAND_RPC_GET_VOTES::request& req, COMMAND_RPC_GET_VOTES::response& res, connection_context& cntx); bool on_get_asset_info(const COMMAND_RPC_GET_ASSET_INFO::request& req, COMMAND_RPC_GET_ASSET_INFO::response& res, connection_context& cntx); bool on_get_assets_list(const COMMAND_RPC_GET_ASSETS_LIST::request& req, COMMAND_RPC_GET_ASSETS_LIST::response& res, connection_context& cntx); + bool on_decrypt_tx_details(const COMMAND_RPC_DECRYPT_TX_DETAILS::request& req, COMMAND_RPC_DECRYPT_TX_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); bool on_get_main_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); bool on_get_alt_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); @@ -153,9 +154,11 @@ namespace currency MAP_JON_RPC ("getrandom_outs1", on_get_random_outs1, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) MAP_JON_RPC ("getrandom_outs3", on_get_random_outs3, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3) MAP_JON_RPC ("get_votes", on_get_votes, COMMAND_RPC_GET_VOTES) + //assets api MAP_JON_RPC ("get_asset_info", on_get_asset_info, COMMAND_RPC_GET_ASSET_INFO) MAP_JON_RPC ("get_assets_list", on_get_assets_list, COMMAND_RPC_GET_ASSETS_LIST) + MAP_JON_RPC_WE("decrypt_tx_details", on_decrypt_tx_details, COMMAND_RPC_DECRYPT_TX_DETAILS) MAP_JON_RPC_WE("get_main_block_details", on_get_main_block_details, COMMAND_RPC_GET_BLOCK_DETAILS) MAP_JON_RPC_WE("get_alt_block_details", on_get_alt_block_details, COMMAND_RPC_GET_BLOCK_DETAILS) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 452a8316..324dd18f 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -179,6 +179,58 @@ namespace currency }; }; + + struct COMMAND_RPC_DECRYPT_TX_DETAILS + { + DOC_COMMAND("Decrypts transaction private information. Should be used only with your own local daemon for security reasons."); + + struct request + { + std::string tx_id; + currency::blobdata tx_blob; + crypto::secret_key tx_secret_key; + std::vector outputs_addresses; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_id) DOC_DSCR("[either] ID for a transaction if it is already in the blockchain. Can be ommited if tx_blob is provided.") DOC_EXMP("a6e8da986858e6825fce7a192097e6afae4e889cabe853a9c29b964985b23da8") DOC_END + KV_SERIALIZE(tx_blob) DOC_DSCR("[or] base64-encoded or hex-encoded tx blob. Can be ommited if tx_id is provided.") DOC_EXMP("ewogICJ2ZXJzaW9uIjogMSwgC....iAgInZpbiI6IFsgewogICAgIC") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(tx_secret_key) DOC_DSCR("Hex-encoded transaction secret key.") DOC_EXMP("2e0b840e70dba386effd64c5d988622dea8c064040566e6bf035034cbb54a5c08") DOC_END + KV_SERIALIZE(outputs_addresses) DOC_DSCR("Address of each of tx's output. Order is important and should correspond to order of tx's outputs. Empty strings are ignored.") DOC_EXMP_AGGR("ZxDNaMeZjwCjnHuU5gUNyrP1pM3U5vckbakzzV6dEHyDYeCpW8XGLBFTshcaY8LkG9RQn7FsQx8w2JeJzJwPwuDm2NfixPAXf", "ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END + END_KV_SERIALIZE_MAP() + }; + + // TODO consider reusing existing structure transfer_destination -- sowle + struct decoded_output + { + uint64_t amount = 0; + std::string address; + crypto::public_key asset_id; + uint64_t out_index; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) DOC_DSCR("Amount begin transferred.") DOC_EXMP(10000000000000) DOC_END + KV_SERIALIZE(address) DOC_DSCR("Destination address.") DOC_EXMP("ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset id.") DOC_EXMP("cc608f59f8080e2fbfe3c8c80eb6e6a953d47cf2d6aebd345bada3a1cab99852") DOC_END + KV_SERIALIZE(out_index) DOC_DSCR("Index of the corresponding output in the transaction.") DOC_EXMP(1) DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector decoded_outputs; + std::string tx_in_json; + crypto::hash verified_tx_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) DOC_DSCR("Status code of operation, OK if success") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(decoded_outputs) DOC_DSCR("Transaction's decoded outputs") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE_BLOB_AS_BASE64_STRING(tx_in_json) DOC_DSCR("Serialized transaction represented in JSON, encoded in Base64.") DOC_EXMP("ewogICJ2ZXJzaW9uIjogMSwgC....iAgInZpbiI6IFsgewogICAgIC") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(verified_tx_id) DOC_DSCR("(Re)calculated transaction id. Can be used in third-party proof generation.") DOC_EXMP("a6e8da986858e6825fce7a192097e6afae4e889cabe853a9c29b964985b23da8") DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_GET_HEIGHT { DOC_COMMAND("Return current blockchain height"); @@ -643,12 +695,14 @@ namespace currency struct request { std::string tx_as_hex; + std::string tx_as_json; request() {} explicit request(const transaction &); BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_as_hex) DOC_DSCR("The transaction data as a hexadecimal string, ready for network broadcast.") DOC_EXMP("00018ed1535b8b4862e.....368cdc5a86") DOC_END + KV_SERIALIZE(tx_as_hex) DOC_DSCR("[either] The transaction data as a hexadecimal string, ready for network broadcast.") DOC_EXMP("00018ed1535b8b4862e.....368cdc5a86") DOC_END + KV_SERIALIZE_BLOB_AS_BASE64_STRING(tx_as_json) DOC_DSCR("[or] The transaction data as a base64-encoded json, ready for network broadcast.") DOC_EXMP("ARMBgKCUpY0dBBoAAAAAAAAAABoCAAAAA.......AAAAAAAAABoPAAAAAAAAACVA4FRLH") DOC_END END_KV_SERIALIZE_MAP() }; diff --git a/src/serialization/boost_types.h b/src/serialization/boost_types.h index 154705b8..ca3e3d7b 100644 --- a/src/serialization/boost_types.h +++ b/src/serialization/boost_types.h @@ -1,3 +1,4 @@ +// Copyright (c) 2018-2024 Zano Project // Copyright (c) 2014-2017 The The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -7,7 +8,7 @@ #include - +// boost::optional template