1
0
Fork 0
forked from lthn/blockchain

Merge branch 'release'

This commit is contained in:
sowle 2024-11-21 18:19:50 +01:00
commit 2817090c8a
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
128 changed files with 11702 additions and 1978 deletions

3
.gitmodules vendored
View file

@ -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

View file

@ -76,6 +76,11 @@ set(USE_PCH FALSE CACHE BOOL "Use shared precompiled headers")
set(DISABLE_TOR FALSE CACHE BOOL "Disable TOR library(and related tor-connect submodule)")
set(TESTNET FALSE CACHE BOOL "Compile for testnet")
set(BUILD_GUI FALSE CACHE BOOL "Build qt-daemon")
set(USE_BITCOIN_SECP256K1_FOR_ECDSA FALSE CACHE BOOL "Use bitcoin-secp256k1 library for validating ECDSA(instead of OpenSSL)")
if(NOT USE_BITCOIN_SECP256K1_FOR_ECDSA)
add_definitions(-DUSE_OPEN_SSL_FOR_ECDSA)
endif()
include_directories(src contrib/eos_portable_archive contrib contrib/epee/include contrib/jwt-cpp/include ${OPENSSL_INCLUDE_DIR} "${CMAKE_BINARY_DIR}/version" "${CMAKE_BINARY_DIR}/contrib/zlib")
@ -89,6 +94,7 @@ endif()
if(CAKEWALLET)
message("NOTICE: Building libraries for CAKEWALLET")
add_definitions(-DCAKEWALLET)
add_definitions(-DDISABLE_PFR_SERIALIZATION_SELFCHECK)
endif()
set(OPENSSL_USE_STATIC_LIBS TRUE) # link statically
@ -226,7 +232,12 @@ endif()
message("CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.00)
set(Boost_LIBRARIES "libboost.a")
if(NOT DEFINED SKIP_BOOST_FATLIB_LIB OR NOT SKIP_BOOST_FATLIB_LIB)
message("Ios: libboost.a included as library")
set(Boost_LIBRARIES "libboost.a")
else()
message("Ios: libboost.a not included as library")
endif()
#workaround for new XCode 12 policy for builds(now it includes a slice for the "arm64" when builds for simulator)
set(__iphoneos_archs "arm64")
#set(__iphonesimulator_archs "arm64,x86_64")
@ -235,8 +246,12 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
#set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}")
#set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
set(Boost_LIBRARY_DIRS "${Boost_LIBRARY_DIRS}/${CMAKE_ANDROID_ARCH_ABI}/")
set(Boost_LIBRARIES "${Boost_LIBRARY_DIRS}libboost_system.a;${Boost_LIBRARY_DIRS}libboost_filesystem.a;${Boost_LIBRARY_DIRS}libboost_thread.a;${Boost_LIBRARY_DIRS}libboost_timer.a;${Boost_LIBRARY_DIRS}libboost_date_time.a;${Boost_LIBRARY_DIRS}libboost_chrono.a;${Boost_LIBRARY_DIRS}libboost_regex.a;${Boost_LIBRARY_DIRS}libboost_serialization.a;${Boost_LIBRARY_DIRS}libboost_atomic.a;${Boost_LIBRARY_DIRS}libboost_program_options.a")
if(CAKEWALLET)
find_package(Boost 1.71 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale)
else()
set(Boost_LIBRARY_DIRS "${Boost_LIBRARY_DIRS}/${CMAKE_ANDROID_ARCH_ABI}/")
set(Boost_LIBRARIES "${Boost_LIBRARY_DIRS}libboost_system.a;${Boost_LIBRARY_DIRS}libboost_filesystem.a;${Boost_LIBRARY_DIRS}libboost_thread.a;${Boost_LIBRARY_DIRS}libboost_timer.a;${Boost_LIBRARY_DIRS}libboost_date_time.a;${Boost_LIBRARY_DIRS}libboost_chrono.a;${Boost_LIBRARY_DIRS}libboost_regex.a;${Boost_LIBRARY_DIRS}libboost_serialization.a;${Boost_LIBRARY_DIRS}libboost_atomic.a;${Boost_LIBRARY_DIRS}libboost_program_options.a")
endif()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fPIC")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fPIC")
elseif(APPLE)
@ -254,7 +269,7 @@ include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/contrib/e
if(MINGW)
set(Boost_LIBRARIES "${Boost_LIBRARIES};ws2_32;mswsock")
elseif(NOT MSVC)
if(NOT APPLE)
if(NOT APPLE AND NOT CAKEWALLET)
set(Boost_LIBRARIES "${Boost_LIBRARIES};")
if(STATIC)
message("NOTICE: Including static ICU libraries")

View file

@ -15,10 +15,10 @@ Be sure to clone the repository properly:\
|--|--|--|--|
| gcc (Linux) | 5.4.0 | 9.4.0 | 12.3.0 |
| llvm/clang (Linux) | UNKNOWN | 7.0.1 | 8.0.0 |
| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2017 (15.9.30) | 2019 (16.11.34) | 2022 (17.9.5) |
| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2017 (15.9.30) | 2019 (16.11.34) | 2022 (17.11.5) |
| [XCode](https://developer.apple.com/downloads/) (macOS) | 12.3 | 14.3 | 15.2 |
| [CMake](https://cmake.org/download/) | 3.15.5 | 3.26.3 | 3.29.0 |
| [Boost](https://www.boost.org/users/download/) | 1.70 | 1.70 | 1.84 |
| [Boost](https://www.boost.org/users/download/) | 1.75 | 1.84 | 1.84 |
| [OpenSSL](https://www.openssl.org/source/) [(win)](https://slproweb.com/products/Win32OpenSSL.html) | 1.1.1n | 1.1.1w | 1.1.1w |
| [Qt](https://download.qt.io/archive/qt/) (*only for GUI*) | 5.8.0 | 5.11.2 | 5.15.2 |
@ -52,10 +52,9 @@ Recommended OS versions: Ubuntu 20.04, 22.04 LTS.
3. Download and build Boost\
(Assuming you have cloned Zano into the 'zano' folder. If you used a different location for Zano, **edit line 4** accordingly.)
curl -OL https://boostorg.jfrog.io/artifactory/main/release/1.70.0/source/boost_1_70_0.tar.bz2
echo "430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778 boost_1_70_0.tar.bz2" | shasum -c && tar -xjf boost_1_70_0.tar.bz2
rm boost_1_70_0.tar.bz2 && cd boost_1_70_0
patch -p0 < ../zano/utils/boost_1.70_gcc_8.patch || cd ..
curl -OL https://boostorg.jfrog.io/artifactory/main/release/1.84.0/source/boost_1_84_0.tar.bz2
echo "cc4b893acf645c9d4b698e9a0f08ca8846aa5d6c68275c14c3e7949c24109454 boost_1_84_0.tar.bz2" | shasum -c && tar -xjf boost_1_84_0.tar.bz2
rm boost_1_84_0.tar.bz2 && cd boost_1_84_0
./bootstrap.sh --with-libraries=system,filesystem,thread,date_time,chrono,regex,serialization,atomic,program_options,locale,timer,log
./b2 && cd ..
Make sure that you see "The Boost C++ Libraries were successfully built!" message at the end.
@ -88,13 +87,13 @@ For instance, by adding the following lines to `~/.bashrc`
[*server version*]
export BOOST_ROOT=/home/user/boost_1_70_0
export BOOST_ROOT=/home/user/boost_1_84_0
export OPENSSL_ROOT_DIR=/home/user/openssl
[*GUI version*]
export BOOST_ROOT=/home/user/boost_1_70_0
export BOOST_ROOT=/home/user/boost_1_84_0
export OPENSSL_ROOT_DIR=/home/user/openssl
export QT_PREFIX_PATH=/home/user/Qt5.11.2/5.11.2/gcc_64

View file

@ -5,6 +5,23 @@ add_subdirectory(zlib)
add_subdirectory(db)
add_subdirectory(ethereum)
if(USE_BITCOIN_SECP256K1_FOR_ECDSA)
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)
if(STATIC)
set(SECP256K1_DISABLE_SHARED ON CACHE BOOL "Disable shared library for secp256k1")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build static libraries by default" FORCE)
endif()
add_subdirectory(bitcoin-secp256k1)
set_property(TARGET secp256k1 PROPERTY FOLDER "contrib")
set_property(TARGET secp256k1_precomputed PROPERTY FOLDER "contrib")
endif()
if( NOT DISABLE_TOR)
add_subdirectory(tor-connect)
endif()
@ -23,6 +40,7 @@ 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")
if( NOT DISABLE_TOR)
set_property(TARGET tor-connect PROPERTY FOLDER "contrib")
endif()

@ -0,0 +1 @@
Subproject commit a5269373fa13ff845f654d81b90629dd78495641

View file

@ -35,7 +35,19 @@
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <filesystem>
#if __has_include(<filesystem>)
#include <filesystem>
namespace stdfs = std::filesystem;
#else
#if TARGET_OS_IOS
#error "This should never happen on ios."
#endif
namespace stdfs = boost::filesystem;
#endif
//#include <filesystem>
#ifndef MAKE64
#define MAKE64(low,high) ((__int64)(((DWORD)(low)) | ((__int64)((DWORD)(high))) << 32))
@ -562,10 +574,10 @@ namespace file_io_utils
try
{
std::filesystem::directory_iterator end_itr; // default construction yields past-the-end
for ( std::filesystem::directory_iterator itr( epee::string_encoding::utf8_to_wstring(path) ); itr != end_itr; ++itr )
stdfs::directory_iterator end_itr; // default construction yields past-the-end
for (stdfs::directory_iterator itr( epee::string_encoding::utf8_to_wstring(path) ); itr != end_itr; ++itr )
{
if ( only_files && std::filesystem::is_directory(itr->status()) )
if ( only_files && stdfs::is_directory(itr->status()) )
{
continue;
}

View file

@ -84,9 +84,9 @@ namespace misc_utils
{
template<typename t_type_a, typename t_type_b>
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<t_type_b*>(&a) = b;
*static_cast<t_type_a*>(&b) = a;
}
template<class _Ty1,

View file

@ -1,4 +1,4 @@
// Copyright (c) 2019, Zano Project
// Copyright (c) 2019-2024, Zano Project
// Copyright (c) 2019, anonimal <anonimal@zano.org>
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
@ -90,7 +90,7 @@ DISABLE_VS_WARNINGS(4100)
#define LOG_JOURNAL_MAX_ELEMENTS 100
#ifdef _DEBUG
#define _ASSERTE__(expr) if(!expr) {__debugbreak();}
#define _ASSERTE__(expr) if(!(expr)) {__debugbreak();}
#else
#define _ASSERTE__(expr)
#endif

View file

@ -282,8 +282,15 @@ namespace epee {
namespace http {
struct i_chain_handler
{
virtual bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response_info,
epee::net_utils::connection_context_base& conn_context, bool& call_found, documentation& docs = epee::net_utils::http::i_chain_handler::m_empty_documentation)
{
return this->handle_http_request_map(query_info, response_info, conn_context, call_found, docs);
}
virtual bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response_info,
epee::net_utils::connection_context_base& m_conn_context, bool& call_found, documentation& docs = epee::net_utils::http::i_chain_handler::m_empty_documentation) = 0;
static inline documentation m_empty_documentation;
};
@ -365,7 +372,7 @@ namespace epee {
LOG_PRINT( "[HTTP/BIN][" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "][" << query_info.m_URI << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
}
#define CHAIN_TO_PHANDLER(pi_chain_handler) else if (pi_chain_handler && pi_chain_handler->handle_http_request_map(query_info, response_info, m_conn_context, call_found, docs) && call_found) { return true;}
#define CHAIN_TO_PHANDLER(pi_chain_handler) else if (pi_chain_handler && pi_chain_handler->handle_http_request(query_info, response_info, m_conn_context, call_found, docs) && call_found) { return true;}
#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);call_found = true;}

View file

@ -25,6 +25,8 @@
//
#pragma once
#include <type_traits>
#include <optional>
#include "misc_language.h"
namespace epee
{
@ -51,24 +53,73 @@ namespace epee
}
};
template<typename T>
struct is_std_optional : std::false_type {};
template<typename T>
struct is_std_optional<std::optional<T>> : std::true_type {};
template<typename T>
struct is_std_optional<boost::optional<T>> : std::true_type {};
//basic helpers for pod-to-hex serialization
template<class t_pod_type>
std::string transform_t_pod_to_str_internal(const t_pod_type& a)
{
return epee::string_tools::pod_to_hex(a);
}
template<class t_pod_type>
std::string transform_t_pod_to_str_internal(const std::optional<t_pod_type>& a)
{
if (a.has_value())
return epee::string_tools::pod_to_hex(*a);
else
return "";
}
template<class t_pod_type>
std::string transform_t_pod_to_str_internal(const boost::optional<t_pod_type>& a)
{
if (a.has_value())
return epee::string_tools::pod_to_hex(*a);
else
return "";
}
//basic helpers for pod-to-hex serialization
template<class t_pod_type>
std::string transform_t_pod_to_str(const t_pod_type & a)
{
return epee::string_tools::pod_to_hex(a);
return transform_t_pod_to_str_internal(a);
}
template<class t_pod_type>
template<class t_pod_type>
t_pod_type transform_str_to_t_pod(const std::string& a)
{
t_pod_type res = AUTO_VAL_INIT(res);
t_pod_type res = AUTO_VAL_INIT(res);
if (a.empty())
return res;
if constexpr (is_std_optional<t_pod_type>::value)
{
typename t_pod_type::value_type v = AUTO_VAL_INIT(v);
if (!epee::string_tools::hex_to_pod(a, v))
throw std::runtime_error(std::string("Unable to transform \"") + a + "\" to pod type " + typeid(typename t_pod_type::value_type).name());
return v;
}
if (!epee::string_tools::hex_to_pod(a, res))
throw std::runtime_error(std::string("Unable to transform \"") + a + "\" to pod type " + typeid(t_pod_type).name());
return res;
}
//basic helpers for blob-to-hex serialization
inline std::string transform_binbuf_to_hexstr(const std::string& a)

View file

@ -1,3 +1,4 @@
// Copyright (c) 2024, Zano Project
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
@ -359,8 +360,12 @@ namespace epee
template< class t_type_stored, class t_type, class t_storage, typename cb_serialize>
static bool serialize_ephemeral(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname, cb_serialize cb_s)
{
t_type_stored a = cb_s(d);
return epee::serialization::selector<true>::serialize(a, stg, hparent_section, pname);
t_type_stored a = AUTO_VAL_INIT(a);
bool add_val = cb_s(d, a);
if (add_val)
return epee::serialization::selector<true>::serialize(a, stg, hparent_section, pname);
else
return true;
}
};
@ -473,6 +478,29 @@ namespace epee
return r;
}
//-------------------------------------------------------------------------------------------------------------------
//std::optional
template<class t_type, class t_storage>
bool kv_serialize(const std::optional<t_type>& 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<class t_type, class t_storage>
bool kv_unserialize(std::optional<t_type>& 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<class t_type, class t_storage>
bool kv_serialize(const boost::shared_ptr<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
@ -496,6 +524,30 @@ namespace epee
}
return r;
}
//-------------------------------------------------------------------------------------------------------------------
//std::shared_ptr
template<class t_type, class t_storage>
bool kv_serialize(const std::shared_ptr<t_type>& 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<class t_type, class t_storage>
bool kv_unserialize(std::shared_ptr<t_type>& 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;
}
}
}

View file

@ -444,7 +444,7 @@ POP_GCC_WARNINGS
inline bool string_to_num_fast(const std::string& buff, int& val)
{
val = atoi(buff.c_str());
if(buff != "0" && val == 0)
if (val == 0 && buff.find_first_not_of('0') != std::string::npos)
return false;
return true;

View file

@ -116,6 +116,13 @@ else()
endif()
add_library(crypto ${CRYPTO})
if(USE_BITCOIN_SECP256K1_FOR_ECDSA)
add_dependencies(crypto secp256k1)
target_link_libraries(crypto secp256k1)
else()
add_dependencies(crypto OpenSSL::Crypto)
target_link_libraries(crypto OpenSSL::Crypto)
endif()
add_library(currency_core ${CURRENCY_CORE})
add_dependencies(currency_core version ${PCH_LIB_NAME})

View file

@ -3,8 +3,11 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#define BEGIN_BOOST_SERIALIZATION() template <class t_archive> inline void serialize(t_archive &_arch, const unsigned int ver) {
#include <type_traits>
#ifndef DISABLE_PFR_SERIALIZATION_SELFCHECK
#include <boost/pfr.hpp>
#endif
#define BEGIN_BOOST_SERIALIZATION() template <class t_archive> void serialize(t_archive &_arch, const unsigned int ver) {
template<size_t A, size_t B> struct TAssertEquality {
static_assert(A == B, "Serialization map is not updated, sizeof() missmatch");
@ -26,6 +29,60 @@ template<size_t A, size_t B> struct TAssertEquality {
#define END_BOOST_SERIALIZATION() }
/**********************************************************************************************************************************
This serialization closing macro adds self-validation by checking the total number of fields in the structure using boost::pfr.
Note: "num_fields" does NOT represent the number of fields included in the serialization. Instead, it indicates the total number
of fields in the structure, some of which might not be included in the serialization for valid reasons. If someone adds new
fields to the structure but forgets to update the serialization map, the compilation will fail. Any update to "num_fields" must
be accompanied by a thorough review of the serialization map to ensure no fields are omitted.
**********************************************************************************************************************************/
#ifndef DISABLE_PFR_SERIALIZATION_SELFCHECK
#define END_BOOST_SERIALIZATION_TOTAL_FIELDS(num_fields) static_assert(num_fields == boost::pfr::tuple_size<std::remove_reference<decltype(*this)>::type>::value, "Unexpected number of fields!"); }
#else
#define END_BOOST_SERIALIZATION_TOTAL_FIELDS(num_fields) END_BOOST_SERIALIZATION()
#endif
#define BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(current_version) static const unsigned int current_boost_version_serialization_version = current_version;
#define LOOP_BACK_BOOST_SERIALIZATION_VERSION(type) BOOST_CLASS_VERSION(type, type::current_boost_version_serialization_version);
template<bool IsSaving, typename destination_t>
struct boost_transition_t {};
template<typename destination_t>
struct boost_transition_t<true, destination_t>
{
template <typename archive, typename origin_type>
static void chain_serialize(archive& ar, const origin_type& origin_tx)
{
destination_t dst_tx = AUTO_VAL_INIT(dst_tx);
transition_convert(origin_tx, dst_tx);
ar & dst_tx;
}
};
template<typename destination_t>
struct boost_transition_t<false, destination_t>
{
template <typename archive, typename origin_type>
static void chain_serialize(archive& ar, origin_type& origin_tx)
{
// TODO: consider using move semantic for temporary 'dst_tx'
destination_t dst_tx = AUTO_VAL_INIT(dst_tx);
ar & dst_tx;
transition_convert(dst_tx, origin_tx);
}
};
#define BOOST_CHAIN_TRANSITION_VER(obj_version, old_type) if (obj_version == ver) {boost_transition_t<t_archive::is_saving::value, old_type>::chain_serialize(_arch, *this);return;}
#define BOOST_CHAIN_TRANSITION_IF_COND_TRUE(condition, old_type) if (condition) {boost_transition_t<t_archive::is_saving::value, old_type>::chain_serialize(_arch, *this);return;}
/*
example of use:

View file

@ -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,28 @@ namespace boost
{
a & reinterpret_cast<char (&)[sizeof(crypto::point_t)]>(x);
}
template <class Archive>
inline void serialize(Archive &a, crypto::eth_public_key &x, const boost::serialization::version_type ver)
{
a & reinterpret_cast<char (&)[sizeof(crypto::eth_public_key)]>(x);
}
template <class Archive>
inline void serialize(Archive &a, crypto::eth_signature &x, const boost::serialization::version_type ver)
{
a & reinterpret_cast<char (&)[sizeof(crypto::eth_signature)]>(x);
}
template <class Archive>
inline void serialize(Archive& a, crypto::scalar_vec_t& x, const boost::serialization::version_type ver)
{
static_assert(sizeof(std::vector<crypto::scalar_t>) == sizeof(crypto::scalar_vec_t));
a & static_cast<std::vector<crypto::scalar_t>&>(x);
}
template <class Archive, size_t N>
inline void serialize(Archive& a, crypto::scalar_mat_t<N>& x, const boost::serialization::version_type ver)
{
static_assert(sizeof(std::vector<crypto::scalar_t>) == sizeof(crypto::scalar_mat_t<N>));
a & static_cast<std::vector<crypto::scalar_t>&>(x);
}
} // namespace serialization
} // namespace boost

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2019 Zano Project
// Copyright (c) 2014-2024 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -394,10 +394,67 @@ namespace tools
}
return true;
}
const char* lmdb_db_backend::name()
{
return "lmdb";
}
bool lmdb_db_backend::convert_db_4kb_page_to_16kb_page(const std::string& source_path, const std::string& destination_path)
{
#define MDB_CHECK(x, msg) {int rc = x; CHECK_AND_ASSERT_MES(rc == MDB_SUCCESS, false, "LMDB 4k->16k error: " << msg << ": " << mdb_strerror(rc));}
MDB_env *env_src = nullptr, *env_dst = nullptr;
// source
MDB_CHECK(mdb_env_create(&env_src), "failed to create LMDB environment");
MDB_CHECK(mdb_env_set_mapsize(env_src, 4 * 1024 * 1024), "failed to set mapsize"); // mapsize ?
MDB_CHECK(mdb_env_open(env_src, source_path.c_str(), 0, 0664), "failed to open source LMDB");
// destination (16k page size)
MDB_CHECK(mdb_env_create(&env_dst), "failed to create LMDB environment");
MDB_CHECK(mdb_env_set_mapsize(env_dst, 16 * 1024 * 1024), "failed to set mapsize"); // mapsize ?
// TODO uncomment after mdb_env_set_pagesize is supported
// MDB_CHECK(mdb_env_set_pagesize(env_dst, 16 * 1024), "failed to set page size to 16K");
MDB_CHECK(mdb_env_open(env_dst, destination_path.c_str(), 0, 0664), "failed to open destination LMDB");
// begin transactions
MDB_txn *txn_src = nullptr, *txn_dst = nullptr;
MDB_dbi dbi_src, dbi_dst;
MDB_CHECK(mdb_txn_begin(env_src, nullptr, MDB_RDONLY, &txn_src), "failed to begin source transaction");
MDB_CHECK(mdb_dbi_open(txn_src, nullptr, 0, &dbi_src), "failed to open source database");
MDB_CHECK(mdb_txn_begin(env_dst, nullptr, 0, &txn_dst), "failed to begin destination transaction");
MDB_CHECK(mdb_dbi_open(txn_dst, nullptr, MDB_CREATE, &dbi_dst), "failed to open destination database");
MDB_cursor *cursor;
MDB_val key, data;
// Iterate over the source database and copy all key-value pairs to the destination database
MDB_CHECK(mdb_cursor_open(txn_src, dbi_src, &cursor), "failed to open cursor");
while (mdb_cursor_get(cursor, &key, &data, MDB_NEXT) == MDB_SUCCESS)
{
MDB_CHECK(mdb_put(txn_dst, dbi_dst, &key, &data, 0), "failed to put data in destination database");
}
mdb_cursor_close(cursor);
// commit transactions
MDB_CHECK(mdb_txn_commit(txn_src), "failed to commit source transaction");
MDB_CHECK(mdb_txn_commit(txn_dst), "failed to commit destination transaction");
mdb_dbi_close(env_src, dbi_src);
mdb_dbi_close(env_dst, dbi_dst);
mdb_env_close(env_src);
mdb_env_close(env_dst);
return true;
#undef MDB_CHECK
}
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2019 Zano Project
// Copyright (c) 2014-2024 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -36,6 +36,7 @@ namespace tools
boost::recursive_mutex m_write_exclusive_lock;
std::map<std::thread::id, transactions_list> m_txs; // size_t -> count of nested read_only transactions
bool pop_tx_entry(tx_entry& txe);
public:
lmdb_db_backend();
~lmdb_db_backend();
@ -60,6 +61,8 @@ namespace tools
bool have_tx();
MDB_txn* get_current_tx();
static bool convert_db_4kb_page_to_16kb_page(const std::string& source_path, const std::string& destination_path);
};
}
}

View file

@ -3391,5 +3391,9 @@ namespace tools
CHECK_AND_ASSERT_THROW_MES(it!= wordsMap.end(), "unable to find word \"" << w << "\" in mnemonic dictionary");
return it->second;
}
const map<string, uint32_t>& get_words_map()
{
return wordsMap;
}
}
}

View file

@ -47,5 +47,6 @@ namespace tools
std::string word_by_num(uint32_t n);
uint64_t num_by_word(const std::string& w);
bool valid_word(const std::string& w);
const std::map<std::string, uint32_t>& get_words_map();
}
}

View file

@ -21,8 +21,8 @@ namespace tools
};
#ifndef TESTNET
static constexpr pre_download_entry c_pre_download_mdbx = { "http://95.217.42.247/pre-download/zano_mdbx_95_2500000.pak", "8ffa2cb4213f4f96f97033c65a9e52bc350f683237808597784e79b24d5bfee7", 3242348793, 5905489920 };
static constexpr pre_download_entry c_pre_download_lmdb = { "http://95.217.42.247/pre-download/zano_lmdb_95_2500000.pak", "5509650e12c8f901e6731a2bfaf3abfd64409e3e1366d3d94cd11db8beddb0c3", 4239505801, 5893566464 };
static constexpr pre_download_entry c_pre_download_mdbx = { "http://95.217.42.247/pre-download/zano_mdbx_95_2892700.pak", "68e819cd119e4af1b81f1852e42978d662f1e6355124352f3e835db32b5a8230", 6414724487, 10468823040 };
static constexpr pre_download_entry c_pre_download_lmdb = { "http://95.217.42.247/pre-download/zano_lmdb_95_2892700.pak", "605eb4eb0903aa7b3a2a046514ef349d45c7de31d2702fd9dc104ca65705d6eb", 7860127140, 10204872704 };
#else
static constexpr pre_download_entry c_pre_download_mdbx = { "", "", 0, 0 };
static constexpr pre_download_entry c_pre_download_lmdb = { "", "", 0, 0 };

View file

@ -658,31 +658,68 @@ std::string get_nix_version_display_string()
return static_cast<uint64_t>(in.tellg());
}
bool check_remote_client_version(const std::string& client_ver)
bool parse_client_version(const std::string& str, int& major, int& minor, int& revision, int& build_number, std::string& commit_id, bool& dirty)
{
std::string v = client_ver.substr(0, client_ver.find('[')); // remove commit id
v = v.substr(0, v.rfind('.')); // remove build number
// "10.101.999.28391"
// "10.101.999.28391[deadbeef31337]"
// "10.101.999.28391[deadbeef31337-dirty]"
// 0123456789012345678901234567890123456
int v_major = 0, v_minor = 0, v_revision = 0;
size_t dot_pos = v.find('.');
if (dot_pos == std::string::npos || !epee::string_tools::string_to_num_fast(v.substr(0, dot_pos), v_major))
if (str.size() == 0)
return false;
v = v.substr(dot_pos + 1);
dot_pos = v.find('.');
if (!epee::string_tools::string_to_num_fast(v.substr(0, dot_pos), v_minor))
return false;
if (dot_pos != std::string::npos)
auto bracket_pos = str.find('[');
if (bracket_pos != std::string::npos)
{
// revision
v = v.substr(dot_pos + 1);
if (!epee::string_tools::string_to_num_fast(v, v_revision))
if (str[str.size() - 1] != ']')
return false;
commit_id = str.substr(bracket_pos + 1, str.size() - bracket_pos - 2);
auto d_pos = commit_id.find("-dirty");
if (d_pos != std::string::npos)
{
dirty = true;
commit_id.erase(d_pos);
}
}
// got v_major, v_minor, v_revision
std::string ver_str = str.substr(0, bracket_pos);
std::vector<std::string> versions;
boost::split(versions, ver_str, boost::is_any_of("."));
if (versions.size() != 4)
return false;
if (!epee::string_tools::string_to_num_fast(versions[0], major))
return false;
if (!epee::string_tools::string_to_num_fast(versions[1], minor))
return false;
if (!epee::string_tools::string_to_num_fast(versions[2], revision))
return false;
if (!epee::string_tools::string_to_num_fast(versions[3], build_number))
return false;
return true;
}
bool parse_client_version_build_number(const std::string& str, int& build_number)
{
int major = -1, minor = -1, revision = -1;
std::string commit_id;
bool dirty = false;
return tools::parse_client_version(str, major, minor, revision, build_number, commit_id, dirty);
}
bool check_remote_client_version(const std::string& client_ver)
{
int v_major = 0, v_minor = 0, v_revision = 0, v_build_number = 0;
std::string commit_id;
bool dirty_flag = false;
if (!parse_client_version(client_ver, v_major, v_minor, v_revision, v_build_number, commit_id, dirty_flag))
return false;
// allow 2.x and greater
if (v_major < 2)

View file

@ -37,6 +37,9 @@ namespace tools
std::string get_current_username();
std::string get_os_version_string();
bool copy_dir(boost::filesystem::path const & source, boost::filesystem::path const & destination);
bool parse_client_version(const std::string& str, int& major, int& minor, int& revision, int& build_number, std::string& commit_id, bool& dirty);
bool parse_client_version_build_number(const std::string& str, int& build_number);
bool check_remote_client_version(const std::string& client_ver);
bool create_directories_if_necessary(const std::string& path);

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2019 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
@ -25,6 +25,7 @@ using namespace epee;
#include "storages/http_abstract_invoke.h"
#include "net/http_client.h"
#include "currency_core/genesis_acc.h"
#include "common/db_backend_lmdb.h"
#include <cstdlib>
namespace po = boost::program_options;
@ -63,6 +64,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_pack_file ("pack-file", "perform gzip-packing and calculate hash for a given file");
const command_line::arg_descriptor<std::string> arg_unpack_file ("unpack-file", "Perform gzip-unpacking and calculate hash for a given file");
const command_line::arg_descriptor<std::string> arg_target_file ("target-file", "Specify target file for pack-file and unpack-file commands");
const command_line::arg_descriptor<std::string> arg_lmdb_page_4to16 ("convert-lmdb-4to16", "Perform LMDB conversion from 4k page size to 16k page size");
//const command_line::arg_descriptor<std::string> arg_send_ipc ("send-ipc", "Send IPC request to UI");
}
@ -1220,7 +1222,10 @@ bool handle_pack_file(po::variables_map& vm)
}
if (!command_line::has_arg(vm, arg_target_file))
{
std::cout << "Error: Parameter target_file is not set." << ENDL;
return false;
}
path_target = command_line::get_arg(vm, arg_target_file);
std::ifstream source;
@ -1251,6 +1256,34 @@ bool handle_pack_file(po::variables_map& vm)
}
}
bool handle_lmdb_page_4to16(po::variables_map& vm)
{
std::string path_source;
std::string path_target;
if (!command_line::has_arg(vm, arg_lmdb_page_4to16))
return false;
path_source = command_line::get_arg(vm, arg_lmdb_page_4to16);
if (!command_line::has_arg(vm, arg_target_file))
{
std::cout << "Error: Parameter target_file is not set." << ENDL;
return false;
}
path_target = command_line::get_arg(vm, arg_target_file);
if (tools::db::lmdb_db_backend::convert_db_4kb_page_to_16kb_page(path_source, path_target))
{
std::cout << "Conversion failed" << ENDL;
return false;
}
std::cout << "Converted successfully" << ENDL;
return true;
}
//---------------------------------------------------------------------------------------------------------------
int main(int argc, char* argv[])
@ -1375,6 +1408,10 @@ int main(int argc, char* argv[])
{
return handle_pack_file(vm) ? EXIT_SUCCESS : EXIT_FAILURE;
}
else if (command_line::has_arg(vm, arg_lmdb_page_4to16))
{
return handle_lmdb_page_4to16(vm) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/*else if (command_line::has_arg(vm, arg_send_ipc))
{
handle_send_ipc(command_line::get_arg(vm, arg_send_ipc)) ? EXIT_SUCCESS : EXIT_FAILURE;

View file

@ -8,6 +8,7 @@
#include <string>
#include <boost/multiprecision/cpp_int.hpp>
#include "crypto.h"
#include "eth_signature.h"
namespace crypto
{
@ -50,38 +51,42 @@ namespace crypto
}
template<class pod_t>
std::string pod_to_hex_reversed(const pod_t &h)
inline std::string buff_to_hex(const void* pdata, size_t len, bool reversed = false)
{
constexpr char hexmap[] = "0123456789abcdef";
const unsigned char* data = reinterpret_cast<const unsigned char*>(&h);
size_t len = sizeof h;
const unsigned char* data = reinterpret_cast<const unsigned char*>(pdata);
std::string s(len * 2, ' ');
for (size_t i = 0; i < len; ++i)
if (!reversed)
{
s[2 * i] = hexmap[data[len - 1 - i] >> 4];
s[2 * i + 1] = hexmap[data[len - 1 - i] & 0x0F];
for (size_t i = 0; i < len; ++i)
{
s[2 * i] = hexmap[data[i] >> 4];
s[2 * i + 1] = hexmap[data[i] & 0x0F];
}
}
else
{
for (size_t i = 0; i < len; ++i)
{
s[2 * i] = hexmap[data[len - 1 - i] >> 4];
s[2 * i + 1] = hexmap[data[len - 1 - i] & 0x0F];
}
}
return s;
}
template<class pod_t>
std::string pod_to_hex_reversed(const pod_t &h)
{
return buff_to_hex(&h, sizeof h, true);
}
template<class pod_t>
std::string pod_to_hex(const pod_t &h)
{
constexpr char hexmap[] = "0123456789abcdef";
const unsigned char* data = reinterpret_cast<const unsigned char*>(&h);
size_t len = sizeof h;
std::string s(len * 2, ' ');
for (size_t i = 0; i < len; ++i)
{
s[2 * i] = hexmap[data[i] >> 4];
s[2 * i + 1] = hexmap[data[i] & 0x0F];
}
return s;
return buff_to_hex(&h, sizeof h);
}
template<class pod_t>
@ -811,26 +816,7 @@ namespace crypto
friend bool operator==(const point_t& lhs, const point_t& rhs)
{
// TODO: @#@# (performance) consider checking (lhs - rhs).is_zero() instead
// convert to xy form, then compare components (because (x, y, z, t) representation is not unique)
fe lrecip, lx, ly;
fe rrecip, rx, ry;
fe_invert(lrecip, lhs.m_p3.Z);
fe_invert(rrecip, rhs.m_p3.Z);
fe_mul(lx, lhs.m_p3.X, lrecip);
fe_mul(rx, rhs.m_p3.X, rrecip);
if (memcmp(&lx, &rx, sizeof lx) != 0)
return false;
fe_mul(ly, lhs.m_p3.Y, lrecip);
fe_mul(ry, rhs.m_p3.Y, rrecip);
if (memcmp(&ly, &ry, sizeof ly) != 0)
return false;
return true;
return (lhs - rhs).is_zero();
}
friend bool operator!=(const point_t& lhs, const point_t& rhs)
@ -1097,7 +1083,6 @@ namespace crypto
make_random();
}
}; // scalar_vec_t
@ -1209,6 +1194,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);

View file

@ -0,0 +1,404 @@
// 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"
#ifndef USE_OPEN_SSL_FOR_ECDSA
#include "bitcoin-secp256k1/include/secp256k1.h"
#endif
#include "random.h"
#include "misc_language.h"
#include <string_tools.h>
#ifdef USE_OPEN_SSL_FOR_ECDSA
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <openssl/bn.h>
#include <openssl/rand.h>
#endif
// Function to create EC_KEY from raw 32 - byte private key
EC_KEY * create_ec_key_from_private_key(const unsigned char* private_key) {
EC_KEY* key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (!key) {
std::cerr << "Failed to create new EC Key" << std::endl;
return nullptr;
}
BIGNUM* priv_key_bn = BN_bin2bn(private_key, 32, nullptr);
if (!priv_key_bn) {
std::cerr << "Failed to convert private key to BIGNUM" << std::endl;
EC_KEY_free(key);
return nullptr;
}
if (!EC_KEY_set_private_key(key, priv_key_bn)) {
std::cerr << "Failed to set private key" << std::endl;
EC_KEY_free(key);
BN_free(priv_key_bn);
return nullptr;
}
BN_free(priv_key_bn);
return key;
}
void ensure_canonical_s(BIGNUM* s, const EC_GROUP* group) {
// Get the order of the curve
BIGNUM* order = BN_new();
EC_GROUP_get_order(group, order, nullptr);
// Compute half of the order: `n / 2`
BIGNUM* half_order = BN_new();
BN_rshift1(half_order, order);
// If `s` is greater than `n / 2`, replace `s` with `n - s`
if (BN_cmp(s, half_order) > 0) {
BN_sub(s, order, s);
}
BN_free(order);
BN_free(half_order);
}
// Update the function to ensure canonical `s`
bool generate_ethereum_signature(const unsigned char* hash, const unsigned char* private_key, crypto::eth_signature& sig_res) {
EC_KEY* ec_key = create_ec_key_from_private_key(private_key);
if (!ec_key) {
throw std::runtime_error("Failed to create EC key from private key");
}
// Sign the hash
unsigned int sig_len = ECDSA_size(ec_key);
std::vector<unsigned char> signature(sig_len);
if (ECDSA_sign(0, hash, 32, signature.data(), &sig_len, ec_key) == 0) {
EC_KEY_free(ec_key);
throw std::runtime_error("Failed to create signature");
}
signature.resize(sig_len);
// The OpenSSL ECDSA signature output is DER encoded, Ethereum expects (r, s, v)
const unsigned char* p = signature.data();
ECDSA_SIG* sig = d2i_ECDSA_SIG(nullptr, &p, sig_len);
if (!sig) {
EC_KEY_free(ec_key);
throw std::runtime_error("Failed to parse ECDSA signature");
}
const BIGNUM* r = nullptr;
const BIGNUM* s = nullptr;
ECDSA_SIG_get0(sig, &r, &s);
// Ensure canonical `s`
BIGNUM* s_canonical = BN_dup(s);
ensure_canonical_s(s_canonical, EC_KEY_get0_group(ec_key));
BN_bn2binpad(r, (unsigned char* )&sig_res.data[0], 32);
BN_bn2binpad(s_canonical, (unsigned char*)&sig_res.data[32], 32);
ECDSA_SIG_free(sig);
BN_free(s_canonical);
EC_KEY_free(ec_key);
return true;
}
// Convert raw 33-byte compressed public key to EC_KEY object
EC_KEY* create_ec_key_from_compressed_public_key(const unsigned char* compressed_pub_key) {
EC_KEY* key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (!key) {
std::cerr << "Failed to create EC_KEY object" << std::endl;
return nullptr;
}
EC_POINT* pub_point = EC_POINT_new(EC_KEY_get0_group(key));
if (!EC_POINT_oct2point(EC_KEY_get0_group(key), pub_point, compressed_pub_key, 33, nullptr)) {
std::cerr << "Failed to convert compressed public key" << std::endl;
EC_POINT_free(pub_point);
EC_KEY_free(key);
return nullptr;
}
if (!EC_KEY_set_public_key(key, pub_point)) {
std::cerr << "Failed to set public key" << std::endl;
EC_POINT_free(pub_point);
EC_KEY_free(key);
return nullptr;
}
EC_POINT_free(pub_point);
return key;
}
// Function to verify Ethereum-compatible signature
bool verify_ethereum_signature(const crypto::hash& m, const crypto::eth_signature& sig_res, const crypto::eth_public_key& compressed_pub_key) {
EC_KEY* ec_key = create_ec_key_from_compressed_public_key((const unsigned char*)&compressed_pub_key.data[0]);
if (!ec_key) {
throw std::runtime_error("Failed to create EC key from compressed public key");
}
const unsigned char* r = (unsigned char*)&sig_res.data[0];
const unsigned char* s = (unsigned char*)&sig_res.data[32];
const unsigned char* hash = (unsigned char*)&m;
// Create ECDSA_SIG from r and s
BIGNUM* bn_r = BN_bin2bn(r, 32, nullptr);
BIGNUM* bn_s = BN_bin2bn(s, 32, nullptr);
if (!bn_r || !bn_s) {
EC_KEY_free(ec_key);
BN_free(bn_r);
BN_free(bn_s);
throw std::runtime_error("Failed to convert r or s to BIGNUM");
}
ECDSA_SIG* sig = ECDSA_SIG_new();
if (!sig) {
EC_KEY_free(ec_key);
BN_free(bn_r);
BN_free(bn_s);
throw std::runtime_error("Failed to create ECDSA_SIG object");
}
if (!ECDSA_SIG_set0(sig, bn_r, bn_s)) {
EC_KEY_free(ec_key);
ECDSA_SIG_free(sig);
BN_free(bn_r);
BN_free(bn_s);
throw std::runtime_error("Failed to set r and s in ECDSA_SIG");
}
// Verify the signature
int verification_result = ECDSA_do_verify(hash, 32, sig, ec_key);
ECDSA_SIG_free(sig);
EC_KEY_free(ec_key);
return verification_result == 1;
}
//
// struct KeyPair {
// std::vector<unsigned char> private_key; // 32 bytes
// std::vector<unsigned char> public_key; // 33 bytes (compressed format)
// };
// Function to generate an Ethereum-compatible key pair
bool generate_ethereum_key_pair(crypto::eth_secret_key& sec_key, crypto::eth_public_key& pub_key) {
/*KeyPair keypair;*/
// Create a new EC_KEY object with the secp256k1 curve
EC_KEY* key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (!key) {
throw std::runtime_error("Failed to create new EC_KEY object");
}
// Generate the key pair
if (EC_KEY_generate_key(key) == 0) {
EC_KEY_free(key);
throw std::runtime_error("Failed to generate key pair");
}
// Extract the private key
const BIGNUM* priv_bn = EC_KEY_get0_private_key(key);
if (!priv_bn) {
EC_KEY_free(key);
throw std::runtime_error("Failed to get private key");
}
BN_bn2binpad(priv_bn, (unsigned char*)&sec_key.data[0], 32);
// Extract the public key in compressed format
const EC_POINT* pub_point = EC_KEY_get0_public_key(key);
if (!pub_point) {
EC_KEY_free(key);
throw std::runtime_error("Failed to get public key");
}
//keypair.public_key.resize(33); // Compressed format
if (EC_POINT_point2oct(EC_KEY_get0_group(key), pub_point, POINT_CONVERSION_COMPRESSED,
(unsigned char*)&pub_key.data[0], sizeof(pub_key.data), nullptr) == 0) {
EC_KEY_free(key);
throw std::runtime_error("Failed to convert public key to compressed format");
}
EC_KEY_free(key);
return true;
}
namespace crypto
{
bool generate_eth_key_pair(eth_secret_key& sec_key, eth_public_key& pub_key) noexcept
{
try
{
#ifndef USE_OPEN_SSL_FOR_ECDSA
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;
#else
return generate_ethereum_key_pair(sec_key, pub_key);
#endif
}
catch(...)
{
return false;
}
}
#ifndef USE_OPEN_SSL_FOR_ECDSA
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;
}
}
#endif
// generates secp256k1 ECDSA signature
bool generate_eth_signature(const hash& m, const eth_secret_key& sec_key, eth_signature& sig) noexcept
{
try
{
#ifndef USE_OPEN_SSL_FOR_ECDSA
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;
#else
return generate_ethereum_signature((const unsigned char*)&m.data, (unsigned char*)&sec_key.data, sig);
#endif
}
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
#ifndef USE_OPEN_SSL_FOR_ECDSA
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;
#else
return verify_ethereum_signature(m, sig, pub_key);
#endif
}
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);
}
std::ostream& operator<<(std::ostream& o, const eth_signature& v)
{
return o << epee::string_tools::pod_to_hex(v);
}
} // namespace crypto

View file

@ -0,0 +1,67 @@
// 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 <cstdint>
#include <iosfwd>
#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);
std::ostream& operator<<(std::ostream& o, const eth_signature& v);
} // namespace crypto

View file

@ -1,10 +1,11 @@
// Copyright (c) 2023 Zano Project
// Copyright (c) 2023 sowle (val@zano.org, crypto.sowle@gmail.com)
// Copyright (c) 2023-2024 Zano Project
// Copyright (c) 2023-2024 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 "../currency_core/currency_config.h" // for static asset checks
#include "epee/include/misc_log_ex.h"
//DISABLE_GCC_AND_CLANG_WARNING(unused-function)
@ -22,6 +23,8 @@ namespace crypto
static const size_t N_max = 256;
static const size_t mn_max = 16;
static_assert(CURRENCY_TX_MAX_ALLOWED_INPUTS <= N_max, "CURRENCY_TX_MAX_ALLOWED_INPUTS is inconsistent with one-out-of-many proof limits"); // TODO: consider moving this check out -- sowle
const point_t& get_BGE_generator(size_t index, bool& ok)
{
static std::vector<point_t> precalculated_generators;

View file

@ -43,7 +43,7 @@ void generate_system_random_bytes(size_t n, void *result) {
void generate_system_random_bytes(size_t n, void *result) {
int fd;
if ((fd = open("/dev/urandom", O_RDONLY | O_NOCTTY | O_CLOEXEC)) < 0) {
err(EXIT_FAILURE, "open /dev/urandom");
exit(EXIT_FAILURE);
}
for (;;) {
ssize_t res = read(fd, result, n);
@ -52,17 +52,17 @@ void generate_system_random_bytes(size_t n, void *result) {
}
if (res < 0) {
if (errno != EINTR) {
err(EXIT_FAILURE, "read /dev/urandom");
exit(EXIT_FAILURE);
}
} else if (res == 0) {
errx(EXIT_FAILURE, "read /dev/urandom: end of file");
exit(EXIT_FAILURE);
} else {
result = padd(result, (size_t) res);
n -= (size_t) res;
}
}
if (close(fd) < 0) {
err(EXIT_FAILURE, "close /dev/urandom");
exit(EXIT_FAILURE);
}
}

View file

@ -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<generator_tag gen = gt_G>
inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept;

View file

@ -60,7 +60,7 @@ namespace currency
return m_keys;
}
//-----------------------------------------------------------------
void crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password)
void account_base::crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password)
{
crypto::chacha8_key key = AUTO_VAL_INIT(key);
crypto::generate_chacha8_key(password, key);
@ -71,28 +71,36 @@ namespace currency
crypto::chacha8(scr_data, src_length, key, iv, (char*)dst_data);
}
//-----------------------------------------------------------------
std::string account_base::get_seed_phrase(const std::string& password) const
std::string account_base::get_seed_phrase(const std::string& password) const
{
if (m_keys_seed_binary.empty())
return "";
return get_seed_phrase(password, m_keys_seed_binary);
}
//-----------------------------------------------------------------
std::string account_base::get_seed_phrase(const std::string& password, const std::vector<unsigned char>& keys_seed_binary) const
{
if (keys_seed_binary.empty())
return "";
std::vector<unsigned char> processed_seed_binary = m_keys_seed_binary;
std::vector<unsigned char> processed_seed_binary = keys_seed_binary;
if (!password.empty())
{
CHECK_AND_ASSERT_THROW_MES(currency::validate_password(password), "seed phrase password contains invalid characters, seed phrase cannot be created with such a password");
//encrypt seed phrase binary data
crypt_with_pass(&m_keys_seed_binary[0], m_keys_seed_binary.size(), &processed_seed_binary[0], password);
crypt_with_pass(&keys_seed_binary[0], keys_seed_binary.size(), &processed_seed_binary[0], password);
}
std::string keys_seed_text = tools::mnemonic_encoding::binary2text(processed_seed_binary);
std::string timestamp_word = currency::get_word_from_timstamp(m_creation_timestamp, !password.empty());
std::string timestamp_word = currency::get_word_from_timestamp(m_creation_timestamp, !password.empty());
// floor creation time to WALLET_BRAIN_DATE_QUANTUM to make checksum calculation stable
bool self_check_is_password_used = false;
uint64_t creation_timestamp_rounded = get_timstamp_from_word(timestamp_word, self_check_is_password_used);
uint64_t creation_timestamp_rounded = get_timestamp_from_word(timestamp_word, self_check_is_password_used);
CHECK_AND_ASSERT_THROW_MES(self_check_is_password_used == !password.empty(), "Account seed phrase internal error: password flag encoded wrong");
constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum
std::string binary_for_check_sum((const char*)&m_keys_seed_binary[0], m_keys_seed_binary.size());
std::string binary_for_check_sum((const char*)&keys_seed_binary[0], keys_seed_binary.size());
binary_for_check_sum.append(password);
crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size());
*reinterpret_cast<uint64_t*>(&h) = creation_timestamp_rounded;
@ -100,6 +108,9 @@ namespace currency
uint64_t h_64 = *reinterpret_cast<uint64_t*>(&h);
uint16_t checksum = h_64 % (checksum_max + 1);
if (checksum == checksum_max) // workaround for incorrect checksum calculation (trying to keep the whole scheme untouched) -- sowle
checksum = 0;
uint8_t auditable_flag = 0;
if (m_keys.account_address.flags & ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE)
auditable_flag = 1;
@ -127,11 +138,12 @@ namespace currency
return true;
}
//-----------------------------------------------------------------
bool account_base::restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password)
bool account_base::restore_from_seed_phrase(const std::string& seed_phrase_, const std::string& seed_password)
{
//cut the last timestamp word from restore_dats
std::list<std::string> words;
boost::split(words, seed_phrase, boost::is_space());
std::string seed_phrase = epee::string_tools::trim(seed_phrase_);
boost::split(words, seed_phrase, boost::is_space(), boost::token_compress_on);
std::string keys_seed_text, timestamp_word, auditable_flag_and_checksum_word;
if (words.size() == SEED_PHRASE_V1_WORDS_COUNT)
@ -176,7 +188,7 @@ namespace currency
bool has_password = false;
try {
m_creation_timestamp = get_timstamp_from_word(timestamp_word, has_password);
m_creation_timestamp = get_timestamp_from_word(timestamp_word, has_password);
}
catch (...)
{
@ -207,6 +219,10 @@ namespace currency
h = crypto::cn_fast_hash(&h, sizeof h);
uint64_t h_64 = *reinterpret_cast<uint64_t*>(&h);
uint16_t checksum_calculated = h_64 % (checksum_max + 1);
if (checksum_calculated == checksum_max) // workaround for incorrect checksum calculation (trying to keep the whole scheme untouched) -- sowle
checksum_calculated = 0;
if (checksum != checksum_calculated)
{
LOG_PRINT_L0("seed phase has invalid checksum: " << checksum_calculated << ", while " << checksum << " is expected, check your words");
@ -221,6 +237,8 @@ namespace currency
if (auditable_flag)
m_keys.account_address.flags |= ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE;
else
m_keys.account_address.flags &= ~ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE;
return true;
}
@ -230,11 +248,13 @@ namespace currency
return seed_phrase.find(':') != std::string::npos;
}
//-----------------------------------------------------------------
bool account_base::is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected)
bool account_base::is_seed_password_protected(const std::string& seed_phrase_, bool& is_password_protected)
{
//cut the last timestamp word from restore_dats
std::list<std::string> words;
boost::split(words, seed_phrase, boost::is_space());
std::string seed_phrase = epee::string_tools::trim(seed_phrase_);
boost::split(words, seed_phrase, boost::is_space(), boost::token_compress_on);
//let's validate each word
for (const auto& w: words)
@ -260,7 +280,7 @@ namespace currency
return false;
}
get_timstamp_from_word(timestamp_word, is_password_protected);
get_timestamp_from_word(timestamp_word, is_password_protected);
return true;
}

View file

@ -56,6 +56,7 @@ namespace currency
std::string get_public_address_str() const;
std::string get_seed_phrase(const std::string& seed_password) const;
std::string get_seed_phrase(const std::string& password, const std::vector<unsigned char>& keys_seed_binary) const;
std::string get_tracking_seed() const;
bool restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password);
bool restore_from_tracking_seed(const std::string& tracking_seed);
@ -82,6 +83,8 @@ namespace currency
static std::vector<unsigned char> string_to_vector_of_chars(const std::string& v) { return std::vector<unsigned char>(v.begin(), v.end()); }
static bool is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected);
static bool is_seed_tracking(const std::string& seed_phrase);
static void crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password);
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_keys)

View file

@ -830,7 +830,7 @@ bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& t
fee = get_tx_fee(tx_res_ptr->tx);
purge_transaction_keyimages_from_blockchain(tx, true);
bool r = unprocess_blockchain_tx_extra(tx);
bool r = unprocess_blockchain_tx_extra(tx, tx_res_ptr->m_keeper_block_height);
CHECK_AND_ASSERT_MES(r, false, "failed to unprocess_blockchain_tx_extra for tx " << tx_id);
r = unprocess_blockchain_tx_attachments(tx, get_current_blockchain_size(), 0/*TODO: add valid timestamp here in future if need*/);
@ -1553,8 +1553,20 @@ bool blockchain_storage::create_block_template(const create_block_template_param
}
diffic = get_next_diff_conditional(pos);
CHECK_AND_ASSERT_MES(diffic, false, "get_next_diff_conditional failed");
// check PoW block timestamp against the current blockchain timestamp median -- if it's not okay, don't create a new block
// TODO (performance) both get_next_diff_conditional and get_last_n_blocks_timestamps obtains last N blocks, consider data reusing -- sowle
if (!pos && !params.ignore_pow_ts_check)
{
uint64_t median_ts = get_last_n_blocks_timestamps_median(BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW);
if(b.timestamp < median_ts)
{
LOG_PRINT_YELLOW("Block template construction failed because current core timestamp, " << b.timestamp << ", is less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts, LOG_LEVEL_2);
return false;
}
}
CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead.");
@ -3805,7 +3817,7 @@ bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::unprocess_blockchain_tx_extra(const transaction& tx)
bool blockchain_storage::unprocess_blockchain_tx_extra(const transaction& tx, const uint64_t height)
{
tx_extra_info ei = AUTO_VAL_INIT(ei);
bool r = parse_and_validate_tx_extra(tx, ei);
@ -3818,9 +3830,7 @@ bool blockchain_storage::unprocess_blockchain_tx_extra(const transaction& tx)
if (ei.m_asset_operation.operation_type != ASSET_DESCRIPTOR_OPERATION_UNDEFINED)
{
crypto::public_key asset_id = currency::null_pkey;
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(ei.m_asset_operation, nullptr, &asset_id), false, "get_or_calculate_asset_id failed");
r = pop_asset_info(asset_id);
r = pop_asset_info(ei.m_asset_operation, height);
CHECK_AND_ASSERT_MES(r, false, "failed to pop_alias_info");
}
return true;
@ -3862,15 +3872,16 @@ bool blockchain_storage::get_asset_info(const crypto::public_key& asset_id, asse
{
CRITICAL_REGION_LOCAL(m_read_lock);
auto as_ptr = m_db_assets.find(asset_id);
if (as_ptr)
{
if (as_ptr->size())
{
result = as_ptr->back().descriptor;
return true;
}
}
return false;
if (!as_ptr)
return false;
if (as_ptr->empty())
return false;
// the last history item must have opt_descriptor, but we check it just to be sure
if (!as_ptr->back().opt_descriptor.has_value())
return false;
result = as_ptr->back().opt_descriptor.get();
return true;
}
//------------------------------------------------------------------
uint64_t blockchain_storage::get_assets(uint64_t offset, uint64_t count, std::list<asset_descriptor_with_id>& assets) const
@ -3880,19 +3891,21 @@ uint64_t blockchain_storage::get_assets(uint64_t offset, uint64_t count, std::li
m_db_assets.enumerate_items([&](uint64_t i, const crypto::public_key& asset_id, const std::list<asset_descriptor_operation>& asset_descriptor_history)
{
if (i < offset)
{
return true;
}
return true; // continue
CHECK_AND_ASSERT_THROW_MES(asset_descriptor_history.size(), "unexpectedly, the asset_descriptor_history is empty; asset_id: " << asset_id);
CHECK_AND_ASSERT_THROW_MES(asset_descriptor_history.back().opt_descriptor.has_value(), "the last element of asset_descriptor_history doesn't have descriptor; asset_id: " << asset_id);
asset_descriptor_with_id& added_item = assets.emplace_back();
asset_descriptor_base& added_item_adb = static_cast<asset_descriptor_base&>(added_item);
added_item_adb = asset_descriptor_history.back().opt_descriptor.get();
replace_asset_ticker_and_full_name_if_invalid(added_item_adb, asset_id);
added_item.asset_id = asset_id;
CHECK_AND_ASSERT_THROW_MES(asset_descriptor_history.size(), "asset_descriptor_history unexpectedly have 0 size");
assets.push_back(asset_descriptor_with_id());
static_cast<asset_descriptor_base&>(assets.back()) = asset_descriptor_history.back().descriptor;
assets.back().asset_id = asset_id;
if (assets.size() >= count)
{
return false;
return false; // stop
}
return true;
return true; // continue
});
return assets.size();
}
@ -4010,7 +4023,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());
@ -4085,133 +4099,399 @@ bool blockchain_storage::put_alias_info(const transaction & tx, extra_alias_entr
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::pop_asset_info(const crypto::public_key& asset_id)
bool blockchain_storage::pop_asset_info(const asset_descriptor_operation& ado, const uint64_t height)
{
CRITICAL_REGION_LOCAL(m_read_lock);
crypto::public_key asset_id = currency::null_pkey;
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(ado, nullptr, &asset_id), false, "get_or_calculate_asset_id failed");
auto asset_history_ptr = m_db_assets.find(asset_id);
CHECK_AND_ASSERT_MES(asset_history_ptr && asset_history_ptr->size(), false, "empty name list in pop_asset_info");
assets_container::t_value_type local_asset_hist = *asset_history_ptr;
local_asset_hist.pop_back();
if (is_hardfork_active_for_height(ZANO_HARDFORK_05, height))
{
// NEW HF5 handling
assets_container::t_value_type local_asset_hist = *asset_history_ptr;
asset_descriptor_operation& last_ado = local_asset_hist.back(); // above we made sure that the history isn't empty
CHECK_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "opt_descriptor is missing during asset pop, op: " << (int)ado.operation_type);
asset_descriptor_base& last_adb = last_ado.opt_descriptor.get();
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT)
{
// just change the most recent history record, don't pop
if (last_adb.hidden_supply)
{
//CHECK_AND_ASSERT_MES(last_ado.opt_amount_commitment.has_value() && ado.opt_amount_commitment.has_value(), false, "last_ado.opt_amount_commitment or ado.opt_amount_commitment is missing (emit)");
//last_ado.opt_amount_commitment.get() = (crypto::point_t(last_ado.opt_amount_commitment.get()) - crypto::point_t(ado.opt_amount_commitment.get())).to_public_key();
return false; // not supported atm
}
else
{
CHECK_AND_ASSERT_MES(ado.opt_amount.has_value(), false, "last_ado.opt_amount or ado.opt_amount is missing (emit)");
last_adb.current_supply -= ado.opt_amount.get();
}
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN)
{
// just change the most recent history record, don't pop
if (last_adb.hidden_supply)
{
//CHECK_AND_ASSERT_MES(last_ado.opt_amount_commitment.has_value() && ado.opt_amount_commitment.has_value(), false, "last_ado.opt_amount_commitment or ado.opt_amount_commitment is missing (burn)");
//last_ado.opt_amount_commitment.get() = (crypto::point_t(last_ado.opt_amount_commitment.get()) + crypto::point_t(ado.opt_amount_commitment.get())).to_public_key();
return false; // not supported atm
}
else
{
CHECK_AND_ASSERT_MES(ado.opt_amount.has_value(), false, "last_ado.opt_amount or ado.opt_amount is missing (burn)");
last_adb.current_supply += ado.opt_amount.get();
}
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
{
// just pop the most recent history record
local_asset_hist.pop_back();
}
else
{
CHECK_AND_ASSERT_MES(false, false, "invalid operation: " << (int)ado.operation_type);
}
}
else
{
// HF4
local_asset_hist.pop_back();
}
// both HF4 and HF5
if (local_asset_hist.size())
m_db_assets.set(asset_id, local_asset_hist);
else
m_db_assets.erase(asset_id);
LOG_PRINT_MAGENTA("[ASSET_POP]: " << asset_id << ": " << (!local_asset_hist.empty() ? "(prev)" : "(erased)"), LOG_LEVEL_1);
return true;
}
//------------------------------------------------------------------
bool validate_ado_ownership(asset_op_verification_context& avc)
bool blockchain_storage::validate_ado_ownership(asset_op_verification_context& avc) const
{
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(last_ado.opt_descriptor.has_value(), false, "last ado is missing opt_descriptor; asset id: " << avc.asset_id);
const asset_descriptor_base& last_adb = last_ado.opt_descriptor.get();
CHECK_AND_ASSERT_MES(avc.asset_op_history->size() != 0, false, "asset with id " << avc.asset_id << " has invalid history size() == 0");
if (is_hardfork_active_for_height(ZANO_HARDFORK_05, avc.height))
{
if (last_adb.owner_eth_pub_key.has_value())
{
CHECK_AND_ASSERT_MES(last_adb.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");
if (!crypto::verify_eth_signature(avc.tx_id, last_adb.owner_eth_pub_key.value(), aoop_eth.eth_sig))
{
LOG_ERROR("Failed to validate secp256k1 signature for hash: " << avc.tx_id << ", signature: " << aoop_eth.eth_sig);
return false;
}
else
{
return true;
}
}
// owner_eth_pub_key has no value -- fallback to default
}
crypto::public_key owner_key = avc.asset_op_history->back().descriptor.owner;
return crypto::verify_schnorr_sig(avc.tx_id, owner_key, aoop.gss);
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_adb.owner, aoop.gss);
}
//------------------------------------------------------------------
bool blockchain_storage::validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const
bool blockchain_storage::validate_asset_operation_hf4(asset_op_verification_context& avc) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
CHECK_AND_ASSERT_MES(!is_hardfork_active_for_height(ZANO_HARDFORK_05, avc.height), false, "validate_asset_operation_hf4 was called in HF5");
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(avc.ado, &avc.asset_id_pt, &avc.asset_id), false, "get_or_calculate_asset_id failed");
avc.asset_op_history = m_db_assets.find(avc.asset_id);
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!");
}
CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing while registering asset " << avc.asset_id);
// CZ please review the following two added lines, do we need them _here_? I think they should go to hardfork_specific_terms...
// anyway, we musn't miss these checks for the sake of consensus
CHECK_AND_ASSERT_MES(!ado.opt_descriptor.get().owner_eth_pub_key.has_value(), false, "owner_eth_pub_key is prohibited before HF5");
CHECK_AND_ASSERT_MES(!ado.opt_asset_id_salt.has_value(), false, "opt_asset_id_salt is prohibited before HF5");
CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing while registering asset " << avc.asset_id);
avc.amount_to_validate = ado.opt_descriptor.get().current_supply;
}
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_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "opt_descriptor is missing in last ado, op: " << (int)ado.operation_type);
const asset_descriptor_base& last_adb = last_ado.opt_descriptor.get();
CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing in ado, op: " << (int)ado.operation_type);
const asset_descriptor_base adb = ado.opt_descriptor.get();
// 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(adb.current_supply == last_adb.current_supply, false, "update operation attempted to change emission, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(adb, last_adb), 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(adb.current_supply > last_adb.current_supply, false, "emit operation does not increase the current supply, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(adb, last_adb), false, "emit operation modifies asset descriptor in a prohibited manner");
CHECK_AND_ASSERT_MES(adb.meta_info == last_adb.meta_info, false, "emit operation is not allowed to update meta info");
avc.amount_to_validate = adb.current_supply - last_adb.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(adb.current_supply < last_adb.current_supply, false, "burn operation does not decrease the current supply, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(adb, last_adb), false, "burn operation modifies asset descriptor in a prohibited manner");
CHECK_AND_ASSERT_MES(adb.meta_info == last_adb.meta_info, false, "burn operation is not allowed to update meta info");
avc.amount_to_validate = last_adb.current_supply - adb.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;
}
//------------------------------------------------------------------
bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado)
bool blockchain_storage::validate_asset_operation_hf5(asset_op_verification_context& avc) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
CHECK_AND_ASSERT_MES(is_hardfork_active_for_height(ZANO_HARDFORK_05, avc.height), false, "validate_asset_operation was called before HF5");
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(avc.ado, &avc.asset_id_pt, &avc.asset_id), false, "get_or_calculate_asset_id failed");
avc.asset_op_history = m_db_assets.find(avc.asset_id);
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");
CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing while registering asset " << avc.asset_id);
avc.amount_to_validate = ado.opt_descriptor.get().current_supply;
// HF5 specific
CHECK_AND_ASSERT_MES(validate_ado_initial(ado.opt_descriptor.get()), false, "validate_ado_initial failed!");
CHECK_AND_ASSERT_MES(ado.opt_amount.has_value() && avc.amount_to_validate == ado.opt_amount.get(), false, "opt_amount is missing or incorrect");
}
else
{
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_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "opt_descriptor is missing in last ado, op: " << (int)ado.operation_type);
const asset_descriptor_base& last_adb = last_ado.opt_descriptor.get();
// check ownership permission
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, "Failed to validate ownership of asset_descriptor_operation, rejecting");
}
avc.amount_to_validate = 0;
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
{
CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing (update)");
//check that total current_supply haven't changed
CHECK_AND_ASSERT_MES(ado.opt_descriptor.get().current_supply == last_adb.current_supply, false, "update operation attempted to change emission, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.opt_descriptor.get(), last_adb), 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 > last_adb.current_supply, false, "emit operation does not increase the current supply, failed");
//CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_adb), false, "emit operation modifies asset descriptor in a prohibited manner");
//CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_adb.meta_info, false, "emit operation is not allowed to update meta info");
if (!last_adb.hidden_supply)
{
CHECK_AND_ASSERT_MES(ado.opt_amount.has_value(), false, "opt_amount is missing (emit)");
uint64_t amount = ado.opt_amount.get();
CHECK_AND_ASSERT_MES(last_adb.current_supply + amount >= last_adb.current_supply, false, "current_supply overflow: " << last_adb.current_supply << ", amount: " << amount << " (emit)");
CHECK_AND_ASSERT_MES(last_adb.current_supply + amount <= last_adb.total_max_supply, false, "current_supply overflow: " << last_adb.current_supply << ", amount: " << amount << ", max supply: " << last_adb.total_max_supply << " (emit)");
avc.amount_to_validate = amount;
}
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN)
{
//CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < last_adb.current_supply, false, "burn operation does not decrease the current supply, failed");
//CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_adb), false, "burn operation modifies asset descriptor in a prohibited manner");
//CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_adb.meta_info, false, "burn operation is not allowed to update meta info");
if (!last_adb.hidden_supply)
{
CHECK_AND_ASSERT_MES(ado.opt_amount.has_value(), false, "opt_amount is missing (burn)");
uint64_t amount = ado.opt_amount.get();
CHECK_AND_ASSERT_MES(last_adb.current_supply - amount <= last_adb.current_supply, false, "current_supply overflow: " << last_adb.current_supply << ", amount: " << amount << " (burn)");
avc.amount_to_validate = amount;
}
}
else
{
LOG_ERROR("Unknown operation type: " << (int)ado.operation_type);
return false;
}
}
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;
}
//------------------------------------------------------------------
bool blockchain_storage::validate_asset_operation(asset_op_verification_context& avc, uint64_t height) const
{
if (is_hardfork_active_for_height(ZANO_HARDFORK_05, height))
{
return validate_asset_operation_hf5(avc);
}
else
{
return validate_asset_operation_hf4(avc);
}
}
//------------------------------------------------------------------
bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, const uint64_t height)
{
CRITICAL_REGION_LOCAL(m_read_lock);
asset_op_verification_context avc = { tx, tx_id, ado };
CHECK_AND_ASSERT_MES(validate_asset_operation_against_current_blochain_state(avc), false, "asset operation validation failed");
asset_op_verification_context avc = { tx, tx_id, ado, height };
CHECK_AND_ASSERT_MES(validate_asset_operation(avc, height), false, "asset operation validation failed");
assets_container::t_value_type local_asset_history{};
if (avc.asset_op_history)
local_asset_history = *avc.asset_op_history;
local_asset_history.push_back(ado);
m_db_assets.set(avc.asset_id, local_asset_history);
switch(ado.operation_type)
if (is_hardfork_active_for_height(ZANO_HARDFORK_05, height))
{
case ASSET_DESCRIPTOR_OPERATION_REGISTER:
LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << print_money_brief(ado.descriptor.current_supply, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1);
break;
case ASSET_DESCRIPTOR_OPERATION_UPDATE:
LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1);
break;
case ASSET_DESCRIPTOR_OPERATION_EMIT:
LOG_PRINT_MAGENTA("[ASSET_EMITTED]: " << print_money_brief(avc.amount_to_validate, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1);
break;
case ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN:
LOG_PRINT_MAGENTA("[ASSET_BURNT]: " << print_money_brief(avc.amount_to_validate, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1);
break;
default:
LOG_ERROR("Unknown operation type: " << (int)ado.operation_type);
// NEW HF5 handling here
assets_container::t_value_type local_asset_history{};
if (avc.asset_op_history)
local_asset_history = *avc.asset_op_history;
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER)
{
CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing (register)"); // validated in validate_asset_operation(), just in case
const asset_descriptor_base& adb = ado.opt_descriptor.get();
local_asset_history.push_back(ado);
LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << print_money_brief(adb.current_supply, adb.decimal_point) << ", " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1);
}
else
{
CHECK_AND_ASSERT_MES(!local_asset_history.empty(), false, "local_asset_history is empty (update/emit/burn)");
asset_descriptor_operation& last_ado = local_asset_history.back();
CHECK_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "last_ado.opt_descriptor is missing (update/emit/burn)");
asset_descriptor_base& last_adb = local_asset_history.back().opt_descriptor.get();
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
{
local_asset_history.push_back(ado);
LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << avc.asset_id << ": " << last_adb.ticker << ", \"" << last_adb.full_name << "\"", LOG_LEVEL_1);
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT)
{
// just change the most recent history record, don't push
if (last_adb.hidden_supply)
{
//CHECK_AND_ASSERT_MES(last_ado.opt_amount_commitment.has_value() && ado.opt_amount_commitment.has_value(), false, "last_ado.opt_amount_commitment or ado.opt_amount_commitment is missing (emit)");
//last_ado.opt_amount_commitment.get() = (crypto::point_t(last_ado.opt_amount_commitment.get()) + crypto::point_t(ado.opt_amount_commitment.get())).to_public_key();
return false; // not supported atm
}
else
{
CHECK_AND_ASSERT_MES(ado.opt_amount.has_value(), false, "last_ado.opt_amount or ado.opt_amount is missing (emit)");
last_adb.current_supply += ado.opt_amount.get();
}
LOG_PRINT_MAGENTA("[ASSET_EMITTED]: " << print_money_brief(avc.amount_to_validate, last_adb.decimal_point) << ", " << avc.asset_id << ": " << last_adb.ticker << ", \"" << last_adb.full_name << "\"", LOG_LEVEL_1);
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN)
{
// just change the most recent history record, don't push
if (last_adb.hidden_supply)
{
//CHECK_AND_ASSERT_MES(last_ado.opt_amount_commitment.has_value() && ado.opt_amount_commitment.has_value(), false, "last_ado.opt_amount_commitment or ado.opt_amount_commitment is missing (burn)");
//last_ado.opt_amount_commitment.get() = (crypto::point_t(last_ado.opt_amount_commitment.get()) - crypto::point_t(ado.opt_amount_commitment.get())).to_public_key();
return false; // not supported atm
}
else
{
CHECK_AND_ASSERT_MES(ado.opt_amount.has_value(), false, "last_ado.opt_amount or ado.opt_amount is missing (burn)");
last_adb.current_supply -= ado.opt_amount.get();
}
LOG_PRINT_MAGENTA("[ASSET_BURNT]: " << print_money_brief(avc.amount_to_validate, last_adb.decimal_point) << ", " << avc.asset_id << ": " << last_adb.ticker << ", \"" << last_adb.full_name << "\"", LOG_LEVEL_1);
}
else
CHECK_AND_ASSERT_MES(false, false, "Unknown operation type: " << (int)ado.operation_type);
}
m_db_assets.set(avc.asset_id, local_asset_history);
}
else
{
// HF4
assets_container::t_value_type local_asset_history{};
if (avc.asset_op_history)
local_asset_history = *avc.asset_op_history;
local_asset_history.push_back(ado);
m_db_assets.set(avc.asset_id, local_asset_history);
const asset_descriptor_base& adb = ado.opt_descriptor.get(); // in HF4 descriptor must always be present, validated in validate_asset_operation_hf4()
switch(ado.operation_type)
{
case ASSET_DESCRIPTOR_OPERATION_REGISTER:
LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << print_money_brief(adb.current_supply, adb.decimal_point) << ", " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1);
break;
case ASSET_DESCRIPTOR_OPERATION_UPDATE:
LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1);
break;
case ASSET_DESCRIPTOR_OPERATION_EMIT:
LOG_PRINT_MAGENTA("[ASSET_EMITTED]: " << print_money_brief(avc.amount_to_validate, adb.decimal_point) << ", " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1);
break;
case ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN:
LOG_PRINT_MAGENTA("[ASSET_BURNT]: " << print_money_brief(avc.amount_to_validate, adb.decimal_point) << ", " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1);
break;
default:
LOG_ERROR("Unknown operation type: " << (int)ado.operation_type);
}
}
return true;
@ -4287,7 +4567,7 @@ bool blockchain_storage::prevalidate_alias_info(const transaction& tx, const ext
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx, const crypto::hash& tx_id)
bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx, const crypto::hash& tx_id, const uint64_t height)
{
//check transaction extra
tx_extra_info ei = AUTO_VAL_INIT(ei);
@ -4303,7 +4583,7 @@ bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx, cons
}
if (ei.m_asset_operation.operation_type != ASSET_DESCRIPTOR_OPERATION_UNDEFINED)
{
r = put_asset_info(tx, tx_id, ei.m_asset_operation);
r = put_asset_info(tx, tx_id, ei.m_asset_operation, height);
CHECK_AND_ASSERT_MES(r, false, "failed to put_asset_info");
}
@ -4503,7 +4783,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, tx_id);
bool r = process_blockchain_tx_extra(tx, tx_id, bl_height);
CHECK_AND_ASSERT_MES(r, false, "failed to process_blockchain_tx_extra");
TIME_MEASURE_FINISH_PD_COND(need_to_profile, tx_process_extra);
@ -4519,7 +4799,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const
{
LOG_ERROR("critical internal error: add_transaction_input_visitor failed. but key_images should be already checked");
purge_transaction_keyimages_from_blockchain(tx, false);
bool r = unprocess_blockchain_tx_extra(tx);
bool r = unprocess_blockchain_tx_extra(tx, bl_height);
CHECK_AND_ASSERT_MES(r, false, "failed to unprocess_blockchain_tx_extra");
unprocess_blockchain_tx_attachments(tx, bl_height, timestamp);
@ -4540,7 +4820,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const
{
LOG_ERROR("critical internal error: tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain");
purge_transaction_keyimages_from_blockchain(tx, true);
bool r = unprocess_blockchain_tx_extra(tx);
bool r = unprocess_blockchain_tx_extra(tx, bl_height);
CHECK_AND_ASSERT_MES(r, false, "failed to unprocess_blockchain_tx_extra");
unprocess_blockchain_tx_attachments(tx, bl_height, timestamp);
@ -5429,7 +5709,8 @@ uint64_t blockchain_storage::get_last_n_blocks_timestamps_median(size_t n) const
std::vector<uint64_t> timestamps = get_last_n_blocks_timestamps(n);
uint64_t median_res = epee::misc_utils::median(timestamps);
m_timestamps_median_cache[n] = median_res;
if (timestamps.size() == n)
m_timestamps_median_cache[n] = median_res;
return median_res;
}
//------------------------------------------------------------------
@ -5803,6 +6084,7 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti
bool var_is_after_hardfork_2_zone = m_core_runtime_config.is_hardfork_active_for_height(2, block_height);
bool var_is_after_hardfork_3_zone = m_core_runtime_config.is_hardfork_active_for_height(3, block_height);
bool var_is_after_hardfork_4_zone = m_core_runtime_config.is_hardfork_active_for_height(4, block_height);
bool var_is_after_hardfork_5_zone = m_core_runtime_config.is_hardfork_active_for_height(5, block_height);
auto is_allowed_before_hardfork1 = [&](const auto& el) -> bool
{
@ -5932,8 +6214,22 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti
LOG_ERROR("asset_descriptor_operation not allowed in tx with TX_FLAG_SIGNATURE_MODE_SEPARATE");
return false;
}
}
if (var_is_after_hardfork_5_zone)
{
// additional checks here
}
else
{
if (count_type_in_variant_container<asset_operation_ownership_proof_eth>(tx.proofs) != 0)
{
LOG_ERROR("asset_operation_ownership_proof_eth is not allowed prior to HF5");
return false;
}
}
return true;
}
//------------------------------------------------------------------
@ -6930,7 +7226,7 @@ bool blockchain_storage::is_hardfork_active(size_t hardfork_id) const
//------------------------------------------------------------------
bool blockchain_storage::is_hardfork_active_for_height(size_t hardfork_id, uint64_t height) const
{
return m_core_runtime_config.is_hardfork_active_for_height(hardfork_id, height);
return m_core_runtime_config.is_hardfork_active_for_height(hardfork_id, height != UINT64_MAX ? height : m_db_blocks.size());
}
//------------------------------------------------------------------
bool blockchain_storage::prevalidate_block(const block& bl)

View file

@ -380,7 +380,10 @@ namespace currency
bool for_altchain,
const alt_chain_type& alt_chain = alt_chain_type(),
uint64_t split_height = 0)const;
bool validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const;
bool validate_ado_ownership(asset_op_verification_context& avc) const;
bool validate_asset_operation_hf4(asset_op_verification_context& avc) const;
bool validate_asset_operation_hf5(asset_op_verification_context& avc) const;
bool validate_asset_operation(asset_op_verification_context& avc, uint64_t height) const;
void set_core_runtime_config(const core_runtime_config& pc) const;
const core_runtime_config& get_core_runtime_config()const;
@ -678,14 +681,14 @@ namespace currency
uint64_t get_adjusted_time()const;
bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
bool update_next_comulative_size_limit();
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_extra(const transaction& tx, const crypto::hash& tx_id, const uint64_t height);
bool unprocess_blockchain_tx_extra(const transaction& tx, const uint64_t height);
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::public_key& asset_id);
bool put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado);
bool pop_asset_info(const asset_descriptor_operation& ado, const uint64_t height);
bool put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, const uint64_t height);
void fill_addr_to_alias_dict();
//bool resync_spent_tx_flags();
bool prune_ring_signatures_and_attachments_if_need();

View file

@ -136,6 +136,7 @@ namespace currency
account_public_address stakeholder_address;
blobdata ex_nonce;
bool pos = false;
bool ignore_pow_ts_check = false;
pos_entry pe;
std::list<transaction> explicit_txs;
fill_block_template_func_t *pcustom_fill_block_template_func;

View file

@ -51,6 +51,8 @@ namespace currency
uint64_t m_last_response_height;
int64_t m_time_delta;
std::string m_remote_version;
int m_build_number = 0;
private:
template<class t_core> friend class t_currency_protocol_handler;
uncopybale_currency_context m_priv;

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2020 Zano Project
// Copyright (c) 2014-2024 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Boolberry developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -109,10 +109,16 @@ namespace currency
wide_difficulty_type max_pos_difficulty;
hard_forks_descriptor hard_forks;
std::array<int, ZANO_HARDFORKS_TOTAL> min_build_numbers_for_hard_forks;
bool is_hardfork_active_for_height(size_t hardfork_id, uint64_t height) const
bool is_hardfork_active_for_height(size_t hardfork_id, uint64_t upcoming_block_height) const
{
return hard_forks.is_hardfork_active_for_height(hardfork_id, height);
return hard_forks.is_hardfork_active_for_height(hardfork_id, upcoming_block_height);
}
int get_min_allowed_build_version_for_height(uint64_t upcoming_block_height) const
{
return min_build_numbers_for_hard_forks[hard_forks.get_the_most_recent_hardfork_id_for_height(upcoming_block_height)];
}
static uint64_t _default_core_time_function()
@ -123,7 +129,7 @@ namespace currency
inline core_runtime_config get_default_core_runtime_config()
{
core_runtime_config pc = AUTO_VAL_INIT(pc);
core_runtime_config pc{};
pc.min_coinstake_age = POS_MINIMUM_COINSTAKE_AGE;
pc.pos_minimum_heigh = POS_START_HEIGHT;
pc.tx_pool_min_fee = TX_MINIMUM_FEE;
@ -137,7 +143,9 @@ namespace currency
pc.hard_forks.set_hardfork_height(2, ZANO_HARDFORK_02_AFTER_HEIGHT);
pc.hard_forks.set_hardfork_height(3, ZANO_HARDFORK_03_AFTER_HEIGHT);
pc.hard_forks.set_hardfork_height(4, ZANO_HARDFORK_04_AFTER_HEIGHT);
pc.hard_forks.set_hardfork_height(5, ZANO_HARDFORK_05_AFTER_HEIGHT); pc.min_build_numbers_for_hard_forks[5] = ZANO_HARDFORK_05_MIN_BUILD_VER;
static_assert(5 + 1 == ZANO_HARDFORKS_TOTAL);
pc.get_core_time = &core_runtime_config::_default_core_time_function;
bool r = epee::string_tools::hex_to_pod(ALIAS_SHORT_NAMES_VALIDATION_PUB_KEY, pc.alias_validation_pubkey);
CHECK_AND_ASSERT_THROW_MES(r, "failed to parse alias_validation_pub_key");

View file

@ -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"
@ -73,7 +74,9 @@ namespace currency
/************************************************************************/
/* */
/************************************************************************/
struct asset_descriptor_operation_v1;
//since structure used in blockchain as a key accessor, then be sure that there is no padding inside
#pragma pack(push, 1)
struct account_public_address_old
@ -695,6 +698,21 @@ namespace currency
}
};
#define ASSET_DESCRIPTOR_BASE_HF4_VER 0
#define ASSET_DESCRIPTOR_BASE_HF5_VER 2
#define ASSET_DESCRIPTOR_BASE_LAST_VER 2
struct dummy
{
BEGIN_SERIALIZE()
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
END_BOOST_SERIALIZATION_TOTAL_FIELDS(0)
};
typedef boost::variant<dummy> asset_descriptor_base_etc_fields;
typedef boost::variant<crypto::public_key, crypto::eth_public_key> asset_owner_pub_key_v;
struct asset_descriptor_base
{
@ -706,9 +724,14 @@ 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;
uint8_t version = ASSET_DESCRIPTOR_BASE_HF4_VER;
//version 1 members
boost::optional<crypto::eth_public_key> 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
//version 2 members
std::vector<asset_descriptor_base_etc_fields> etc; //container for future use if we would be adding some optional parameters that is not known yet, but without mess related to format version
BEGIN_VERSIONED_SERIALIZE(0, version)
BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_BASE_LAST_VER, version)
FIELD(total_max_supply)
FIELD(current_supply)
FIELD(decimal_point)
@ -717,9 +740,13 @@ namespace currency
FIELD(meta_info)
FIELD(owner)
FIELD(hidden_supply)
END_VERSION_UNDER(1)
FIELD(owner_eth_pub_key)
END_VERSION_UNDER(2)
FIELD(etc)
END_SERIALIZE()
BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(2)
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(total_max_supply)
BOOST_SERIALIZE(current_supply)
@ -729,17 +756,23 @@ namespace currency
BOOST_SERIALIZE(meta_info)
BOOST_SERIALIZE(owner)
BOOST_SERIALIZE(hidden_supply)
END_BOOST_SERIALIZATION()
BOOST_END_VERSION_UNDER(1)
BOOST_SERIALIZE(owner_eth_pub_key)
BOOST_END_VERSION_UNDER(2)
BOOST_SERIALIZE(etc)
BOOST_SERIALIZE(version)
END_BOOST_SERIALIZATION_TOTAL_FIELDS(11)
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_POD_AS_HEX_STRING(owner_eth_pub_key) DOC_DSCR("[Optional] Owner's key in the case when ETH signature is used.") DOC_END
END_KV_SERIALIZE_MAP()
};
@ -768,31 +801,62 @@ namespace currency
#define ASSET_DESCRIPTOR_OPERATION_UPDATE 3
#define ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN 4
#define ASSET_DESCRIPTOR_OPERATION_STRUCTURE_VER 1
#define ASSET_DESCRIPTOR_OPERATION_HF4_VER 1
#define ASSET_DESCRIPTOR_OPERATION_HF5_VER 2
#define ASSET_DESCRIPTOR_OPERATION_LAST_VER 2
typedef boost::variant<dummy> asset_descriptor_operation_etc_fields;
struct asset_descriptor_operation
{
uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED;
asset_descriptor_base descriptor;
crypto::public_key amount_commitment; // premultiplied by 1/8
boost::optional<crypto::public_key> opt_asset_id; // target asset_id - for update/emit
uint8_t verion = ASSET_DESCRIPTOR_OPERATION_STRUCTURE_VER;
uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED;
uint8_t version = ASSET_DESCRIPTOR_OPERATION_HF4_VER;
// register emit burn update
boost::optional<crypto::public_key> opt_amount_commitment; // + + + - (premultiplied by 1/8)
boost::optional<crypto::public_key> opt_asset_id; // - + + +
boost::optional<asset_descriptor_base> opt_descriptor; // + - - +
boost::optional<uint64_t> opt_amount; // ? ? ? - (only for non-hidden supply)
boost::optional<uint32_t> opt_asset_id_salt; // ? - - - (optional)
std::vector<asset_descriptor_operation_etc_fields> etc; // (reserved for future use)
BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_OPERATION_STRUCTURE_VER, verion)
BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_OPERATION_LAST_VER, version)
CHAIN_TRANSITION_VER(0, asset_descriptor_operation_v1)
CHAIN_TRANSITION_VER(1, asset_descriptor_operation_v1)
FIELD(operation_type)
FIELD(descriptor)
FIELD(amount_commitment)
END_VERSION_UNDER(1)
FIELD(opt_amount_commitment)
FIELD(opt_asset_id)
FIELD(opt_descriptor)
FIELD(opt_amount)
FIELD(opt_asset_id_salt)
FIELD(etc)
END_SERIALIZE()
BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(2)
BEGIN_BOOST_SERIALIZATION()
BOOST_CHAIN_TRANSITION_VER(1, asset_descriptor_operation_v1)
BOOST_CHAIN_TRANSITION_VER(0, asset_descriptor_operation_v1)
BOOST_SERIALIZE(version)
BOOST_SERIALIZE(operation_type)
BOOST_SERIALIZE(descriptor)
BOOST_SERIALIZE(amount_commitment)
BOOST_END_VERSION_UNDER(1)
BOOST_SERIALIZE(opt_amount_commitment)
BOOST_SERIALIZE(opt_asset_id)
END_BOOST_SERIALIZATION()
BOOST_SERIALIZE(opt_descriptor)
BOOST_SERIALIZE(opt_amount)
BOOST_SERIALIZE(opt_asset_id_salt)
BOOST_SERIALIZE(etc)
END_BOOST_SERIALIZATION_TOTAL_FIELDS(8)
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(version) DOC_DSCR("Asset operation type struct version") DOC_EXMP(2) DOC_END
KV_SERIALIZE(operation_type) DOC_DSCR("Asset operation type identifier") DOC_EXMP(1) DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(opt_amount_commitment) DOC_DSCR("(optional) Asset operation amount commitment (register/emit/burn).") DOC_EXMP("5688b56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(opt_asset_id) DOC_DSCR("(optional) ID of an asset (emit/burn/update).") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END
KV_SERIALIZE(opt_descriptor) DOC_DSCR("(optional) Asset operation descriptor (register/update).") DOC_EXMP_AUTO() DOC_END
KV_SERIALIZE(opt_amount) DOC_DSCR("(optional) Asset operation amount (register/emit/burn when supply is non-hidden).") DOC_EXMP_AUTO() DOC_END
KV_SERIALIZE(opt_asset_id_salt) DOC_DSCR("(optional) Asset ID salt. May only be used for asset registration.") DOC_EXMP_AUTO() DOC_END
//KV_SERIALIZE(etc) DOC_DSCR("Extra operations") DOC_EXMP_AUTO() DOC_END <---- serialization for variant not supported yet
END_KV_SERIALIZE_MAP()
};
struct asset_operation_proof
@ -807,6 +871,7 @@ namespace currency
FIELD(opt_amount_commitment_g_proof)
END_SERIALIZE()
BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(1)
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(opt_amount_commitment_composition_proof)
BOOST_SERIALIZE(opt_amount_commitment_g_proof)
@ -825,6 +890,7 @@ namespace currency
FIELD(gss)
END_SERIALIZE()
BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(1)
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(gss)
BOOST_END_VERSION_UNDER(1)
@ -833,6 +899,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<uint8_t> buff; //stub
@ -936,7 +1022,7 @@ namespace currency
typedef boost::variant<NLSAG_sig, void_sig, ZC_sig, zarcanum_sig> signature_v;
typedef boost::variant<zc_asset_surjection_proof, zc_outs_range_proof, zc_balance_proof, asset_operation_proof, asset_operation_ownership_proof> proof_v;
typedef boost::variant<zc_asset_surjection_proof, zc_outs_range_proof, zc_balance_proof, asset_operation_proof, asset_operation_ownership_proof, asset_operation_ownership_proof_eth> proof_v;
//include backward compatibility defintions
@ -986,6 +1072,19 @@ namespace currency
FIELD(signatures)
FIELD(proofs)
END_SERIALIZE()
BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(0)
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(version)
BOOST_SERIALIZE(vin)
BOOST_SERIALIZE(vout)
BOOST_SERIALIZE(extra)
BOOST_SERIALIZE(signatures)
BOOST_SERIALIZE(attachment)
BOOST_END_VERSION_UNDER(1)
BOOST_SERIALIZE(proofs)
END_BOOST_SERIALIZATION()
};
@ -1121,9 +1220,12 @@ BLOB_SERIALIZER(currency::txout_to_key);
VARIANT_TAG(json_archive, type_name, json_tag)
BOOST_CLASS_VERSION(currency::asset_descriptor_operation, 1);
BOOST_CLASS_VERSION(currency::asset_operation_proof, 1);
BOOST_CLASS_VERSION(currency::asset_operation_ownership_proof, 1);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_descriptor_operation);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_descriptor_base);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_operation_proof);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_operation_ownership_proof);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::transaction);
// txin_v variant currency
@ -1195,6 +1297,11 @@ 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");
SET_VARIANT_TAGS(currency::dummy, 62, "dummy");

View file

@ -116,3 +116,74 @@ bool transition_convert(const transaction_v1& from, transaction_current_t& to)
}
return true;
}
struct asset_descriptor_operation_v1
{
uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED;
asset_descriptor_base descriptor;
crypto::public_key amount_commitment = currency::null_pkey; // premultiplied by 1/8
boost::optional<crypto::public_key> opt_asset_id; // target asset_id - for update/emit
uint8_t verion = ASSET_DESCRIPTOR_OPERATION_HF4_VER;
BEGIN_SERIALIZE()
FIELD(operation_type)
FIELD(descriptor)
FIELD(amount_commitment)
//END_VERSION_UNDER(1)
FIELD(opt_asset_id)
END_SERIALIZE()
//this map doesn't store internal version member, but it set it by default to val "1", which then transfered via transition_convert() to destination struct
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(operation_type)
BOOST_SERIALIZE(descriptor)
BOOST_SERIALIZE(amount_commitment)
//BOOST_END_VERSION_UNDER(1)
BOOST_SERIALIZE(opt_asset_id)
END_BOOST_SERIALIZATION()
};
template<typename asset_descriptor_operation_t>
bool transition_convert(const asset_descriptor_operation_t& from, asset_descriptor_operation_v1& to)
{
to.verion = from.version;
to.operation_type = from.operation_type;
if(from.opt_descriptor.has_value())
{
to.descriptor = *from.opt_descriptor;
}
else
{
throw std::runtime_error(std::string("Unexpected: missing descriptor in from transaction_current_t"));
}
if (from.opt_amount_commitment.has_value())
{
to.amount_commitment = *from.opt_amount_commitment;
}
else
{
//not used over update operations //throw std::runtime_error(std::string("Unexpected: missing amount_commitment in from transaction_current_t"));
}
to.opt_asset_id = from.opt_asset_id;
if(from.opt_amount.has_value() || from.etc.size())
{
throw std::runtime_error(std::string("Unexpected: opt_amount or etc have values during convention, looks like object slicing with information getting lost"));
}
return true;
}
template<typename asset_descriptor_operation_t>
bool transition_convert(const asset_descriptor_operation_v1& from, asset_descriptor_operation_t& to)
{
to.operation_type = from.operation_type;
to.opt_descriptor = from.descriptor;
to.opt_amount_commitment = from.amount_commitment;
to.opt_asset_id = from.opt_asset_id; // target asset_id - for update/emit
to.version = from.verion;
return true;
}

View file

@ -222,16 +222,7 @@ namespace boost
a & x.buff;
}
template <class Archive>
inline void serialize(Archive &a, currency::transaction &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.vin;
a & x.vout;
a & x.extra;
a & x.signatures;
a & x.attachment;
}
template <class Archive>
inline void serialize(Archive &a, currency::keypair &kp, const boost::serialization::version_type ver)

View file

@ -10,7 +10,7 @@
#ifndef TESTNET
#define CURRENCY_FORMATION_VERSION 84
#else
#define CURRENCY_FORMATION_VERSION 98
#define CURRENCY_FORMATION_VERSION 99
#endif
#define CURRENCY_GENESIS_NONCE (CURRENCY_FORMATION_VERSION + 101011010121) //bender's nightmare
@ -19,6 +19,7 @@
#define CURRENCY_MAX_BLOCK_NUMBER 500000000
#define CURRENCY_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used!
#define CURRENCY_TX_MAX_ALLOWED_INPUTS 256 // limited primarily by asset surjection proof
#define CURRENCY_TX_MAX_ALLOWED_OUTS 2000
#define CURRENCY_TX_MIN_ALLOWED_OUTS 2 // effective starting HF4 Zarcanum
#define CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX 0xc5 // addresses start with 'Zx'
@ -117,7 +118,7 @@
#define P2P_MAINTAINERS_PUB_KEY "8f138bb73f6d663a3746a542770781a09579a7b84cb4125249e95530824ee607"
#define DIFFICULTY_POS_STARTER 1
#else
#define P2P_DEFAULT_PORT (11112 + CURRENCY_FORMATION_VERSION)
#define P2P_DEFAULT_PORT (11211 + CURRENCY_FORMATION_VERSION)
#define RPC_DEFAULT_PORT 12111
#define STRATUM_DEFAULT_PORT 11888
#define STRARUM_DEFAULT_PORT 51113
@ -252,30 +253,34 @@
#define BC_OFFERS_CURRENCY_MARKET_FILENAME "market.bin"
#define WALLET_FILE_SERIALIZATION_VERSION 167
#define WALLET_FILE_SERIALIZATION_VERSION 168
#define WALLET_FILE_LAST_SUPPORTED_VERSION 165
#define CURRENT_MEMPOOL_ARCHIVE_VER (CURRENCY_FORMATION_VERSION+31)
//hard forks section
#define BLOCK_MAJOR_VERSION_GENESIS 1
#define BLOCK_MINOR_VERSION_GENESIS 0
#define BLOCK_MAJOR_VERSION_INITIAL 0
/////// Hard forks setup //////////////////////////////
#ifndef TESTNET
// Mainnet
#define ZANO_HARDFORK_01_AFTER_HEIGHT 194624 // 2019-09-21 20:25:16
#define ZANO_HARDFORK_02_AFTER_HEIGHT 999999 // 2021-04-05 09:11:45
#define ZANO_HARDFORK_03_AFTER_HEIGHT 1082577 // 2021-06-01 23:28:10
#define ZANO_HARDFORK_04_AFTER_HEIGHT 2555000 // 2024-03-21 11:49:55
#define ZANO_HARDFORK_05_AFTER_HEIGHT 999999999999999999
#define ZANO_HARDFORK_04_TIMESTAMP_ACTUAL 1711021795ull // block 2555000, 2024-03-21 11:49:55 UTC
#define ZANO_HARDFORK_05_AFTER_HEIGHT 999999999999999999
#define ZANO_HARDFORK_05_MIN_BUILD_VER 354
#else
/////// Zarcanum Testnet //////////////////////////////
// Testnet
#define ZANO_HARDFORK_01_AFTER_HEIGHT 0
#define ZANO_HARDFORK_02_AFTER_HEIGHT 0
#define ZANO_HARDFORK_03_AFTER_HEIGHT 0
#define ZANO_HARDFORK_04_AFTER_HEIGHT 200
#define ZANO_HARDFORK_05_AFTER_HEIGHT 200
#define ZANO_HARDFORK_04_TIMESTAMP_ACTUAL 1712785801ull // block 200, 2024-04-10 21:50:01 UTC
#define ZANO_HARDFORK_04_AFTER_HEIGHT 100
#define ZANO_HARDFORK_04_TIMESTAMP_ACTUAL 1712800000ull // block 100, 2024-00-00 00:00:00 UTC
#define ZANO_HARDFORK_05_AFTER_HEIGHT 200
#define ZANO_HARDFORK_05_MIN_BUILD_VER 356
#endif

View file

@ -552,6 +552,7 @@ namespace currency
if (hardfork_id_for_prev_block != hardfork_id_for_curr_block)
{
LOG_PRINT_GREEN("Hardfork " << hardfork_id_for_curr_block << " has been activated after the block at height " << h, LOG_LEVEL_0);
m_pprotocol->on_hardfork_activated(hardfork_id_for_curr_block);
}
}

View file

@ -4,7 +4,7 @@
// Copyright (c) 2012-2013 The Boolberry developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <regex>
#include "include_base_utils.h"
#include <boost/foreach.hpp>
#ifndef MOBILE_WALLET_BUILD
@ -116,7 +116,7 @@ namespace currency
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, asset id: " << H);
CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "out #" << j << ": cannot find a corresponding asset id in inputs or asset operations; asset id: " << H);
result.bge_proofs.emplace_back(crypto::BGE_proof_s{});
uint8_t err = 0;
@ -197,17 +197,17 @@ namespace currency
return true;
}
//--------------------------------------------------------------------------------
bool generate_zc_outs_range_proof(const crypto::hash& context_hash, size_t out_index_start, const tx_generation_context& outs_gen_context,
bool generate_zc_outs_range_proof(const crypto::hash& context_hash, const tx_generation_context& outs_gen_context,
const std::vector<tx_out_v>& vouts, zc_outs_range_proof& result)
{
size_t outs_count = outs_gen_context.amounts.size();
// TODO @#@# reconsider this check CHECK_AND_ASSERT_MES(gen_context.check_sizes(outs_count), false, "");
CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, "");
CHECK_AND_ASSERT_MES(outs_count == vouts.size(), false, "");
// prepare data for aggregation proof
std::vector<crypto::point_t> 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)
for (size_t i = 0; i < outs_count; ++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
@ -322,7 +322,7 @@ namespace currency
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");
// (sum(bare inputs' amounts) - fee) * H + sum(pseudo out amount commitments) - sum(outputs' commitments) = lin(G)
// (sum(bare inputs' amounts) - fee) * H - sum(outputs' commitments) = lin(G)
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;
@ -336,6 +336,8 @@ namespace currency
{
// 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
// (sum(bare inputs' amounts) - fee) * H + sum(pseudo out amount commitments) + asset_op_commitment - sum(outputs' commitments) = lin(X)
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_commitment_in_outputs ? -ogc.ao_amount_commitment : 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;
@ -472,7 +474,7 @@ namespace currency
}
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.");
tx = AUTO_VAL_INIT_T(transaction);
// tx is not cleared intentionally to allow passing additional args in the extra/attachments
tx.version = tx_version;
tx_generation_context tx_gen_context{};
@ -562,7 +564,7 @@ namespace currency
// range proofs
currency::zc_outs_range_proof range_proofs{};
r = generate_zc_outs_range_proof(tx_id, 0, tx_gen_context, tx.vout, range_proofs);
r = generate_zc_outs_range_proof(tx_id, tx_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));
@ -607,7 +609,7 @@ namespace currency
CHECK_AND_ASSERT_MES(count_type_in_variant_container<asset_operation_proof>(context.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<const asset_operation_proof>(context.tx.proofs);
if (context.ado.descriptor.hidden_supply)
if (context.ado.opt_descriptor.has_value() && context.ado.opt_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 -- check composition proof
@ -617,7 +619,8 @@ namespace currency
{
// make sure that amount commitment corresponds to opt_amount_commitment_g_proof
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(context.ado.amount_commitment).modify_mul8() - context.amount_to_validate * context.asset_id_pt;
CHECK_AND_ASSERT_MES(context.ado.opt_amount_commitment.has_value(), false, "amount_commitment is absent");
crypto::point_t A = crypto::point_t(context.ado.opt_amount_commitment.get()).modify_mul8() - context.amount_to_validate * context.asset_id_pt;
bool r = crypto::check_signature(context.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");
@ -673,12 +676,14 @@ namespace currency
{
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT)
{
CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is missing");
// amount_commitment supposed to be validated earlier in validate_asset_operation_amount_commitment()
sum_of_pseudo_out_amount_commitments += crypto::point_t(ado.amount_commitment); // *1/8
sum_of_pseudo_out_amount_commitments += crypto::point_t(ado.opt_amount_commitment.get()); // *1/8
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN)
{
outs_commitments_sum += crypto::point_t(ado.amount_commitment); // *1/8
CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is missing");
outs_commitments_sum += crypto::point_t(ado.opt_amount_commitment.get()); // *1/8
}
}
size_t zc_sigs_count = 0;
@ -2182,16 +2187,25 @@ namespace currency
return true;
}
// otherwise, calculate asset id
// otherwise, it must be a register operation
CHECK_AND_ASSERT_MES(ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER, false, "unexpected asset operation type: " << (int)ado.operation_type << ", " << get_asset_operation_type_string(ado.operation_type));
CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing: " << (int)ado.operation_type << ", " << get_asset_operation_type_string(ado.operation_type));
const asset_descriptor_base &adb = ado.opt_descriptor.get();
// calculate asset id
crypto::hash_helper_t::hs_t hsc;
hsc.add_32_chars(CRYPTO_HDS_ASSET_ID);
hsc.add_hash(crypto::hash_helper_t::h(ado.descriptor.ticker));
hsc.add_hash(crypto::hash_helper_t::h(ado.descriptor.full_name));
hsc.add_hash(crypto::hash_helper_t::h(ado.descriptor.meta_info));
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);
hsc.add_hash(crypto::hash_helper_t::h(adb.ticker));
hsc.add_hash(crypto::hash_helper_t::h(adb.full_name));
hsc.add_hash(crypto::hash_helper_t::h(adb.meta_info));
hsc.add_scalar(crypto::scalar_t(adb.total_max_supply));
hsc.add_scalar(crypto::scalar_t(adb.decimal_point));
hsc.add_pub_key(adb.owner);
if (adb.owner_eth_pub_key.has_value())
hsc.add_eth_pub_key(adb.owner_eth_pub_key.value());
if (ado.opt_asset_id_salt.has_value())
hsc.add_scalar(crypto::scalar_t(ado.opt_asset_id_salt.get()));
crypto::hash h = hsc.calc_hash_no_reduce();
// this hash function needs to be computationally expensive (s.a. the whitepaper)
@ -2236,15 +2250,11 @@ namespace currency
{
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER)
{
//crypto::secret_key asset_control_key{};
//bool r = derive_key_pair_from_key_pair(sender_account_keys.account_address.spend_public_key, tx_key.sec, asset_control_key, ado.descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY);
//CHECK_AND_ASSERT_MES(r, false, "derive_key_pair_from_key_pair failed");
//
// old:
// 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
CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing (register)");
asset_descriptor_base& adb = ado.opt_descriptor.get();
ado.descriptor.owner = sender_account_keys.account_address.spend_public_key;
if (!adb.owner_eth_pub_key.has_value())
adb.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");
@ -2261,10 +2271,14 @@ namespace currency
amount_of_emitted_asset += item.amount;
}
}
ado.descriptor.current_supply = amount_of_emitted_asset; // TODO: consider setting current_supply beforehand, not setting it hear in ad-hoc manner -- sowle
adb.current_supply = amount_of_emitted_asset;
if (ado.version >= ASSET_DESCRIPTOR_BASE_HF5_VER)
{
ado.opt_amount = amount_of_emitted_asset; // TODO: support hidden supply -- sowle
}
gen_context.ao_amount_commitment = amount_of_emitted_asset * gen_context.ao_asset_id_pt + gen_context.ao_amount_blinding_mask * crypto::c_point_G;
ado.amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key();
ado.opt_amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key();
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN)
{
@ -2274,7 +2288,7 @@ namespace currency
gen_context.ao_amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_ASSET_CONTROL_ABM, tx_key.sec);
gen_context.ao_commitment_in_outputs = true;
// set correct asset_id to the corresponding destination entries
// calculate the amount of asset being burnt using asset_id field in inputs and outputs (sources and destinations)
uint64_t amount_of_burned_assets = 0;
for (auto& item : ftp.sources)
{
@ -2291,10 +2305,19 @@ namespace currency
amount_of_burned_assets -= item.amount;
}
}
ado.descriptor.current_supply -= amount_of_burned_assets;
if (ado.version < ASSET_DESCRIPTOR_BASE_HF5_VER)
{
CHECK_AND_ASSERT_THROW_MES(ado.opt_descriptor.has_value(), "Internal error: opt_descriptor unset during ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN for version less then 2");
ado.opt_descriptor->current_supply -= amount_of_burned_assets;
}
else
{
CHECK_AND_ASSERT_THROW_MES(!ado.opt_descriptor.has_value(), "Internal error: opt_descriptor unset during ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN for version less then 2");
ado.opt_amount = amount_of_burned_assets; // TODO: support hidden supply -- sowle
}
gen_context.ao_amount_commitment = amount_of_burned_assets * gen_context.ao_asset_id_pt + gen_context.ao_amount_blinding_mask * crypto::c_point_G;
ado.amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key();
ado.opt_amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key();
if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_handle_asset_descriptor_operation_before_burn{ &ado });
@ -2318,10 +2341,19 @@ namespace currency
item.asset_id = gen_context.ao_asset_id;
}
}
ado.descriptor.current_supply += amount_of_emitted_asset;
if (ado.version < ASSET_DESCRIPTOR_BASE_HF5_VER)
{
CHECK_AND_ASSERT_THROW_MES(ado.opt_descriptor.has_value(), "Internal error: opt_descriptor unset during ASSET_DESCRIPTOR_OPERATION_EMIT for version less then 2");
ado.opt_descriptor->current_supply += amount_of_emitted_asset;
}
else
{
CHECK_AND_ASSERT_THROW_MES(!ado.opt_descriptor.has_value(), "Internal error: opt_descriptor unset during ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN for version less then 2");
ado.opt_amount = amount_of_emitted_asset; // TODO: support hidden supply -- sowle
}
gen_context.ao_amount_commitment = amount_of_emitted_asset * gen_context.ao_asset_id_pt + gen_context.ao_amount_blinding_mask * crypto::c_point_G;
ado.amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key();
ado.opt_amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key();
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
@ -2333,25 +2365,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;
}
@ -2374,7 +2387,8 @@ namespace currency
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.");
CHECK_AND_ASSERT_MES(sources.size() <= CURRENCY_TX_MAX_ALLOWED_INPUTS, false, "Too many inputs: " << sources.size() << ", max allowed: " << CURRENCY_TX_MAX_ALLOWED_INPUTS << ". Tx cannot be constructed.");
CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS, false, "Too many outputs: " << destinations.size() << ", max allowed: " << CURRENCY_TX_MAX_ALLOWED_OUTS << ". Tx cannot be constructed.");
bool append_mode = false;
if (flags&TX_FLAG_SIGNATURE_MODE_SEPARATE && tx.vin.size())
@ -2600,8 +2614,7 @@ namespace currency
// ASSET oprations handling
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
{
asset_descriptor_operation* pado = nullptr;
pado = get_type_in_variant_container<asset_descriptor_operation>(tx.extra);
asset_descriptor_operation* pado = get_type_in_variant_container<asset_descriptor_operation>(tx.extra);
if (pado)
{
bool r = construct_tx_handle_ado(sender_account_keys, ftp, *pado, gen_context, gen_context.tx_key, shuffled_dsts);
@ -2619,7 +2632,6 @@ namespace currency
// 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 = 0;
std::set<uint16_t> existing_derivation_hints, new_derivation_hints;
CHECK_AND_ASSERT_MES(copy_all_derivation_hints_from_tx_to_container(tx, existing_derivation_hints), false, "move_all_derivation_hints_from_tx_to_container failed");
for(size_t destination_index = 0; destination_index < shuffled_dsts.size(); ++destination_index, ++output_index)
@ -2714,7 +2726,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;
@ -2760,7 +2773,7 @@ namespace currency
// range proofs
currency::zc_outs_range_proof range_proofs{};
r = generate_zc_outs_range_proof(tx_prefix_hash, range_proof_start_index, gen_context, tx.vout, range_proofs);
r = generate_zc_outs_range_proof(tx_prefix_hash, 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));
@ -2770,35 +2783,29 @@ 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<asset_descriptor_operation>(tx.extra);
if (pado != nullptr)
{
if ((pado->operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || pado->operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) && (!ftp.ado_sign_thirdparty))
{
//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);
}
}
@ -2850,7 +2857,7 @@ namespace currency
return reward;
}
//---------------------------------------------------------------
std::string get_word_from_timstamp(uint64_t timestamp, bool use_password)
std::string get_word_from_timestamp(uint64_t timestamp, bool use_password)
{
uint64_t date_offset = timestamp > WALLET_BRAIN_DATE_OFFSET ? timestamp - WALLET_BRAIN_DATE_OFFSET : 0;
uint64_t weeks_count = date_offset / WALLET_BRAIN_DATE_QUANTUM;
@ -2865,7 +2872,7 @@ namespace currency
return tools::mnemonic_encoding::word_by_num(weeks_count_32);
}
//---------------------------------------------------------------
uint64_t get_timstamp_from_word(std::string word, bool& password_used)
uint64_t get_timestamp_from_word(std::string word, bool& password_used)
{
uint64_t count_of_weeks = tools::mnemonic_encoding::num_by_word(word);
if (count_of_weeks >= WALLET_BRAIN_DATE_MAX_WEEKS_COUNT)
@ -3625,13 +3632,13 @@ namespace currency
return true;
}
//------------------------------------------------------------------
#define PASSWORD_REGEXP R"([A-Za-z0-9~!?@#$%^&*_+|{}\[\]()<>:;"'\-=/.,]{0,40})"
bool validate_password(const std::string& password)
{
static const std::string allowed_password_symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!?@#$%^&*_+|{}[]()<>:;\"'-=\\/.,";
size_t n = password.find_first_not_of(allowed_password_symbols, 0);
return n == std::string::npos;
// OLD: static const std::string allowed_password_symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!?@#$%^&*_+|{}[]()<>:;\"'-=\\/.,";
static std::regex password_regexp(PASSWORD_REGEXP);
return std::regex_match(password, password_regexp);
}
//------------------------------------------------------------------
#define ANTI_OVERFLOW_AMOUNT 1000000
#define GET_PERECENTS_BIG_NUMBERS(per, total) (per/ANTI_OVERFLOW_AMOUNT)*100 / (total/ANTI_OVERFLOW_AMOUNT)
@ -4458,6 +4465,45 @@ namespace currency
}
}
//------------------------------------------------------------------
#define ASSET_TICKER_REGEXP R"([A-Za-z0-9]{1,14})"
#define ASSET_FULL_NAME_REGEXP R"([A-Za-z0-9.,:!?\-() ]{0,400})"
bool validate_asset_ticker(const std::string& ticker)
{
static std::regex asset_ticker_regexp(ASSET_TICKER_REGEXP);
return std::regex_match(ticker, asset_ticker_regexp);
}
//------------------------------------------------------------------
bool validate_asset_full_name(const std::string& full_name)
{
static std::regex asset_full_name_regexp(ASSET_FULL_NAME_REGEXP);
return std::regex_match(full_name, asset_full_name_regexp);
}
//------------------------------------------------------------------
bool validate_asset_ticker_and_full_name(const asset_descriptor_base& adb)
{
if (!validate_asset_ticker(adb.ticker))
return false;
if (!validate_asset_full_name(adb.full_name))
return false;
//CHECK_AND_ASSERT_MES(validate_asset_ticker(adb.ticker), false, "asset's ticker isn't valid: " << adb.ticker);
//CHECK_AND_ASSERT_MES(validate_asset_full_name(adb.full_name), false, "asset's full_name isn't valid: " << adb.full_name);
return true;
}
//------------------------------------------------------------------
void replace_asset_ticker_and_full_name_if_invalid(asset_descriptor_base& adb, const crypto::public_key& asset_id)
{
if (!validate_asset_ticker(adb.ticker))
adb.ticker = "#BADASSET#";
if (!validate_asset_full_name(adb.full_name))
{
std::string abcd = crypto::pod_to_hex(asset_id).substr(60, 4); // last 4 hex chars
adb.full_name = "#bad asset name " + abcd + "#";
}
}
//------------------------------------------------------------------
std::string dump_ring_sig_data(const crypto::hash& hash_for_sig, const crypto::key_image& k_image, const std::vector<const crypto::public_key*>& output_keys_ptrs, const std::vector<crypto::signature>& sig)
{
std::stringstream s;

View file

@ -29,6 +29,7 @@
#include "currency_format_utils_transactions.h"
#include "core_runtime_config.h"
#include "wallet/wallet_public_structs_defs.h"
#include "wallet/wallet_public_structs_defs.h"
#include "bc_attachments_helpers.h"
#include "bc_payments_id_service.h"
#include "bc_offers_service_basic.h"
@ -139,11 +140,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<crypto::public_key, crypto::eth_public_key> asset_owner_key_v;
struct finalize_tx_param
{
uint64_t unlock_time;
@ -162,13 +165,11 @@ namespace currency
uint64_t tx_version;
uint64_t mode_separate_fee = 0;
epee::misc_utils::events_dispatcher* pevents_dispatcher;
epee::misc_utils::events_dispatcher* pevents_dispatcher = nullptr;
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;
bool ado_sign_thirdparty = false;//@#@ TODO: add to serialization map @zoidberg
BEGIN_SERIALIZE_OBJECT()
@ -191,14 +192,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 +208,7 @@ namespace currency
BEGIN_SERIALIZE_OBJECT()
FIELD(tx)
FIELD(tx_id)
FIELD(one_time_key)
FIELD(ftp)
FIELD(htlc_origin)
@ -261,6 +262,7 @@ namespace currency
const transaction& tx;
const crypto::hash& tx_id;
const asset_descriptor_operation& ado;
uint64_t height = UINT64_MAX; // default value means the height of the upcoming block (top_block + 1)
crypto::public_key asset_id = currency::null_pkey;
crypto::point_t asset_id_pt = crypto::c_point_0;
uint64_t amount_to_validate = 0;
@ -271,13 +273,18 @@ namespace currency
bool generate_asset_surjection_proof(const crypto::hash& context_hash, bool has_non_zc_inputs, tx_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 tx_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 tx_generation_context& outs_gen_context,
bool generate_zc_outs_range_proof(const crypto::hash& context_hash, const tx_generation_context& outs_gen_context,
const std::vector<tx_out_v>& 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_amount_commitment(asset_op_verification_context& context);
const char* get_asset_operation_type_string(size_t asset_operation_type, bool short_name = false);
bool validate_asset_ticker(const std::string& ticker);
bool validate_asset_full_name(const std::string& full_name);
bool validate_asset_ticker_and_full_name(const asset_descriptor_base& adb);
void replace_asset_ticker_and_full_name_if_invalid(asset_descriptor_base& adb, const 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,
@ -457,9 +464,9 @@ namespace currency
bool fill_block_rpc_details(block_rpc_extended_info& pei_rpc, const block_extended_info& bei_chain, const crypto::hash& h);
void append_per_block_increments_for_tx(const transaction& tx, std::unordered_map<uint64_t, uint32_t>& gindices);
std::string get_word_from_timstamp(uint64_t timestamp, bool use_password);
uint64_t get_timstamp_from_word(std::string word, bool& password_used, const std::string& buff);
uint64_t get_timstamp_from_word(std::string word, bool& password_used);
std::string get_word_from_timestamp(uint64_t timestamp, bool use_password);
uint64_t get_timestamp_from_word(std::string word, bool& password_used, const std::string& buff);
uint64_t get_timestamp_from_word(std::string word, bool& password_used);
bool parse_vote(const std::string& buff, std::list<std::pair<std::string, bool>>& votes);
std::string generate_origin_for_htlc(const txout_htlc& htlc, const account_keys& acc_keys);
@ -546,7 +553,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<currency::asset_descriptor_base>(pr.second));
epee::misc_utils::cast_assign_a_to_b(static_cast<currency::asset_descriptor_base>(pr.second), assets_list.back());
//*static_cast<currency::asset_descriptor_base*>(&assets_list.back()) = pr.second;
}
}
@ -947,7 +954,15 @@ namespace currency
}
}
}
/*
//---------------------------------------------------------------
template<typename invocable_t>
typename std::enable_if_t<std::is_invocable_v<invocable_t, std::ostream&>, 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);
@ -1205,7 +1220,13 @@ namespace currency
tv.short_view = std::string("op:") + get_asset_operation_type_string(ado.operation_type, true);
if (ado.opt_asset_id.has_value())
tv.short_view += std::string(" , id:") + crypto::pod_to_hex(ado.opt_asset_id);
tv.details_view = tv.short_view + std::string(" , ticker:") + ado.descriptor.ticker + std::string(" , cur.supply:") + print_money_brief(ado.descriptor.current_supply, ado.descriptor.decimal_point);
tv.details_view = tv.short_view;
if (ado.opt_descriptor.has_value())
{
tv.details_view += std::string(" , ticker:") + ado.opt_descriptor->ticker + std::string(" , cur.supply:") + print_money_brief(ado.opt_descriptor->current_supply, ado.opt_descriptor->decimal_point);
}
//@#@ TODO: add other info from asset_descriptor_operation v2+
return true;
}
template<typename t_type>

View file

@ -40,24 +40,25 @@ namespace currency
}
miner::miner(i_miner_handler* phandler, blockchain_storage& bc):m_stop(1),
//m_bc(bc),
m_template(boost::value_initialized<block>()),
m_template_no(0),
m_diffic(0),
m_thread_index(0),
m_phandler(phandler),
m_height(0),
m_pausers_count(0),
m_threads_total(0),
m_starter_nonce(0),
m_do_print_hashrate(false),
m_do_mining(false),
m_current_hash_rate(0),
m_last_hr_merge_time(0),
m_hashes(0),
m_config(AUTO_VAL_INIT(m_config)),
m_mine_address{}
miner::miner(i_miner_handler* phandler, blockchain_storage& bc)
: m_stop(1)
, m_template(boost::value_initialized<block>())
, m_template_no(0)
, m_diffic(0)
, m_thread_index(0)
, m_phandler(phandler)
, m_height(0)
, m_pausers_count(0)
, m_block_template_ready(false)
, m_threads_total(0)
, m_starter_nonce(0)
, m_do_print_hashrate(false)
, m_do_mining(false)
, m_current_hash_rate(0)
, m_last_hr_merge_time(0)
, m_hashes(0)
, m_config(AUTO_VAL_INIT(m_config))
, m_mine_address{}
{
}
//-----------------------------------------------------------------------------------------------------
@ -76,6 +77,7 @@ namespace currency
m_height = height;
++m_template_no;
m_starter_nonce = crypto::rand<uint32_t>();
m_block_template_ready = true;
return true;
}
//-----------------------------------------------------------------------------------------------------
@ -100,7 +102,8 @@ namespace currency
}
if(!m_phandler->get_block_template(bl, m_mine_address, m_mine_address, di, height, extra_nonce))
{
LOG_ERROR("Failed to get_block_template()");
// it's quite possible that block template cannot be created at particular time; need to wait
m_block_template_ready = false;
return false;
}
set_block_template(bl, di, height);
@ -110,7 +113,8 @@ namespace currency
bool miner::on_idle()
{
m_update_block_template_interval.do_call([&](){
if(is_mining())request_block_template();
if(is_mining())
request_block_template();
return true;
});
@ -233,8 +237,13 @@ namespace currency
return false;
}
if(!m_template_no)
request_block_template();//lets update block template
while(!m_template_no)
{
if (request_block_template())
break;
std::this_thread::sleep_for(std::chrono::seconds(2));
LOG_PRINT_L1("Trying to get block template....");
}
boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0);
boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0);
@ -313,12 +322,11 @@ namespace currency
uint64_t local_height = 0;
crypto::hash local_blob_data_hash = null_hash;
//uint64_t local_template_height = 0;
block b;
block b{};
while(!m_stop)
{
if(m_pausers_count)//anti split workaround
if(m_pausers_count != 0 || !m_block_template_ready)
{
misc_utils::sleep_no_w(100);
continue;

View file

@ -101,6 +101,7 @@ namespace currency
volatile uint32_t m_thread_index;
volatile uint32_t m_threads_total;
std::atomic<int32_t> m_pausers_count;
std::atomic<bool> m_block_template_ready;
epee::critical_section m_miners_count_lock;
std::list<boost::thread> m_threads;

View file

@ -247,7 +247,7 @@ namespace currency
r = process_type_in_variant_container_and_make_sure_its_unique<asset_descriptor_operation>(tx.extra, [&](const asset_descriptor_operation& ado){
asset_op_verification_context avc = { tx, id, ado };
return m_blockchain.validate_asset_operation_against_current_blochain_state(avc);
return m_blockchain.validate_asset_operation(avc, m_blockchain.get_current_blockchain_size());
}, true);
CHECK_AND_ASSERT_MES_CUSTOM(r, false, { tvc.m_verification_failed = true; }, "post-HF4 tx: asset operation is invalid");
}

View file

@ -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
@ -135,7 +135,7 @@ namespace currency
struct CORE_SYNC_DATA
{
uint64_t current_height;
uint64_t current_height; // height of the top block + 1
crypto::hash top_id;
uint64_t last_checkpoint_height;
uint64_t core_time;

View file

@ -74,6 +74,10 @@ namespace currency
//-----------------------------------------------------------------------------------
void set_to_debug_mode(uint32_t ip);
bool is_remote_client_version_allowed(int build_number, size_t min_allowed_build_number = SIZE_MAX) const;
bool is_remote_client_version_allowed(const std::string& client_version) const;
void check_all_client_versions_are_okay();
private:
//----------------- commands handlers ----------------------------------------------
int handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, currency_connection_context& context);
@ -86,11 +90,12 @@ namespace currency
//----------------- i_bc_protocol_layout ---------------------------------------
virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, currency_connection_context& exclude_context);
virtual bool relay_transactions(NOTIFY_OR_INVOKE_NEW_TRANSACTIONS::request& arg, currency_connection_context& exclude_context);
//----------------- i_currency_protocol ---------------------------------------
virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, currency_connection_context& exclude_context) override;
virtual bool relay_transactions(NOTIFY_OR_INVOKE_NEW_TRANSACTIONS::request& arg, currency_connection_context& exclude_context) override;
virtual void on_hardfork_activated(size_t hardfork_id) override;
//----------------------------------------------------------------------------------
//bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, currency_connection_context& context);
bool request_missing_objects(currency_connection_context& context, bool check_having_blocks);
bool on_connection_synchronized();
void relay_que_worker();

View file

@ -1,12 +1,14 @@
// 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
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/interprocess/detail/atomic.hpp>
#include "currency_protocol_handler.h"
#include "currency_core/currency_format_utils.h"
#include "profile_tools.h"
#include <version.h>
namespace currency
{
@ -95,7 +97,7 @@ namespace currency
ss << std::setw(29) << std::left << "Remote Host"
<< std::setw(20) << "Peer id"
<< std::setw(25) << "Recv/Sent (idle,sec)"
<< std::setw(27) << "Recv/Sent (idle,sec)"
<< std::setw(25) << "State"
<< std::setw(20) << "Livetime"
<< std::setw(20) << "Client version" << ENDL;
@ -109,7 +111,7 @@ namespace currency
conn_ss << std::setw(29) << std::left << std::string(cntxt.m_is_income ? "[INC]":"[OUT]") +
epst::get_ip_string_from_int32(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port)
<< std::setw(20) << std::hex << peer_id
<< std::setw(25) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")"
<< std::setw(27) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")"
<< std::setw(25) << get_protocol_state_string(cntxt.m_state)
<< std::setw(20) << epee::misc_utils::get_time_interval_string(livetime)
<< std::setw(20) << cntxt.m_remote_version
@ -128,9 +130,12 @@ namespace currency
template<class t_core>
bool t_currency_protocol_handler<t_core>::process_payload_sync_data(const CORE_SYNC_DATA& hshd, currency_connection_context& context, bool is_inital)
{
context.m_remote_version = hshd.client_version;
if (!tools::parse_client_version_build_number(context.m_remote_version, context.m_build_number))
{
LOG_PRINT_RED_L0("Couldn't parse remote node's version: " << context.m_remote_version << ". Connection will be dropped.");
return false;
}
if(context.m_state == currency_connection_context::state_befor_handshake && !is_inital)
return true;
@ -988,7 +993,7 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_currency_protocol_handler<t_core>::relay_transactions(NOTIFY_OR_INVOKE_NEW_TRANSACTIONS::request& arg, currency_connection_context& exclude_context)
{
{
#ifdef ASYNC_RELAY_MODE
{
CRITICAL_REGION_LOCAL(m_relay_que_lock);
@ -1002,4 +1007,52 @@ namespace currency
return relay_post_notify<NOTIFY_OR_INVOKE_NEW_TRANSACTIONS>(arg, exclude_context);
#endif
}
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
void t_currency_protocol_handler<t_core>::on_hardfork_activated(size_t hardfork_id)
{
check_all_client_versions_are_okay();
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_currency_protocol_handler<t_core>::is_remote_client_version_allowed(int build_number, size_t min_allowed_build_number /*= SIZE_MAX*/) const
{
if (min_allowed_build_number == SIZE_MAX)
min_allowed_build_number = m_core.get_blockchain_storage().get_core_runtime_config().get_min_allowed_build_version_for_height(m_core.get_top_block_height() + 1);
if (build_number < static_cast<int>(min_allowed_build_number))
return false;
return true;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_currency_protocol_handler<t_core>::is_remote_client_version_allowed(const std::string& client_version) const
{
int major = -1, minor = -1, revision = -1, build_number = -1;
std::string commit_id;
bool dirty = false;
if (!tools::parse_client_version(client_version, major, minor, revision, build_number, commit_id, dirty))
return false;
return is_remote_client_version_allowed(build_number);
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
void t_currency_protocol_handler<t_core>::check_all_client_versions_are_okay()
{
size_t min_allowed_build_number = m_core.get_blockchain_storage().get_core_runtime_config().get_min_allowed_build_version_for_height(m_core.get_top_block_height() + 1);
m_p2p->for_each_connection([&](const connection_context& cc, nodetool::peerid_type peer_id)
{
if (!is_remote_client_version_allowed(cc.m_build_number, min_allowed_build_number))
{
LOG_PRINT_CC_YELLOW(cc, "client's build number is " << cc.m_build_number << ", which is absolutely not okay in the current hardfork era, prompting us to adjust our connections accordingly.", LOG_LEVEL_0);
m_p2p->drop_connection(cc);
}
return true; // = continue
});
}
} // namespace currency

View file

@ -18,6 +18,7 @@ namespace currency
{
virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, currency_connection_context& exclude_context)=0;
virtual bool relay_transactions(NOTIFY_OR_INVOKE_NEW_TRANSACTIONS::request& arg, currency_connection_context& exclude_context)=0;
virtual void on_hardfork_activated(size_t hardfork_id) {}
//virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, currency_connection_context& context)=0;
};

View file

@ -61,7 +61,7 @@ struct core_critical_error_handler_t : public currency::i_critical_error_handler
if (dont_stop_on_time_error)
return false; // ignore such errors
LOG_ERROR(ENDL << ENDL << "Serious time sync problem detected, daemon will stop immediately" << ENDL << ENDL);
LOG_ERROR(ENDL << ENDL << "Serious TIME sync problem detected, daemon will stop immediately" << ENDL << ENDL);
// stop handling
dch.stop_handling();

View file

@ -48,6 +48,7 @@ public:
m_cmd_binder.set_handler("print_tx_prun_info", boost::bind(&daemon_commands_handler::print_tx_prun_info, this, ph::_1), "Print tx prunning info");
m_cmd_binder.set_handler("print_tx", boost::bind(&daemon_commands_handler::print_tx, this, ph::_1), "Print transaction, print_tx <transaction_hash>");
m_cmd_binder.set_handler("print_asset_info", boost::bind(&daemon_commands_handler::print_asset_info, this, ph::_1), "Print information about the given asset by its id");
m_cmd_binder.set_handler("print_blocked_ips", boost::bind(&daemon_commands_handler::print_blocked_ips, this, ph::_1), "Print ip address blacklists");
m_cmd_binder.set_handler("start_mining", boost::bind(&daemon_commands_handler::start_mining, this, ph::_1), "Start mining for specified address, start_mining <addr> [threads=1]");
m_cmd_binder.set_handler("stop_mining", boost::bind(&daemon_commands_handler::stop_mining, this, ph::_1), "Stop mining");
m_cmd_binder.set_handler("print_pool", boost::bind(&daemon_commands_handler::print_pool, this, ph::_1), "Print transaction pool (long format)");
@ -235,6 +236,20 @@ private:
return true;
}
//--------------------------------------------------------------------------------
bool print_blocked_ips(const std::vector<std::string>& args)
{
std::map<uint32_t, time_t> blocklist;
m_srv.get_ip_block_list(blocklist);
std::stringstream ss;
ss << "BLOCKED IPS:" << ENDL;
for (const auto& e : blocklist)
{
ss << string_tools::get_ip_string_from_int32(e.first) << ", time: " << std::put_time(std::localtime(&e.second), "%Y-%m-%d %H:%M:%S") << ENDL;
}
LOG_PRINT_L0(ss.str());
return true;
}
//--------------------------------------------------------------------------------
bool print_bc(const std::vector<std::string>& args)
{
if (!args.size())
@ -806,17 +821,21 @@ private:
size_t idx = 0;
for(auto& aop: asset_history)
{
ss << "[" << std::setw(2) << idx << "] operation: " << currency::get_asset_operation_type_string(aop.operation_type) << " (" << (int)aop.operation_type << ")" << ENDL <<
" ticker: \"" << aop.descriptor.ticker << "\"" << ENDL <<
" full name: \"" << aop.descriptor.full_name << "\"" << ENDL <<
" meta info: \"" << aop.descriptor.meta_info << "\"" << ENDL <<
" current supply: " << currency::print_money_brief(aop.descriptor.current_supply, aop.descriptor.decimal_point) << ENDL <<
" max supply: " << currency::print_money_brief(aop.descriptor.total_max_supply, aop.descriptor.decimal_point) << ENDL <<
" decimal point: " << (int)aop.descriptor.decimal_point << ENDL <<
" owner * 1/8: " << aop.descriptor.owner << ENDL <<
" amount cmt * 1/8: " << aop.amount_commitment << ENDL <<
" hidden supply: " << (aop.descriptor.hidden_supply ? "yes" : "no") << ENDL <<
"";
if (aop.opt_descriptor.has_value())
{
const currency::asset_descriptor_base& adb = aop.opt_descriptor.value();
ss << "[" << std::setw(2) << idx << "] operation: " << currency::get_asset_operation_type_string(aop.operation_type) << " (" << (int)aop.operation_type << ")" << ENDL <<
" ticker: \"" << adb.ticker << "\"" << ENDL <<
" full name: \"" << adb.full_name << "\"" << ENDL <<
" meta info: \"" << adb.meta_info << "\"" << ENDL <<
" current supply: " << currency::print_money_brief(adb.current_supply, adb.decimal_point) << ENDL <<
" max supply: " << currency::print_money_brief(adb.total_max_supply, adb.decimal_point) << ENDL <<
" decimal point: " << (int)adb.decimal_point << ENDL <<
" owner * 1/8: " << adb.owner << ENDL <<
" amount cmt * 1/8: " << (aop.opt_amount_commitment.has_value() ? aop.opt_amount_commitment.value() : currency::null_pkey) << ENDL <<
" hidden supply: " << (adb.hidden_supply ? "yes" : "no") << ENDL <<
"";
}
++idx;
}

View file

@ -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 Boolberry developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -714,9 +714,12 @@ bool MainWindow::show_inital()
{
TRY_ENTRY();
if (load_app_config())
{
restore_pos(true);
}
else
{
LOG_PRINT_L1("set defaults values to config");
m_config = AUTO_VAL_INIT(m_config);
this->show();
QSize sz = AUTO_VAL_INIT(sz);
@ -976,6 +979,11 @@ QString MainWindow::start_backend(const QString& params)
CATCH_ENTRY_FAIL_API_RESPONCE();
}
void MainWindow::show_notification(const QString& title, const QString& message)
{
show_notification(title.toStdString(), message.toStdString());
}
QString MainWindow::sync_call(const QString& func_name, const QString& params)
{
if (func_name == "test_call")
@ -1107,6 +1115,7 @@ bool MainWindow::get_is_disabled_notifications(const QString& param)
}
bool MainWindow::set_is_disabled_notifications(const bool& param)
{
LOG_PRINT_L1("set_is_disabled_notifications: notifications were " << (m_config.disable_notifications ? "DISABLED" : "ENABLED") << " -> now " << (param ? "DISABLED" : "ENABLED"));
m_config.disable_notifications = param;
return m_config.disable_notifications;
}
@ -1712,7 +1721,7 @@ QString MainWindow::have_secure_app_data(const QString& param)
CATCH_ENTRY_FAIL_API_RESPONCE();
}
QString MainWindow::drop_secure_app_data(const QString& param)
QString MainWindow::drop_secure_app_data()
{
TRY_ENTRY();
LOG_API_TIMING();
@ -2468,6 +2477,10 @@ QString MainWindow::print_log(const QString& param)
void MainWindow::show_notification(const std::string& title, const std::string& message)
{
TRY_ENTRY();
if (m_config.disable_notifications)
return;
LOG_PRINT_L1("system notification: \"" << title << "\", \"" << message << "\"");
// it's expected that title and message are utf-8 encoded!
@ -2482,5 +2495,3 @@ void MainWindow::show_notification(const std::string& title, const std::string&
#endif
CATCH_ENTRY2(void());
}

View file

@ -112,7 +112,7 @@ public:
QString get_network_type(const QString& param);
QString transfer(const QString& param);
QString have_secure_app_data(const QString& param);
QString drop_secure_app_data(const QString& param);
QString drop_secure_app_data();
QString get_secure_app_data(const QString& param);
QString store_secure_app_data(const QString& param, const QString& password);
QString set_master_password(const QString& param);
@ -190,6 +190,7 @@ public:
void on_menu_show(const QString& param);
QString is_remnotenode_mode_preconfigured(const QString& param);
QString start_backend(const QString& params);
void show_notification(const QString& title, const QString& message);
QString async_call(const QString& func_name, const QString& params);
QString sync_call(const QString& func_name, const QString& params);

@ -1 +1 @@
Subproject commit 2fb143cc67280f0e0cfcd3165e1c087ba8279edf
Subproject commit 7cd0e5e54a0d692ea819b9653f60a1cd5512dc2b

View file

@ -121,6 +121,7 @@ namespace nodetool
peerlist_manager& get_peerlist_manager(){return m_peerlist;}
bool handle_maintainers_entry(const maintainers_entry& me);
bool get_maintainers_info(maintainers_info_external& me);
void get_ip_block_list(std::map<uint32_t, time_t>& blocklist);
typedef COMMAND_REQUEST_STAT_INFO_T<typename t_payload_net_handler::stat_info> COMMAND_REQUEST_STAT_INFO;
private:

View file

@ -448,6 +448,13 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
void node_server<t_payload_net_handler>::get_ip_block_list(std::map<uint32_t, time_t>& blocklist)
{
CRITICAL_REGION_LOCAL(m_blocked_ips_lock);
blocklist = m_blocked_ips;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::on_maintainers_entry_update()
{
LOG_PRINT_CHANNEL_COLOR2(NULL, NULL, "Fresh maintainers info recieved(timestamp: " << m_maintainers_info_local.timestamp << ")", LOG_LEVEL_0, epee::log_space::console_color_magenta);
@ -523,7 +530,7 @@ namespace nodetool
return;
}
if (!tools::check_remote_client_version(rsp.payload_data.client_version))
if (!m_payload_handler.is_remote_client_version_allowed(rsp.payload_data.client_version))
{
LOG_PRINT_CC_YELLOW(context, "COMMAND_HANDSHAKE Failed, wrong client version: " << rsp.payload_data.client_version << ", closing connection.", LOG_LEVEL_1);
return;
@ -1391,7 +1398,7 @@ namespace nodetool
return 1;
}
if (!tools::check_remote_client_version(arg.payload_data.client_version))
if (!m_payload_handler.is_remote_client_version_allowed(arg.payload_data.client_version))
{
LOG_PRINT_CCONTEXT_L2("COMMAND_HANDSHAKE: wrong client version: " << arg.payload_data.client_version << ", closing connection.");
drop_connection(context);

View file

@ -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)
{
@ -575,7 +576,7 @@ namespace currency
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_pos_mining_details(const COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, COMMAND_RPC_GET_POS_MINING_DETAILS::response& res, connection_context& cntx)
{
if (!m_p2p.get_connections_count())
if (!m_ignore_status && !m_p2p.get_connections_count())
{
res.status = API_RETURN_CODE_DISCONNECTED;
return true;
@ -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<tx_out_zarcanum>(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))
@ -793,7 +862,7 @@ namespace currency
return true;
}
if (!m_p2p.get_payload_object().get_synchronized_connections_count())
if (!m_ignore_status && !m_p2p.get_payload_object().get_synchronized_connections_count())
{
LOG_PRINT_L0("[on_send_raw_tx]: Failed to send, daemon not connected to net");
res.status = API_RETURN_CODE_DISCONNECTED;
@ -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))
@ -1301,6 +1369,110 @@ namespace currency
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_find_outs_in_recent_blocks(const COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS::request& req, COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS::response& resp, 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_find_outs_in_recent_blocks: " << msg); 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_find_outs_in_recent_blocks: " << msg); return false; }
LOCAL_CHECK(req.address != account_public_address{}, "address is missing");
LOCAL_CHECK(req.viewkey != null_skey, "viewkey is missing");
LOCAL_CHECK(req.blocks_limit <= 5, "blocks_limit is out of allowed bounds");
// verify addess keys
crypto::point_t view_pk, spend_pk;
LOCAL_CHECK(view_pk.from_public_key(req.address.view_public_key), "cannon load point from address.view_public_key");
LOCAL_CHECK(view_pk.is_in_main_subgroup(), "address.view_public_key isn't in main subgroup");
LOCAL_CHECK(spend_pk.from_public_key(req.address.spend_public_key), "cannon load point from address.spend_public_key");
LOCAL_CHECK(spend_pk.is_in_main_subgroup(), "address.spend_public_key isn't in main subgroup");
// verify viewkey
crypto::scalar_t view_sc = req.viewkey;
LOCAL_CHECK(view_sc.is_reduced(), "viewkey is invalid");
LOCAL_CHECK(view_sc * crypto::c_point_G == view_pk, "viewkey doesn't correspond to the given address");
const blockchain_storage& bcs = m_core.get_blockchain_storage();
resp.blockchain_top_block_height = bcs.get_top_block_height();
resp.blocks_limit = req.blocks_limit;
// get blockchain transactions
std::unordered_map<uint64_t, std::pair<std::vector<crypto::hash>, std::list<transaction>>> blockchain_txs; // block height -> (vector of tx_ids, list of txs)
if (req.blocks_limit > 0)
{
uint64_t start_offset = resp.blockchain_top_block_height - req.blocks_limit + 1;
std::list<block> recent_blocks;
LOCAL_CHECK_INT_ERR(bcs.get_blocks(start_offset, static_cast<size_t>(req.blocks_limit), recent_blocks), "cannot get recent blocks");
std::vector<crypto::hash> blockchain_tx_ids, missed_tx;
for(auto& b : recent_blocks)
{
blockchain_tx_ids.insert(blockchain_tx_ids.end(), b.tx_hashes.begin(), b.tx_hashes.end());
uint64_t height = get_block_height(b);
auto& el = blockchain_txs[height];
el.first = b.tx_hashes;
missed_tx.clear();
LOCAL_CHECK_INT_ERR(bcs.get_transactions(b.tx_hashes, el.second, missed_tx), "bcs.get_transactions failed");
LOCAL_CHECK_INT_ERR(missed_tx.empty(), "missed_tx is not empty");
LOCAL_CHECK_INT_ERR(el.first.size() == el.second.size(), "el.first.size() != el.second.size()");
}
}
// get pool transactions
std::list<transaction> pool_txs;
LOCAL_CHECK_INT_ERR(m_core.get_tx_pool().get_transactions(pool_txs), "cannot get txs from pool");
// processor lambda
auto process_tx = [&](const transaction& tx, const crypto::hash& tx_id, const int64_t height) {
crypto::key_derivation derivation{};
LOCAL_CHECK(generate_key_derivation(get_tx_pub_key_from_extra(tx), req.viewkey, derivation), "generate_key_derivation failed");
for(size_t i = 0, sz = tx.vout.size(); i < sz; ++i)
{
const tx_out_v& outv = tx.vout[i];
if (outv.type() != typeid(tx_out_zarcanum))
continue;
uint64_t decoded_amount = 0;
crypto::public_key decoded_asset_id{};
crypto::scalar_t amount_blinding_mask{}, asset_id_blinding_mask{};
if (!is_out_to_acc(req.address, boost::get<tx_out_zarcanum>(outv), derivation, i, decoded_amount, decoded_asset_id, amount_blinding_mask, asset_id_blinding_mask))
continue;
auto& el = resp.outputs.emplace_back();
el.amount = decoded_amount;
el.asset_id = decoded_asset_id;
el.tx_id = tx_id;
el.tx_block_height = height;
el.output_tx_index = i;
}
return true;
};
// process blockchain txs
for(auto& [height, pair] : blockchain_txs)
{
LOCAL_CHECK(pair.first.size() == pair.second.size(), "container size inconsistency");
auto tx_it = pair.second.begin();
for(size_t i = 0, sz = pair.first.size(); i < sz; ++i, ++tx_it)
{
const crypto::hash& tx_id = pair.first[i];
LOCAL_CHECK(process_tx(*tx_it, tx_id, height), "process blockchain tx failed for tx " + crypto::pod_to_hex(tx_id));
}
}
// process pool txs
for(auto& tx : pool_txs)
{
crypto::hash tx_id = get_transaction_hash(tx);
LOCAL_CHECK(process_tx(tx, tx_id, -1), "process pool tx failed for tx " + crypto::pod_to_hex(tx_id));
}
resp.status = resp.outputs.empty() ? API_RETURN_CODE_NOT_FOUND : API_RETURN_CODE_OK;
return true;
#undef LOCAL_CHECK_INT_ERR
#undef LOCAL_CHECK
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_aliases_by_address(const COMMAND_RPC_GET_ALIASES_BY_ADDRESS::request& req, COMMAND_RPC_GET_ALIASES_BY_ADDRESS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx)
{
account_public_address addr = AUTO_VAL_INIT(addr);

View file

@ -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,11 +89,13 @@ 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);
bool on_get_alt_blocks_details(const COMMAND_RPC_GET_ALT_BLOCKS_DETAILS::request& req, COMMAND_RPC_GET_ALT_BLOCKS_DETAILS::response& res, connection_context& cntx);
bool on_get_est_height_from_date(const COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE::request& req, COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE::response& res, connection_context& cntx);
bool on_find_outs_in_recent_blocks(const COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS::request& req, COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_validate_signature(const COMMAND_VALIDATE_SIGNATURE::request& req, COMMAND_VALIDATE_SIGNATURE::response& res, epee::json_rpc::error& er, connection_context& cntx);
@ -133,6 +135,8 @@ namespace currency
MAP_JON_RPC_WE("get_alias_by_address", on_aliases_by_address, COMMAND_RPC_GET_ALIASES_BY_ADDRESS)
MAP_JON_RPC_WE("get_alias_reward", on_get_alias_reward, COMMAND_RPC_GET_ALIAS_REWARD)
MAP_JON_RPC ("get_est_height_from_date", on_get_est_height_from_date, COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE)
MAP_JON_RPC_WE("find_outs_in_recent_blocks", on_find_outs_in_recent_blocks, COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS)
//block explorer api
MAP_JON_RPC ("get_blocks_details", on_rpc_get_blocks_details, COMMAND_RPC_GET_BLOCKS_DETAILS)
MAP_JON_RPC_WE("get_tx_details", on_get_tx_details, COMMAND_RPC_GET_TX_DETAILS)
@ -150,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)

View file

@ -23,6 +23,7 @@
#include <list>
#include <string>
#include <vector>
#include <currency_core/account.h>
//#include "currency_core/basic_api_response_codes.h"
namespace currency
@ -178,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<std::string> 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_output> 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");
@ -289,6 +342,58 @@ namespace currency
};
};
//-----------------------------------------------
struct COMMAND_RPC_FIND_OUTS_IN_RECENT_BLOCKS
{
DOC_COMMAND("Retrieves information about outputs in recent blocks that are targeted for the given address with the corresponding secret view key.")
static constexpr uint64_t blocks_limit_default = 5;
struct request
{
account_public_address address;
crypto::secret_key viewkey;
uint64_t blocks_limit = blocks_limit_default;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_ADDRESS_AS_TEXT(address) DOC_DSCR("Target address for which outputs are being searched") DOC_EXMP("ZxCSpsGGeJsS8fwvQ4HktDU3qBeauoJTR6j73jAWWZxFXdF7XTbGm4YfS2kXJmAP4Rf5BVsSQ9iZ45XANXEYsrLN2L2W77dH7") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(viewkey) DOC_DSCR("Secret view key corresponding to the given address.") DOC_EXMP("5fa8eaaf231a305053260ff91d69c6ef1ecbd0f5") DOC_END
KV_SERIALIZE(blocks_limit) DOC_DSCR("Block count limit. If 0, only the transaction pool will be searched. Maximum and default is " + epee::string_tools::num_to_string_fast(blocks_limit_default) + ".") DOC_EXMP(1711021795) DOC_END
END_KV_SERIALIZE_MAP()
};
struct out_entry
{
uint64_t amount;
crypto::public_key asset_id;
crypto::hash tx_id;
int64_t tx_block_height;
uint64_t output_tx_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount) DOC_DSCR("The amount of the output.") DOC_EXMP(1000000000000) DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset ID of the output.") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Transaction ID where the output is present, if found.") DOC_EXMP("a6e8da986858e6825fce7a192097e6afae4e889cabe853a9c29b964985b23da8") DOC_END
KV_SERIALIZE(tx_block_height) DOC_DSCR("Block height where the transaction is present.") DOC_EXMP(2555000) DOC_END
KV_SERIALIZE(output_tx_index) DOC_DSCR("Index of the output in the transaction.") DOC_EXMP(2) DOC_END
END_KV_SERIALIZE_MAP()
};
struct response
{
std::vector<out_entry> outputs;
uint64_t blockchain_top_block_height;
uint64_t blocks_limit;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outputs) DOC_DSCR("List of found outputs.") DOC_EXMP_AUTO(1) DOC_END
KV_SERIALIZE(blockchain_top_block_height) DOC_DSCR("Height of the most recent block in the blockchain.") DOC_EXMP(2555000) DOC_END
KV_SERIALIZE(blocks_limit) DOC_DSCR("Used limit for block count.") DOC_EXMP(5) DOC_END
KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END
END_KV_SERIALIZE_MAP()
};
};
//-----------------------------------------------
struct COMMAND_RPC_GET_TX_POOL
{
DOC_COMMAND("Retreives transactions from tx pool (and other information).")
@ -583,32 +688,32 @@ namespace currency
};
//-----------------------------------------------
struct COMMAND_RPC_SEND_RAW_TX
{
struct COMMAND_RPC_SEND_RAW_TX
{
DOC_COMMAND("Broadcasts a raw transaction encoded in hexadecimal format to the network.");
struct request
{
std::string tx_as_hex;
{
std::string tx_as_hex;
request() {}
explicit request(const transaction &);
request() {}
explicit request(const transaction &);
BEGIN_KV_SERIALIZE_MAP()
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
END_KV_SERIALIZE_MAP()
};
};
struct response
{
std::string status;
struct response
{
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END
END_KV_SERIALIZE_MAP()
};
};
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END
END_KV_SERIALIZE_MAP()
};
};
//-----------------------------------------------
@ -619,7 +724,7 @@ namespace currency
std::vector<std::string> txs_as_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txs_as_hex) DOC_DSCR("List of transactions as a hexadecimal strings.") DOC_EXMP_AGGR("000535b8b2e.....3685a86", "00087368b2e.....349b77f") DOC_END
KV_SERIALIZE(txs_as_hex) DOC_DSCR("List of transactions as a hexadecimal strings.") DOC_EXMP_AGGR("000535b8b2e.....3685a86", "00087368b2e.....349b77f") DOC_END
END_KV_SERIALIZE_MAP()
};

View file

@ -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/serialization/optional.hpp>
// boost::optional
template <template <bool> class Archive, class T>
bool do_serialize(Archive<false> &ar, boost::optional<T> &o)
{
@ -58,3 +59,55 @@ bool do_serialize(Archive<true> &ar, boost::optional<T> &v)
return true;
}
// std::optional
template <template <bool> class Archive, class T>
bool do_serialize(Archive<false> &ar, std::optional<T> &o)
{
//reading flag
bool is_none = false;
if (!::do_serialize(ar, is_none))
{
ar.stream().setstate(std::ios::failbit);
return false;
}
if (is_none)
{
o.reset();
return true;
}
o = T();
T& rval = o.value();
//reading value
if (!::do_serialize(ar, rval))
{
ar.stream().setstate(std::ios::failbit);
return false;
}
return true;
}
template <template <bool> class Archive, class T>
bool do_serialize(Archive<true> &ar, std::optional<T> &v)
{
//writing flag
bool is_none = !v.has_value();
if (!::do_serialize(ar, is_none))
{
ar.stream().setstate(std::ios::failbit);
return false;
}
if (is_none)
{
return true;
}
if (!::do_serialize(ar, v.value()))
{
ar.stream().setstate(std::ios::failbit);
return false;
}
return true;
}

View file

@ -238,8 +238,7 @@ bool t_unserializable_object_from_blob(t_object& to, const std::string& blob)
ss << blob;
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, to);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob");
return true;
return r;
}
//---------------------------------------------------------------
template<class t_object>

View file

@ -12,6 +12,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/join.hpp>
#include "include_base_utils.h"
#include "common/command_line.h"
#include "common/util.h"
@ -26,7 +27,7 @@
#include "string_coding.h"
#include "wallet/wrap_service.h"
#include "common/general_purpose_commands_defs.h"
#include "common/mnemonic-encoding.h"
#include "wallet/wallet_helpers.h"
@ -87,9 +88,7 @@ namespace ph = boost::placeholders;
} \
catch (const tools::error::tx_too_big& e) \
{ \
currency::transaction tx = e.tx(); \
fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " is too big. Transaction size: " << \
get_object_blobsize(e.tx()) << " bytes, transaction size limit: " << e.tx_size_limit() << " bytes. Try to separate this payment into few smaller transfers."; \
fail_msg_writer() << "transaction is too big. " << e.get_message() << " Try to split this payment into a few smaller transfers."; \
} \
catch (const tools::error::zero_destination&) \
{ \
@ -126,6 +125,7 @@ namespace
{
const command_line::arg_descriptor<std::string> arg_wallet_file ("wallet-file", "Use wallet <arg>", "");
const command_line::arg_descriptor<std::string> arg_generate_new_wallet ("generate-new-wallet", "Generate new wallet and save it to <arg> or <address>.wallet by default", "");
const command_line::arg_descriptor<bool> arg_derive_custom_seed("derive_custom_seed", "Derive seed phrase from custom 24-words secret(advanced option, do it on your own risk)", "");
const command_line::arg_descriptor<std::string> arg_generate_new_auditable_wallet ("generate-new-auditable-wallet", "Generate new auditable wallet and store it to <arg>", "");
const command_line::arg_descriptor<std::string> arg_daemon_address ("daemon-address", "Use daemon instance at <host>:<port>", "");
const command_line::arg_descriptor<std::string> arg_daemon_host ("daemon-host", "Use daemon instance at host <arg> instead of localhost", "");
@ -144,6 +144,7 @@ namespace
const command_line::arg_descriptor<unsigned int> arg_set_timeout("set-timeout", "Set timeout for the wallet");
const command_line::arg_descriptor<std::string> arg_voting_config_file("voting-config-file", "Set voting config instead of getting if from daemon", "");
const command_line::arg_descriptor<bool> arg_no_password_confirmations("no-password-confirmation", "Enable/Disable password confirmation for transactions", false);
const command_line::arg_descriptor<bool> arg_seed_doctor("seed-doctor", "Experimental: if your seed is not working for recovery this is likely because you've made a mistake whene you were doing back up(typo, wrong words order, missing word). This experimental code will attempt to recover seed phrase from with few approaches.");
const command_line::arg_descriptor< std::vector<std::string> > arg_command ("command", "");
@ -590,6 +591,12 @@ bool simple_wallet::try_connect_to_daemon()
//----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password, bool create_auditable_wallet)
{
if (!currency::validate_password(password))
{
fail_msg_writer() << R"(Provided password contains invalid characters. Only letters, numbers and ~!?@#$%^&*_+|{}[]()<>:;"'-=/., symbols are allowed.)" << ENDL;
return false;
}
m_wallet_file = wallet_file;
m_wallet.reset(new tools::wallet2());
@ -1201,7 +1208,7 @@ bool simple_wallet::show_staking_history(const std::vector<std::string>& args)
bool transfers_found = false;
for (auto it = transfers.rbegin(); it != transfers.rend(); it++)
{
const auto& td = *it;
const auto& td = it->second;
if (timestamp && td.m_ptx_wallet_info->m_block_timestamp < timestamp)
break;
@ -1263,8 +1270,9 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
m_wallet->get_transfers(transfers);
bool transfers_found = false;
for (const auto& td : transfers)
for (const auto& tr : transfers)
{
const auto& td = tr.second;
if (!filter || available != static_cast<bool>(td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_SPENT))
{
if (!transfers_found)
@ -1307,8 +1315,9 @@ bool simple_wallet::show_incoming_transfers_counts(const std::vector<std::string
uint64_t spent_count = 0;
uint64_t unspent_count = 0;
for (const auto& td : transfers)
for (const auto& tr : transfers)
{
const auto& td = tr.second;
if (td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_SPENT)
{
++spent_count;
@ -2087,17 +2096,24 @@ bool simple_wallet::deploy_new_asset(const std::vector<std::string> &args)
fail_msg_writer() << "Failed to load json file with asset specification: " << args[0];
return true;
}
if (!validate_asset_ticker_and_full_name(adb))
{
fail_msg_writer() << "ticker or full_name are invalid (perhaps they contain invalid symbols)";
return true;
}
tx_destination_entry td = AUTO_VAL_INIT(td);
td.addr.push_back(m_wallet->get_account().get_public_address());
td.amount = adb.current_supply;
td.asset_id = currency::null_pkey;
std::vector<currency::tx_destination_entry> destinations;
destinations.push_back(td);
currency::transaction result_tx = AUTO_VAL_INIT(result_tx);
currency::finalized_tx ft{};
crypto::public_key result_asset_id = currency::null_pkey;
m_wallet->deploy_new_asset(adb, destinations, result_tx, result_asset_id);
m_wallet->deploy_new_asset(adb, destinations, ft, result_asset_id);
success_msg_writer(true) << "New asset successfully deployed with tx " << get_transaction_hash(result_tx) << " (unconfirmed) : " << ENDL
success_msg_writer(true) << "New asset successfully deployed with tx " << ft.tx_id << " (unconfirmed) : " << ENDL
<< "Asset ID: " << result_asset_id << ENDL
<< "Title: " << adb.full_name << ENDL
<< "Ticker: " << adb.ticker << ENDL
@ -2872,6 +2888,266 @@ bool search_for_wallet_file(const std::wstring &search_here/*, const std::string
return false;
}
int custom_seed_builder()
{
success_msg_writer() <<
"**********************************************************************\n" <<
"This is an experimental tool that helps you create a custom seed phrase \n"
"based on your own 24 words. It can be extremely unsafe, so only use it \n"
"if you're confident in what you're doing.\n"
"**********************************************************************";
success_msg_writer() << "Please enter 24 words that you want to use as base for the seed:";
std::string seed_24;
std::getline(std::cin, seed_24);
std::list<std::string> words;
std::string trimed_seed_24 = epee::string_tools::trim(seed_24);
boost::split(words, trimed_seed_24, boost::is_space(), boost::token_compress_on);
seed_24 = boost::algorithm::join(words, " ");
std::string passphrase;
success_msg_writer() << "Please enter seed passphrase(it's highly recommended to use passphrase for custom seed):";
std::getline(std::cin, passphrase);
if (passphrase.empty())
{
success_msg_writer() << "Using unsecured seed(no passphrase)";
}
else
{
std::string passphrase_confirmation;
success_msg_writer() << "Please confirm passphrase:";
std::getline(std::cin, passphrase_confirmation);
if (passphrase_confirmation != passphrase)
{
success_msg_writer() << "Passphrase mismatched, try again";
return EXIT_FAILURE;
}
}
account_base acc;
acc.generate();
std::string pass_protected_or_not = "";
std::vector<unsigned char> binary_from_seed = tools::mnemonic_encoding::text2binary(seed_24);
std::vector<unsigned char> processed_binary_from_seed = binary_from_seed;
if (!passphrase.empty())
{
//encrypt seed phrase binary data
account_base::crypt_with_pass(&binary_from_seed[0], binary_from_seed.size(), &processed_binary_from_seed[0], passphrase);
pass_protected_or_not = "(secured with passphrase)";
}
{
pass_protected_or_not = "(!without passphrase!)";
}
const std::string new_seed = acc.get_seed_phrase(passphrase, processed_binary_from_seed);
success_msg_writer() << "Here is your seed" << pass_protected_or_not << "\n " << new_seed;
return EXIT_SUCCESS;
}
int seed_doctor()
{
success_msg_writer() <<
"**********************************************************************\n" <<
"This is experimental tool that might help you to recover your wallet's corrupted seed phrase. \n" <<
"It might help to sort out only trivial situations where one word was written wrong or \n" <<
"two word was written in wrong order\n" <<
"**********************************************************************";
success_msg_writer() << "Please enter problematic seed phrase:";
std::string seed;
std::getline(std::cin, seed);
success_msg_writer() << "Please enter wallet address if you have it(press enter if you don't know it):";
std::string address;
std::getline(std::cin, address);
address = epee::string_tools::trim(address);
std::string passphrase;
bool check_summ_available = false;
//cut the last timestamp word from restore_dats
std::vector<std::string> words;
boost::split(words, seed, boost::is_space());
std::set<size_t> failed_words;
size_t i = 0;
//let's validate each word
for (const auto& w : words)
{
if (!tools::mnemonic_encoding::valid_word(w))
{
success_msg_writer() << "Word " << i << " '" << w << "' is invalid, attempting to restore it";
failed_words.insert(i);
}
i++;
}
if (words.size() == SEED_PHRASE_V1_WORDS_COUNT)
{
// 24 seed words + one timestamp word = 25 total
success_msg_writer() << "SEED_PHRASE_V1_WORDS_COUNT, checksum is unavailable";
if (address.empty())
{
success_msg_writer() << "With SEED_PHRASE_V1_WORDS_COUNT and address unknown it's not enough information to recover it, sorry";
return EXIT_FAILURE;
}
}
else if (words.size() == SEED_PHRASE_V2_WORDS_COUNT)
{
// 24 seed words + one timestamp word + one flags & checksum = 26 total
success_msg_writer() << "SEED_PHRASE_V2_WORDS_COUNT, checksum is available";
check_summ_available = true;
}
else if (words.size() == 24)
{
//seed only with no date, add date to it
std::string zero_word = tools::mnemonic_encoding::word_by_num(0); //zero_word is likely to be "like"
words.push_back(zero_word); //
}
else
{
success_msg_writer() << "Impossible to recover something with " << words.size() << " words only";
return EXIT_FAILURE;
}
bool pass_protected = false;
bool success = account_base::is_seed_password_protected(seed, pass_protected);
success_msg_writer() << "SECURED_SEED: " << (pass_protected ? "true" : "false");
if (pass_protected)
{
success_msg_writer() << "Please enter seed passphrase(if passphrase is wrong then chances to correct seed recovery is nearly zero):";
std::getline(std::cin, passphrase);
}
size_t global_candidates_count = 0;
auto brute_force_func = [&](size_t word_index) -> bool {
const map<string, uint32_t>& all_words = tools::mnemonic_encoding::get_words_map();
success_msg_writer() << "Brute forcing word " << word_index << " ....";
size_t candidates_count = 0;
std::vector<std::string> words_local = words;
for (auto it = all_words.begin(); it != all_words.end(); it++)
{
words_local[word_index] = it->first;
std::string result = boost::algorithm::join(words_local, " ");
account_base acc;
bool r = acc.restore_from_seed_phrase(result, passphrase);
if (r)
{
if (!address.empty())
{
//check against address
if (acc.get_public_address_str() == address)
{
success_msg_writer(true) << "!!!SUCCESS!!!";
success_msg_writer() << "Seed recovered, please write down recovered seed and use it to restore the wallet:\n" << result;
return true;
}
}
else
{
success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str();
candidates_count++;
}
}
}
global_candidates_count += candidates_count;
return false;
};
auto swap_func = [&](size_t word_index) -> bool {
size_t candidates_count = 0;
std::vector<std::string> words_local = words;
std::string tmp = words_local[word_index];
words_local[word_index] = words_local[word_index + 1];
words_local[word_index + 1] = tmp;
std::string result = boost::algorithm::join(words_local, " ");
account_base acc;
bool r = acc.restore_from_seed_phrase(result, passphrase);
if (r)
{
if (!address.empty())
{
//check against address
if (acc.get_public_address_str() == address)
{
success_msg_writer(true) << "!!!SUCCESS!!!";
success_msg_writer() << "Seed recovered, please write down recovered seed and use it to restore the wallet:\n" << result;
return true;
}
}
else
{
success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str();
candidates_count++;
}
}
global_candidates_count += candidates_count;
return false;
};
if (failed_words.size())
{
if (failed_words.size() > 1)
{
success_msg_writer() << "Restoring more then 1 broken words not implemented yet, sorry";
return EXIT_FAILURE;
}
if (!check_summ_available && address.empty())
{
success_msg_writer() << "No address and no checksum, recovery is impossible, sorry";
return EXIT_FAILURE;
}
size_t broken_word_index = *failed_words.begin();
bool r = brute_force_func(broken_word_index);
success_msg_writer() << "Brute forcing finished, " << global_candidates_count << " potential candidates found";
return r ? EXIT_SUCCESS : EXIT_FAILURE;
}
else
{
if (!check_summ_available && address.empty())
{
success_msg_writer() << "No address and no checksum, recovery is limited only to date reset";
std::string result = boost::algorithm::join(words, " ");
account_base acc;
bool r = acc.restore_from_seed_phrase(result, passphrase);
success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str();
return EXIT_FAILURE;
}
success_msg_writer() << "Brute forcing all each word";
for (size_t i = 0; i != words.size() && i != 24; i++)
{
bool r = brute_force_func(i);
if(r)
return EXIT_SUCCESS;
}
}
success_msg_writer() << "Brute forcing finished, " << global_candidates_count << " potential candidates found";
success_msg_writer() << "Swap check...";
for (size_t i = 0; i != words.size() && i != 23; i++)
{
bool r = swap_func(i);
if (r)
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
//----------------------------------------------------------------------------------------------------
#ifdef WIN32
int wmain( int argc, wchar_t* argv_w[ ], wchar_t* envp[ ] )
@ -2879,7 +3155,7 @@ int wmain( int argc, wchar_t* argv_w[ ], wchar_t* envp[ ] )
int main(int argc, char* argv[])
#endif
{
#ifdef WIN32
#if defined(WIN32) && defined(_DEBUG)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//_CrtSetBreakAlloc(9594);
#endif
@ -2936,7 +3212,10 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_set_timeout);
command_line::add_arg(desc_params, arg_voting_config_file);
command_line::add_arg(desc_params, arg_no_password_confirmations);
command_line::add_arg(desc_params, command_line::arg_generate_rpc_autodoc);
command_line::add_arg(desc_params, command_line::arg_generate_rpc_autodoc);
command_line::add_arg(desc_params, arg_seed_doctor);
command_line::add_arg(desc_params, arg_derive_custom_seed);
tools::wallet_rpc_server::init_options(desc_params);
@ -3014,6 +3293,16 @@ int main(int argc, char* argv[])
bool offline_mode = command_line::get_arg(vm, arg_offline_mode);
if (command_line::has_arg(vm, arg_seed_doctor))
{
return seed_doctor();
}
if (command_line::has_arg(vm, arg_derive_custom_seed))
{
return custom_seed_builder();
}
if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port))
{
// runs wallet as RPC server
@ -3198,7 +3487,7 @@ int main(int argc, char* argv[])
//runs wallet with console interface
sw->set_offline_mode(offline_mode);
r = sw->init(vm);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet");
CHECK_AND_ASSERT_MES(r, EXIT_FAILURE, "Failed to initialize wallet");
if (command_line::get_arg(vm, arg_generate_new_wallet).size() || command_line::get_arg(vm, arg_generate_new_auditable_wallet).size())
return EXIT_FAILURE;
@ -3224,3 +3513,4 @@ int main(int argc, char* argv[])
CATCH_ENTRY_L0(__func__, EXIT_FAILURE);
return EXIT_SUCCESS;
}

View file

@ -29,7 +29,7 @@ namespace currency
typedef std::vector<std::string> command_type;
simple_wallet();
~simple_wallet();
virtual ~simple_wallet();
bool init(const boost::program_options::variables_map& vm);
bool deinit();
bool run();

View file

@ -590,7 +590,8 @@ namespace
error_str << "wallet address " << user_str << " doesn't match the address previously set in daemon and/or other workers.";
}
set_miner_address(address);
if (!error)
set_miner_address(address);
}
if (error)

View file

@ -5,9 +5,9 @@
#define PROJECT_MAJOR_VERSION "2"
#define PROJECT_MINOR_VERSION "0"
#define PROJECT_REVISION "0"
#define PROJECT_REVISION "1"
#define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION
#define PROJECT_VERSION_BUILD_NO 333
#define PROJECT_VERSION_BUILD_NO 367
#define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO)
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]"

View file

@ -20,7 +20,11 @@ namespace tools
{
bool default_http_core_proxy::set_connection_addr(const std::string& url)
{
m_daemon_address = url;
if (m_daemon_address != url)
{
m_daemon_address = url;
m_http_client.disconnect();
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------

View file

@ -9,7 +9,6 @@
#include <boost/serialization/deque.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/extended_type_info.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/optional.hpp>
#include <atomic>

View file

@ -56,6 +56,9 @@ void static_destroy_handler()
LOG_PRINT_L0("[DESTROY CALLBACK HANDLER FINISHED]: ");
}
namespace plain_wallet
{
struct plain_wallet_instance
@ -63,7 +66,9 @@ namespace plain_wallet
plain_wallet_instance() :initialized(false), gjobs_counter(1)
{}
wallets_manager gwm;
std::atomic<bool> initialized;
std::atomic<bool> initialized = false;
std::atomic<bool> postponed_run_wallet = false;
std::atomic<bool> postponed_main_worked_started = false;
std::atomic<uint64_t> gjobs_counter;
std::map<uint64_t, std::string> gjobs;
@ -73,7 +78,6 @@ namespace plain_wallet
std::shared_ptr<plain_wallet::plain_wallet_instance> ginstance_ptr;
typedef epee::json_rpc::response<epee::json_rpc::dummy_result, error> error_response;
@ -182,8 +186,11 @@ namespace plain_wallet
}
std::string init(const std::string& ip, const std::string& port, const std::string& working_dir, int log_level)
std::string init(const std::string& ip_, const std::string& port_, const std::string& working_dir, int log_level)
{
std::string ip = ip_;
std::string port = port_;
auto local_ptr = std::atomic_load(&ginstance_ptr);
if (local_ptr)
{
@ -197,6 +204,14 @@ namespace plain_wallet
std::shared_ptr<plain_wallet_instance> ptr(new plain_wallet_instance());
if (ip.empty())
{
ip = "0.0.0.0";
port = "0";
ptr->postponed_run_wallet = true;
}
set_bundle_working_dir(working_dir);
initialize_logs(log_level);
@ -217,10 +232,14 @@ namespace plain_wallet
ptr->gwm.set_use_deffered_global_outputs(true);
if(!ptr->gwm.start())
if (!ptr->postponed_run_wallet && !ptr->postponed_main_worked_started)
{
LOG_ERROR("Failed to start wallets_manager");
return GENERAL_INTERNAL_ERRROR_INIT;
if (!ptr->gwm.start())
{
LOG_ERROR("Failed to start wallets_manager");
return GENERAL_INTERNAL_ERRROR_INIT;
}
ptr->postponed_main_worked_started = true;
}
LOG_PRINT_L0("[INIT PLAIN_WALLET_INSTANCE] Ver:" << PROJECT_VERSION_LONG << "(" << BUILD_TYPE << ")");
@ -261,11 +280,12 @@ namespace plain_wallet
std::string init(const std::string& address, const std::string& working_dir, int log_level)
{
epee::net_utils::http::url_content url_data = AUTO_VAL_INIT(url_data);
if(!epee::net_utils::parse_url(address, url_data))
if(!address.empty() && !epee::net_utils::parse_url(address, url_data))
{
LOG_ERROR("Failed to parse address");
return API_RETURN_CODE_BAD_ARG;
}
return init(url_data.host, std::to_string(url_data.port), working_dir, log_level);
}
@ -438,7 +458,10 @@ namespace plain_wallet
{
ok_response.result.recovered = true;
}
inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
if (!inst_ptr->postponed_run_wallet)
{
inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
}
return epee::serialization::store_t_to_json(ok_response);
}
@ -460,7 +483,10 @@ namespace plain_wallet
{
ok_response.result.recovered = true;
}
inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
if (!inst_ptr->postponed_run_wallet)
{
inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
}
return epee::serialization::store_t_to_json(ok_response);
}
error_response err_result = AUTO_VAL_INIT(err_result);
@ -481,7 +507,10 @@ namespace plain_wallet
{
ok_response.result.recovered = true;
}
inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
if (!inst_ptr->postponed_run_wallet)
{
inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
}
return epee::serialization::store_t_to_json(ok_response);
}
error_response err_result = AUTO_VAL_INIT(err_result);
@ -550,6 +579,52 @@ namespace plain_wallet
return std::string("{ \"job_id\": ") + std::to_string(job_id) + "}";
}
std::string handle_reset_connection_url(const std::string& url)
{
GET_INSTANCE_PTR(inst_ptr);
inst_ptr->gwm.set_remote_node_url(url);
view::api_response ar = AUTO_VAL_INIT(ar);
ar.error_code = API_RETURN_CODE_OK;
return epee::serialization::store_t_to_json(ar);
}
std::string handle_run_wallet(uint64_t instance_id)
{
GET_INSTANCE_PTR(inst_ptr);
//postponed worker loop launch
if (!inst_ptr->postponed_main_worked_started)
{
if (!inst_ptr->gwm.start())
{
LOG_ERROR("Failed to start wallets_manager");
return API_RETURN_CODE_INTERNAL_ERROR;
}
inst_ptr->postponed_main_worked_started = true;
}
view::api_response ar = AUTO_VAL_INIT(ar);
ar.error_code = inst_ptr->gwm.run_wallet(instance_id);
return epee::serialization::store_t_to_json(ar);
}
std::string handle_configure(const std::string& settings_json)
{
GET_INSTANCE_PTR(inst_ptr);
configure_object conf = AUTO_VAL_INIT(conf);
configure_response conf_resp = AUTO_VAL_INIT(conf_resp);
bool res = epee::serialization::load_t_from_json(conf, settings_json);
if (!res)
{
conf_resp.status = API_RETURN_CODE_BAD_ARG;
return epee::serialization::store_t_to_json(conf_resp);
}
inst_ptr->postponed_run_wallet = conf.postponed_run_wallet;
conf_resp.status = API_RETURN_CODE_OK;
return epee::serialization::store_t_to_json(conf_resp);
}
std::string sync_call(const std::string& method_name, uint64_t instance_id, const std::string& params)
{
@ -612,6 +687,18 @@ namespace plain_wallet
{
res = get_wallet_status(instance_id);
}
else if (method_name == "configure")
{
res = handle_configure(params);
}
else if (method_name == "reset_connection_url")
{
res = handle_reset_connection_url(params);
}
else if (method_name == "run_wallet")
{
res = handle_run_wallet(instance_id);
}
else
{
view::api_response ar = AUTO_VAL_INIT(ar);
@ -621,9 +708,6 @@ namespace plain_wallet
return res;
}
std::string try_pull_result(uint64_t job_id)
{
auto inst_ptr = std::atomic_load(&ginstance_ptr);

View file

@ -47,4 +47,19 @@ namespace plain_wallet
END_KV_SERIALIZE_MAP()
};
struct configure_object
{
bool postponed_run_wallet = false;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(postponed_run_wallet)
END_KV_SERIALIZE_MAP()
};
struct configure_response
{
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
} // namespace tools

File diff suppressed because it is too large Load diff

View file

@ -50,6 +50,8 @@
#define WALLET_DEFAULT_TX_SPENDABLE_AGE CURRENCY_HF4_MANDATORY_MIN_COINAGE
#define WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL 1
#define WALLET_CONCISE_MODE_MAX_REORG_BLOCKS CURRENCY_BLOCKS_PER_DAY * 7 //week
#define WALLET_CONCISE_MODE_MOBILE_MAX_HISTORY_SIZE 500
const uint64_t WALLET_MINIMUM_HEIGHT_UNSET_CONST = std::numeric_limits<uint64_t>::max();
@ -134,9 +136,9 @@ namespace tools
std::unordered_map<crypto::key_image, size_t> m_key_images;
std::vector<wallet_public::wallet_transfer_info> m_transfer_history;
std::unordered_map<crypto::hash, currency::transaction> m_unconfirmed_in_transfers;
std::unordered_map<crypto::hash, tools::wallet_public::wallet_transfer_info> m_unconfirmed_txs;
unconfirmed_txs_container m_unconfirmed_txs;
std::unordered_set<crypto::hash> m_unconfirmed_multisig_transfers;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
tx_secrete_keys_container m_tx_keys;
std::unordered_map<crypto::public_key, wallet_own_asset_context> m_own_asset_descriptors;
std::unordered_map<crypto::public_key, currency::asset_descriptor_base> m_custom_assets; //assets that manually added by user
mutable std::unordered_map<crypto::public_key, currency::asset_descriptor_base> m_whitelisted_assets; //assets that whitelisted
@ -149,13 +151,14 @@ namespace tools
uint64_t m_last_pow_block_h = 0;
std::list<std::pair<uint64_t, wallet_event_t>> m_rollback_events;
std::list<std::pair<uint64_t, uint64_t> > m_last_zc_global_indexs; // <height, last_zc_global_indexs>, biggest height comes in front
//variables that not being serialized
std::atomic<uint64_t> m_last_bc_timestamp = 0;
uint64_t m_height_of_start_sync = 0;
std::atomic<uint64_t> m_last_sync_percent = 0;
mutable uint64_t m_current_wallet_file_size = 0;
bool m_use_assets_whitelisting = true;
bool m_use_assets_whitelisting = true;
mutable std::optional<bool> m_has_bare_unspent_outputs; // recalculated each time the balance() is called
// variables that should be part of state data object but should not be stored during serialization
@ -201,7 +204,17 @@ namespace tools
a & m_chain;
a & m_minimum_height;
a & m_amount_gindex_to_transfer_id;
a & m_transfers;
if (ver <= 167)
{
std::deque<transfer_details> transfer_container_old;
a& transfer_container_old;
for (size_t i = 0; i != transfer_container_old.size(); i++){m_transfers[i] = transfer_container_old[i];}
}
else
{
a& m_transfers;
}
a & m_multisig_transfers;
a & m_key_images;
a & m_unconfirmed_txs;
@ -234,7 +247,12 @@ namespace tools
{
//workaround for m_last_zc_global_indexs holding invalid index for last item
m_last_zc_global_indexs.pop_front();
}
}
if (ver <= 167)
{
return;
}
}
};
@ -393,7 +411,7 @@ namespace tools
//i_wallet2_callback* callback() const { return m_wcallback; }
//void callback(i_wallet2_callback* callback) { m_callback = callback; }
void callback(std::shared_ptr<i_wallet2_callback> callback) { m_wcallback = callback; m_do_rise_transfer = (callback != nullptr); }
i_wallet2_callback* get_callback() { return m_wcallback.get(); }
std::shared_ptr<i_wallet2_callback> get_callback() { return m_wcallback.lock(); }
void set_do_rise_transfer(bool do_rise) { m_do_rise_transfer = do_rise; }
bool has_related_alias_entry_unconfirmed(const currency::transaction& tx);
@ -420,10 +438,16 @@ namespace tools
bool check_available_sources(std::list<uint64_t>& amounts);
void deploy_new_asset(const currency::asset_descriptor_base& asset_info, const std::vector<currency::tx_destination_entry>& destinations, currency::transaction& result_tx, crypto::public_key& new_asset_id);
void emit_asset(const crypto::public_key asset_id, std::vector<currency::tx_destination_entry>& destinations, currency::transaction& result_tx);
void update_asset(const crypto::public_key asset_id, const currency::asset_descriptor_base new_descriptor, currency::transaction& result_tx);
void burn_asset(const crypto::public_key asset_id, uint64_t amount_to_burn, currency::transaction& result_tx);
void transfer_asset_ownership(const crypto::public_key asset_id, const crypto::public_key& new_owner, currency::transaction& result_tx);
void emit_asset(const crypto::public_key& asset_id, std::vector<currency::tx_destination_entry>& destinations, currency::transaction& result_tx);
void update_asset(const crypto::public_key& asset_id, const currency::asset_descriptor_base new_descriptor, currency::transaction& result_tx);
void burn_asset(const crypto::public_key& asset_id, uint64_t amount_to_burn, currency::transaction& result_tx, const std::vector<currency::tx_service_attachment>& service_entries = std::vector<currency::tx_service_attachment>(), const std::string& address_to_point = std::string(), uint64_t native_amount_to_point = 0);
void transfer_asset_ownership(const crypto::public_key& asset_id, const currency::asset_owner_pub_key_v& new_owner_v, currency::transaction& result_tx);
void deploy_new_asset(const currency::asset_descriptor_base& asset_info, const std::vector<currency::tx_destination_entry>& destinations, currency::finalized_tx& ft, crypto::public_key& new_asset_id);
void emit_asset(const crypto::public_key& asset_id, const std::vector<currency::tx_destination_entry>& destinations, currency::finalized_tx& ft);
void update_asset(const crypto::public_key& asset_id, const currency::asset_descriptor_base& new_descriptor, currency::finalized_tx& ft);
void burn_asset(const crypto::public_key& asset_id, uint64_t amount_to_burn, currency::finalized_tx& ft, const std::vector<currency::tx_service_attachment>& service_entries = std::vector<currency::tx_service_attachment>(), const std::string& address_to_point = std::string(), uint64_t native_amount_to_point = 0);
void transfer_asset_ownership(const crypto::public_key& asset_id, const currency::asset_owner_pub_key_v& new_owner_v, currency::finalized_tx& ft);
bool daemon_get_asset_info(const crypto::public_key& asset_id, currency::asset_descriptor_base& adb);
bool set_core_proxy(const std::shared_ptr<i_core_proxy>& proxy);
@ -559,10 +583,12 @@ namespace tools
currency::transaction &escrow_template_tx);
bool check_connection();
bool truncate_transfers_and_history(const std::list<size_t>& items_to_remove);
bool truncate_wallet();
// PoS mining
void do_pos_mining_prepare_entry(mining_context& cxt, size_t transfer_index);
bool do_pos_mining_iteration(mining_context& cxt, size_t transfer_index, uint64_t ts);
void do_pos_mining_prepare_entry(mining_context& cxt, const transfer_details& td);
bool do_pos_mining_iteration(mining_context& cxt, uint64_t ts);
template<typename idle_condition_cb_t> //do refresh as external callback
bool scan_pos(mining_context& cxt, std::atomic<bool>& stop, idle_condition_cb_t idle_condition_cb, const currency::core_runtime_config &runtime_config);
bool fill_mining_context(mining_context& ctx);
@ -589,6 +615,7 @@ namespace tools
void sign_transfer_files(const std::string& tx_sources_file, const std::string& signed_tx_file, currency::transaction& tx);
void submit_transfer(const std::string& signed_tx_blob, currency::transaction& tx);
void submit_transfer_files(const std::string& signed_tx_file, currency::transaction& tx);
void submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const crypto::eth_signature& eth_sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked);
void sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id,
uint64_t fee, size_t& outs_total, uint64_t& amount_total, size_t& outs_swept, uint64_t& amount_swept, currency::transaction* p_result_tx = nullptr, std::string* p_filename_or_unsigned_tx_blob_str = nullptr);
@ -725,6 +752,9 @@ namespace tools
bool accept_ionic_swap_proposal(const std::string& raw_proposal, currency::transaction& result_tx);
bool accept_ionic_swap_proposal(const wallet_public::ionic_swap_proposal& proposal, currency::transaction& result_tx);
void fill_ado_version_based_onhardfork(currency::asset_descriptor_operation& asset_reg_info);
void fill_adb_version_based_onhardfork(currency::asset_descriptor_base& asset_base);
// Signing and auth
bool sign_buffer(const std::string& buff, crypto::signature& sig);
bool validate_sign(const std::string& buff, const crypto::signature& sig, const crypto::public_key& pkey);
@ -733,6 +763,9 @@ namespace tools
bool is_in_hardfork_zone(uint64_t hardfork_index) const;
//performance inefficient call, suitable only for rare ocasions or super lazy developers
bool proxy_to_daemon(const std::string& uri, const std::string& body, int& response_code, std::string& response_body);
void set_concise_mode(bool enabled) { m_concise_mode = enabled; }
void set_concise_mode_reorg_max_reorg_blocks(uint64_t max_blocks) { m_wallet_concise_mode_max_reorg_blocks = max_blocks; }
void set_concise_mode_truncate_history(uint64_t max_entries) { m_truncate_history_max_entries = max_entries; }
construct_tx_param get_default_construct_tx_param();
@ -767,7 +800,7 @@ private:
bool on_idle();
void unserialize_block_complete_entry(const currency::COMMAND_RPC_GET_BLOCKS_FAST::response& serialized,
currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& unserialized);
void pull_blocks(size_t& blocks_added, std::atomic<bool>& stop);
void pull_blocks(size_t& blocks_added, std::atomic<bool>& stop, bool& full_reset_needed);
bool prepare_free_transfers_cache(uint64_t fake_outputs_count);
bool select_transfers(assets_selection_context& needed_money_map, size_t fake_outputs_count, uint64_t dust, std::vector<uint64_t>& selected_indicies);
void add_transfers_to_transfers_cache(const std::vector<uint64_t>& indexs);
@ -788,7 +821,7 @@ private:
void add_to_last_zc_global_indexs(uint64_t h, uint64_t last_zc_output_index);
uint64_t get_actual_zc_global_index();
void handle_pulled_blocks(size_t& blocks_added, std::atomic<bool>& stop,
currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& blocks);
currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& blocks, bool& full_reset_needed);
std::string get_alias_for_address(const std::string& addr);
std::vector<std::string> get_aliases_for_address(const std::string& addr);
bool is_connected_to_net();
@ -836,7 +869,7 @@ private:
uint64_t get_tx_expiration_median() const;
void print_tx_sent_message(const currency::transaction& tx, const std::string& description, uint64_t fee = UINT64_MAX);
void print_tx_sent_message(const currency::transaction& tx, const std::string& description, bool broadcasted, uint64_t fee = UINT64_MAX);
// Validates escrow template tx in assumption it's related to wallet's account (wallet's account is either A or B party in escrow process)
bool validate_escrow_proposal(const wallet_public::wallet_transfer_info& wti, const bc_services::proposal_body& prop,
@ -935,7 +968,7 @@ private:
std::atomic<bool> m_stop;
std::shared_ptr<i_core_proxy> m_core_proxy;
std::shared_ptr<i_wallet2_callback> m_wcallback;
std::weak_ptr<i_wallet2_callback> m_wcallback;
currency::core_runtime_config m_core_runtime_config;
@ -951,8 +984,16 @@ private:
std::string m_votes_config_path;
tools::wallet_public::wallet_vote_config m_votes_config;
std::atomic<bool> m_concise_mode = true; //in this mode the wallet don't keep spent entries in m_transfers as well as m_recent_transfers longer then 100 entries
uint64_t m_last_known_daemon_height = 0;
uint64_t m_wallet_concise_mode_max_reorg_blocks = WALLET_CONCISE_MODE_MAX_REORG_BLOCKS;
uint64_t m_full_resync_requested_at_h = 0;
uint64_t m_truncate_history_max_entries
#ifdef MOBILE_WALLET_BUILD
= WALLET_CONCISE_MODE_MOBILE_MAX_HISTORY_SIZE;
#else
= 0;
#endif
//this needed to access wallets state in coretests, for creating abnormal blocks and tranmsactions
friend class test_generator;
}; // class wallet2
@ -1064,7 +1105,13 @@ namespace tools
if (tr_index != UINT64_MAX)
{
transfer_details& td = m_transfers[tr_index];
auto it_tr = m_transfers.find(tr_index);
if (it_tr == m_transfers.end())
{
throw tools::error::wallet_error_resync_needed();
}
transfer_details& td = it_tr->second;
ptc.total_balance_change[td.get_asset_id()] -= td.amount();
if (td.is_native_coin())
{
@ -1114,9 +1161,9 @@ namespace tools
ts_middle -= ts_middle % POS_SCAN_STEP;
uint64_t ts_window = std::min(ts_middle - ts_from, ts_to - ts_middle);
for (size_t transfer_index = 0; transfer_index != m_transfers.size(); transfer_index++)
for (auto it = m_transfers.begin(); it != m_transfers.end(); it++)//size_t transfer_index = 0; transfer_index != m_transfers.size(); transfer_index++)
{
auto& tr = m_transfers[transfer_index];
auto& tr = it->second;
uint64_t stake_unlock_time = 0;
if (!is_transfer_okay_for_pos(tr, cxt.zarcanum, stake_unlock_time))
@ -1142,7 +1189,7 @@ namespace tools
}
};
do_pos_mining_prepare_entry(cxt, transfer_index);
do_pos_mining_prepare_entry(cxt, tr);
cxt.total_items_checked++;
cxt.total_amount_checked += tr.amount();
while(step <= ts_window)
@ -1171,9 +1218,9 @@ namespace tools
return false;
cxt.iterations_processed++;
if (do_pos_mining_iteration(cxt, transfer_index, ts))
if (do_pos_mining_iteration(cxt, ts))
{
cxt.index = transfer_index;
cxt.index = it->first;
cxt.stake_unlock_time = stake_unlock_time;
cxt.status = API_RETURN_CODE_OK;
return true;

View file

@ -11,7 +11,6 @@
#include <boost/serialization/deque.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/extended_type_info.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/optional.hpp>
#include <atomic>
@ -52,6 +51,8 @@
#define WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER uint32_t(1 << 3)
#define WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION uint32_t(1 << 4) // transfer is reserved for cold-signing (unsigned tx was created and passed for signing)
#define WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM uint32_t(1 << 5) // for htlc keeps info if this htlc belong as redeem or as refund
#define WALLET_TRANSFER_DETAIL_CONCISE_MODE_PRESERVE uint32_t(1 << 6) // do not truncate this output with CONCISE mode
#define WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION uint32_t(1 << 7) // transfer is reserved for an ongoing asset operation with external signing
@ -224,9 +225,13 @@ namespace tools
bool shuffle = false;
bool create_utxo_defragmentation_tx = false;
bool need_at_least_1_zc = false;
//crypto::secret_key asset_deploy_control_key = currency::null_skey;
currency::thirdparty_sign_handler* pthirdparty_sign_handler = nullptr;
crypto::public_key ado_current_asset_owner = currency::null_pkey;
// misc
std::string tx_meaning_for_logs; // used to correctly log things, e.g. "escrow" or "asset emission".
uint32_t additional_transfer_flags_to_mark = 0;
//ado
bool ado_sign_thirdparty = false;
};
struct mode_separate_context
@ -356,7 +361,7 @@ namespace tools
uint64_t m_spent_height = 0;
uint32_t m_flags = 0;
uint64_t m_amount = 0;
boost::shared_ptr<ZC_out_info> m_zc_info_ptr;
std::shared_ptr<ZC_out_info> m_zc_info_ptr;
uint64_t amount() const { return m_amount; }
uint64_t amount_for_global_output_index() const { return is_zc() ? 0 : m_amount; } // amount value for global outputs index, it's zero for outputs with hidden amounts
@ -381,14 +386,16 @@ namespace tools
return true;
return false;
}
static inline uint64_t transfer_details_base_to_amount(const transfer_details_base& tdb)
static inline bool transfer_details_base_to_amount(const transfer_details_base& tdb, uint64_t& val)
{
return tdb.amount();
val = tdb.amount();
return true;
}
//----------------------------------------------------------------------------------------------------
static inline std::string transfer_details_base_to_tx_hash(const transfer_details_base& tdb)
static inline bool transfer_details_base_to_tx_hash(const transfer_details_base& tdb, std::string& val)
{
return epee::string_tools::pod_to_hex(currency::get_transaction_hash(tdb.m_ptx_wallet_info->m_tx));
val = epee::string_tools::pod_to_hex(currency::get_transaction_hash(tdb.m_ptx_wallet_info->m_tx));
return true;
}
@ -510,12 +517,14 @@ namespace tools
typedef std::unordered_multimap<std::string, payment_details> payment_container;
typedef std::deque<transfer_details> transfer_container;
typedef std::map<uint64_t, transfer_details> transfer_container; //typedef std::deque<transfer_details> transfer_container;
typedef std::unordered_map<crypto::hash, transfer_details_base> multisig_transfer_container;
typedef std::unordered_map<crypto::hash, tools::wallet_public::escrow_contract_details_basic> escrow_contracts_container;
typedef std::map<uint64_t, std::set<size_t> > free_amounts_cache_type;
typedef std::unordered_map<crypto::public_key, free_amounts_cache_type> free_assets_amounts_cache_type;
typedef std::unordered_map<std::pair<uint64_t, uint64_t>, uint64_t> amount_gindex_to_transfer_id_container; // maps [amount; gindex] -> tid
typedef std::unordered_map<crypto::hash, crypto::secret_key> tx_secrete_keys_container;
typedef std::unordered_map<crypto::hash, tools::wallet_public::wallet_transfer_info> unconfirmed_txs_container;
}// namespace tools

View file

@ -10,7 +10,6 @@
#include <boost/serialization/deque.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/extended_type_info.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <atomic>
#include <map>

View file

@ -30,3 +30,7 @@ struct wde_construct_tx_after_asset_ownership_proof_generated
currency::asset_operation_ownership_proof* pownership_proof;
};
struct wde_construct_tx_after_asset_ownership_eth_proof_generated
{
currency::asset_operation_ownership_proof_eth* pownership_proof_eth;
};

View file

@ -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
@ -321,6 +321,11 @@ namespace tools
const currency::transaction m_tx;
};
//----------------------------------------------------------------------------------------------------
struct wallet_error_resync_needed : public std::exception
{
virtual const char* what() const noexcept override { return "wallet_error_resync_needed"; }
};
//----------------------------------------------------------------------------------------------------
struct tx_parse_error : public refresh_error
{
explicit tx_parse_error(std::string&& loc, const currency::blobdata& tx_blob)
@ -350,7 +355,7 @@ namespace tools
//----------------------------------------------------------------------------------------------------
struct not_enough_money : public transfer_error
{
not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee, const crypto::public_key& asset_id, size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT)
not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee, const crypto::public_key& asset_id, uint8_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT)
: transfer_error(std::move(loc), "")
, m_available(availbable)
, m_tx_amount(tx_amount)
@ -381,12 +386,13 @@ namespace tools
uint64_t m_tx_amount;
uint64_t m_fee;
crypto::public_key m_asset_id;
size_t m_decimal_point;
uint8_t m_decimal_point;
};
struct no_zc_inputs : public transfer_error
{
no_zc_inputs(const std::string& /*v*/): transfer_error(std::string(""), API_RETURN_CODE_MISSING_ZC_INPUTS)
no_zc_inputs(std::string&& loc, const std::string&)
: transfer_error(std::move(loc), API_RETURN_CODE_MISSING_ZC_INPUTS)
{}
virtual const char* what() const noexcept
@ -566,34 +572,25 @@ namespace tools
//----------------------------------------------------------------------------------------------------
struct tx_too_big : public transfer_error
{
explicit tx_too_big(std::string&& loc, const currency::transaction& tx, uint64_t tx_size_limit)
: transfer_error(std::move(loc), "transaction is too big")
, m_tx(tx)
, m_tx_size_limit(tx_size_limit)
explicit tx_too_big(std::string&& loc, const std::string& message)
: transfer_error(std::move(loc), API_RETURN_CODE_TX_IS_TOO_BIG)
, m_message(message)
{
}
const currency::transaction& tx() const { return m_tx; }
uint64_t tx_size_limit() const { return m_tx_size_limit; }
const std::string get_message() const { return m_message; }
// TODO the following overrides need to be redesigned (seems to be necessary for API, consider writing API tests and then refactor this) -- sowle
std::string to_string() const
{
std::ostringstream ss;
ss << API_RETURN_CODE_TX_IS_TOO_BIG;
//currency::transaction tx = m_tx;
//ss << transfer_error::to_string() <<
// ", tx_size_limit = " << m_tx_size_limit <<
// ", tx size = " << get_object_blobsize(m_tx) <<
// ", tx:\n" << currency::obj_to_json_str(tx);
return ss.str();
return API_RETURN_CODE_TX_IS_TOO_BIG;
}
virtual const char* what() const noexcept
{
return API_RETURN_CODE_TX_IS_TOO_BIG;
}
private:
currency::transaction m_tx;
uint64_t m_tx_size_limit;
std::string m_message;
};
//----------------------------------------------------------------------------------------------------
struct zero_destination : public transfer_error
@ -668,8 +665,6 @@ namespace tools
};
//----------------------------------------------------------------------------------------------------
#if !defined(_MSC_VER)
template<typename TException, typename... TArgs>
void throw_wallet_ex(std::string&& loc, const TArgs&... args)
{
@ -677,31 +672,6 @@ namespace tools
LOG_PRINT_L0(e.to_string());
throw e;
}
#else
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
template<typename TException>
void throw_wallet_ex(std::string&& loc)
{
TException e(std::move(loc));
LOG_PRINT_L0(e.to_string());
throw e;
}
#define GEN_throw_wallet_ex(z, n, data) \
template<typename TException, BOOST_PP_ENUM_PARAMS(n, typename TArg)> \
void throw_wallet_ex(std::string&& loc, BOOST_PP_ENUM_BINARY_PARAMS(n, const TArg, &arg)) \
{ \
TException e(std::move(loc), BOOST_PP_ENUM_PARAMS(n, arg)); \
LOG_PRINT_L0(e.to_string()); \
throw e; \
}
BOOST_PP_REPEAT_FROM_TO(1, 6, GEN_throw_wallet_ex, ~)
#endif
}
}
@ -739,14 +709,14 @@ if (!(cond))
}
#define THROW_IF_FALSE_WALLET_EX_MES(cond, err_type, mess, ...) \
#define THROW_IF_FALSE_WALLET_EX_MES(cond, err_type, mes, ...) \
if (!(cond)) \
{ \
exception_handler(); \
std::stringstream ss; \
ss << std::endl << mess; \
LOG_ERROR(#cond << ". THROW EXCEPTION: " << #err_type); \
tools::error::throw_wallet_ex<err_type>(std::string(__FILE__ ":" STRINGIZE(__LINE__)) + ss.str(), ## __VA_ARGS__); \
ss << mes; \
LOG_ERROR(#cond << ". THROW EXCEPTION: " << #err_type << " : " << ss.str()); \
tools::error::throw_wallet_ex<err_type>(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ss.str(), ## __VA_ARGS__); \
}

View file

@ -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 Boolberry developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -29,31 +29,36 @@ struct i_wallet_to_i_backend_adapter: public tools::i_wallet2_callback
m_wallet_id(wallet_id)
{}
virtual void on_new_block(uint64_t height, const currency::block& block) {
virtual void on_new_block(uint64_t height, const currency::block& block) override
{
m_pbackend->on_new_block(m_wallet_id, height, block);
}
virtual void on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, const std::list<tools::wallet_public::asset_balance_entry>& balances, uint64_t total_mined) {
virtual void on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, const std::list<tools::wallet_public::asset_balance_entry>& balances, uint64_t total_mined) override
{
m_pbackend->on_transfer2(m_wallet_id, wti, balances, total_mined);
}
virtual void on_pos_block_found(const currency::block& wti) {
virtual void on_pos_block_found(const currency::block& wti) override
{
m_pbackend->on_pos_block_found(m_wallet_id, wti);
}
virtual void on_sync_progress(const uint64_t& progress) {
virtual void on_sync_progress(const uint64_t& progress) override
{
m_pbackend->on_sync_progress(m_wallet_id, progress);
}
virtual void on_transfer_canceled(const tools::wallet_public::wallet_transfer_info& wti) {
virtual void on_transfer_canceled(const tools::wallet_public::wallet_transfer_info& wti) override
{
m_pbackend->on_transfer_canceled(m_wallet_id, wti);
}
virtual void on_tor_status_change(const std::string& state)
virtual void on_tor_status_change(const std::string& state) override
{
m_pbackend->on_tor_status_change(m_wallet_id, state);
}
virtual void on_mw_get_wallets(std::vector<tools::wallet_public::wallet_entry_info>& wallets)
virtual void on_mw_get_wallets(std::vector<tools::wallet_public::wallet_entry_info>& wallets) override
{
m_pbackend->on_mw_get_wallets(wallets);
}
virtual bool on_mw_select_wallet(uint64_t wallet_id)
virtual bool on_mw_select_wallet(uint64_t wallet_id) override
{
return m_pbackend->on_mw_select_wallet(wallet_id);
}

View file

@ -13,7 +13,7 @@
#include "currency_core/offers_service_basics.h"
#include "currency_core/bc_escrow_service.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "currency_protocol/blobdatatype.h"
const uint64_t WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED = std::numeric_limits<uint64_t>::max();
@ -153,7 +153,6 @@ namespace wallet_public
std::vector<currency::tx_service_attachment> service_entries;
std::vector<std::string> remote_addresses; //optional
std::vector<std::string> remote_aliases; //optional, describe only if there only one remote address
std::vector<wallet_sub_transfer_info> subtransfers;
//not included in streaming serialization
@ -191,6 +190,8 @@ namespace wallet_public
KV_SERIALIZE(remote_addresses) DOC_DSCR("Remote addresses of this transfer(destination if it's outgoing transfer or sender if it's incoming transaction)") DOC_EXMP_AUTO(1, "ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END
KV_SERIALIZE(remote_aliases) DOC_DSCR("Aliases for remot addresses, of discovered") DOC_EXMP_AUTO(1, "roger") DOC_END
KV_SERIALIZE(subtransfers) DOC_DSCR("Essential part of transfer entry: amounts that been transfered in this transaction grouped by asset id") DOC_EXMP_AUTO(1) DOC_END
KV_SERIALIZE_EPHEMERAL_N(currency::asset_descriptor_operation, wallet_transfer_info_get_ado, "ado") DOC_DSCR("\"Asset Descriptor Operation\" if it was present in transaction") DOC_END
END_KV_SERIALIZE_MAP()
BEGIN_BOOST_SERIALIZATION()
@ -277,6 +278,13 @@ namespace wallet_public
subtransfers.back().is_income = true;
return subtransfers.back().amount;
}
static inline bool wallet_transfer_info_get_ado(const wallet_transfer_info& tdb, currency::asset_descriptor_operation& val)
{
if (currency::get_type_in_variant_container(tdb.tx.extra, val))
return true;
return false;
}
};
struct wallet_transfer_info_old : public wallet_transfer_info
@ -291,14 +299,16 @@ namespace wallet_public
KV_CHAIN_BASE(wallet_transfer_info)
END_KV_SERIALIZE_MAP()
static uint64_t wallet_transfer_info_to_amount(const wallet_transfer_info_old& wtio)
static bool wallet_transfer_info_to_amount(const wallet_transfer_info_old& wtio, uint64_t &val)
{
return wtio.get_native_amount();
val = wtio.get_native_amount();
return true;
}
static bool wallet_transfer_info_to_is_income(const wallet_transfer_info_old& wtio)
static bool wallet_transfer_info_to_is_income(const wallet_transfer_info_old& wtio, bool& val)
{
return wtio.get_native_is_income();
val = wtio.get_native_is_income();
return true;
}
};
@ -690,7 +700,7 @@ namespace wallet_public
KV_SERIALIZE(destinations) DOC_DSCR("List of destinations") DOC_EXMP_AUTO(1) DOC_END
KV_SERIALIZE(fee) DOC_DSCR("Fee to be paid on behalf of sender's wallet(paid in native coins)") DOC_EXMP_AUTO(10000000000) DOC_END
KV_SERIALIZE(mixin) DOC_DSCR("Specifies number of mixins(decoys) that would be used to create input, actual for pre-zarcanum outputs, for post-zarcanum outputs instead of this option, number that is defined by network hard rules(15+)") DOC_EXMP(15) DOC_END
KV_SERIALIZE(payment_id) DOC_DSCR("Hex-encoded payment_id, that normally used for user database by exchanges") DOC_EXMP_AUTO("1dfe5a88ff9effb3") DOC_END
KV_SERIALIZE(payment_id) DOC_DSCR("Hex-encoded payment_id, that normally used for user database by exchanges") DOC_EXMP_AUTO("") DOC_END
KV_SERIALIZE(comment) DOC_DSCR("Text comment that is displayed in UI") DOC_EXMP_AUTO("Thanks for the coffe") DOC_END
KV_SERIALIZE(push_payer) DOC_DSCR("Reveal information about sender of this transaction, basically add sender address to transaction in encrypted way, so only receiver can see who sent transaction") DOC_EXMP(false) DOC_END
KV_SERIALIZE(hide_receiver) DOC_DSCR("This add to transaction information about remote address(destination), might be needed when the wallet restored from seed phrase and fully resynched, if this option were true, then sender won't be able to see remote address for sent transactions anymore.") DOC_EXMP(true) DOC_END
@ -706,9 +716,9 @@ namespace wallet_public
uint64_t tx_size;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_hash) DOC_DSCR("Has of the generated transaction(if succeded)") DOC_EXMP("01220e8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a93") DOC_END
KV_SERIALIZE(tx_unsigned_hex)
KV_SERIALIZE(tx_size)
KV_SERIALIZE(tx_size) DOC_DSCR("Transaction size in bytes") DOC_EXMP(1234) DOC_END
END_KV_SERIALIZE_MAP()
};
};
@ -1002,92 +1012,6 @@ namespace wallet_public
END_KV_SERIALIZE_MAP()
};
struct COMMAND_RPC_MAKETELEPOD
{
struct request
{
uint64_t amount;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string status; //"OK", "INSUFFICIENT_COINS", "INTERNAL_ERROR"
telepod tpd;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(tpd)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_TELEPODSTATUS
{
struct request
{
telepod tpd;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tpd)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string status; //"OK", "UNCONFIRMED", "BAD", "SPENT", "INTERNAL_ERROR"
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_CLONETELEPOD
{
struct request
{
telepod tpd;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tpd)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string status;//"OK", "UNCONFIRMED", "BAD", "SPENT", "INTERNAL_ERROR:"
telepod tpd;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(tpd)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_WITHDRAWTELEPOD
{
struct request
{
telepod tpd;
std::string addr;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tpd)
KV_SERIALIZE(addr)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string status; //"OK", "UNCONFIRMED", "BAD", "SPENT", "INTERNAL_ERROR", "BAD_ADDRESS"
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
};
struct create_proposal_param
{
// uint64_t wallet_id;
@ -1982,31 +1906,49 @@ namespace wallet_public
struct COMMAND_ASSETS_DEPLOY
{
DOC_COMMAND("Deploy new asset in the system.");
struct request
{
std::list<transfer_destination> destinations;
currency::asset_descriptor_base asset_descriptor;
bool do_not_split_destinations = false;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in the destinations is irreleant and can be omitted.") DOC_EXMP_AUTO(1) DOC_END
KV_SERIALIZE(asset_descriptor) DOC_DSCR("Descriptor that holds all information about asset - ticker, emission, description etc") DOC_END
KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in the destinations is irreleant and can be omitted.") DOC_EXMP_AUTO(1) DOC_END
KV_SERIALIZE(asset_descriptor) DOC_DSCR("Descriptor that holds all information about asset - ticker, emission, description etc") DOC_END
KV_SERIALIZE(do_not_split_destinations) DOC_DSCR("If true, the provided destinations will be used as-is and won't be splitted (or altered) to avoid common issues. Default is false.") DOC_EXMP(false) DOC_END
END_KV_SERIALIZE_MAP()
};
struct response
{
crypto::hash result_tx;
crypto::hash tx_id;
crypto::public_key new_asset_id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_POD_AS_HEX_STRING(result_tx) DOC_DSCR("Id of transaction that carries asset registration command, asset would be registered as soon as transaction got confirmed") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Id of transaction that carries asset registration command, asset would be registered as soon as transaction got confirmed") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(new_asset_id) DOC_DSCR("Issued asset id") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END
END_KV_SERIALIZE_MAP()
};
};
// contains data for external checking & signing asset-emitting/-updating transaction by a third-party
struct data_for_external_asset_signing_tx
{
currency::blobdata unsigned_tx;
crypto::secret_key tx_secret_key;
std::vector<std::string> outputs_addresses;
currency::blobdata finalized_tx;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_BLOB_AS_BASE64_STRING(unsigned_tx) DOC_DSCR("Base64-encoded unsigned transaction blob.") 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("Target address for each of the transaction zoutput.") DOC_EXMP_AGGR("ZxDNaMeZjwCjnHuU5gUNyrP1pM3U5vckbakzzV6dEHyDYeCpW8XGLBFTshcaY8LkG9RQn7FsQx8w2JeJzJwPwuDm2NfixPAXf", "ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END
KV_SERIALIZE_BLOB_AS_BASE64_STRING(finalized_tx)DOC_DSCR("Base64-encoded finalized_tx data structure, which should be passed along with submitting the transaction.") DOC_EXMP("ewogICJ2ZXJzaW9uIjogMSwgC....iAgInZpbiI6IFsgewogICAgIC") DOC_END
END_KV_SERIALIZE_MAP()
};
struct COMMAND_ASSETS_EMIT
{
DOC_COMMAND("Emmit new coins of the the asset, that is controlled by this wallet.");
@ -2015,20 +1957,23 @@ namespace wallet_public
{
crypto::public_key asset_id;
std::list<transfer_destination> destinations;
bool do_not_split_destinations = false;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Id of the asset to emit more coins") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END
KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in the destinations is irreleant and can be omitted.") DOC_EXMP_AUTO(1) DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Id of the asset to emit more coins") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END
KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in the destinations is irreleant and can be omitted.") DOC_EXMP_AUTO(1) DOC_END
KV_SERIALIZE(do_not_split_destinations) DOC_DSCR("If true, the provided destinations will be used as-is and won't be splitted (or altered) to avoid common issues. Default is false.") DOC_EXMP(false) DOC_END
END_KV_SERIALIZE_MAP()
};
struct response
{
crypto::hash result_tx;
crypto::hash tx_id;
std::optional<data_for_external_asset_signing_tx> data_for_external_signing;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_POD_AS_HEX_STRING(result_tx) DOC_DSCR("Id of transaction that carries asset registration command, asset would be registered as soon as transaction got confirmed") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Id of transaction that emits the required asset.") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE(data_for_external_signing) DOC_DSCR("[optional] Additional data for external asset tx signing.") DOC_EXMP_AGGR() DOC_END
END_KV_SERIALIZE_MAP()
};
};
@ -2050,10 +1995,12 @@ namespace wallet_public
struct response
{
crypto::hash result_tx;
crypto::hash tx_id;
std::optional<data_for_external_asset_signing_tx> data_for_external_signing;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_POD_AS_HEX_STRING(result_tx) DOC_DSCR("Id of transaction that carries asset registration command, asset would be registered as soon as transaction got confirmed") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Id of transaction that carries asset registration command, asset would be registered as soon as transaction got confirmed") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE(data_for_external_signing) DOC_DSCR("[optional] Hex-encoded transaction for external signing. ") DOC_EXMP_AGGR() DOC_END
END_KV_SERIALIZE_MAP()
};
};
@ -2064,24 +2011,65 @@ namespace wallet_public
struct request
{
crypto::public_key asset_id;
uint64_t burn_amount;
crypto::public_key asset_id = currency::null_pkey;
uint64_t burn_amount = 0;
//optional params
std::string point_tx_to_address;
uint64_t native_amount = 0;
std::vector<currency::tx_service_attachment> service_entries;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Id of the asset to burn") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END
KV_SERIALIZE(burn_amount) DOC_DSCR("Amount to burn") DOC_EXMP(10000000) DOC_END
KV_SERIALIZE(point_tx_to_address) DOC_DSCR("Optional, if we need this transaction to be seen by particular wallet") DOC_EXMP("ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END
KV_SERIALIZE(native_amount) DOC_DSCR("Optional, if we need this transaction to be seen by particular wallet") DOC_EXMP(0) DOC_END
KV_SERIALIZE(service_entries) DOC_DSCR("Optional, if we need to include service entries for burn transaction") DOC_EXMP_AUTO(1) DOC_END
END_KV_SERIALIZE_MAP()
};
struct response
{
crypto::hash result_tx;
crypto::hash tx_id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_POD_AS_HEX_STRING(result_tx) DOC_DSCR("Id of transaction that carries asset burn operation") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Id of transaction that carries asset burn operation") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_ASSET_SEND_EXT_SIGNED_TX
{
DOC_COMMAND("Inserts externally made asset ownership signature into the given transaction and broadcasts it.");
struct request
{
currency::blobdata finalized_tx;
currency::blobdata unsigned_tx;
crypto::eth_signature eth_sig; //TODO: add value initialization here
crypto::hash expected_tx_id = currency::null_hash;
bool unlock_transfers_on_fail = false;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_BLOB_AS_BASE64_STRING(finalized_tx)DOC_DSCR("Base64-encoded finalized_tx data structure, which was received from emit_asset call.") DOC_EXMP("ewogICJ2ZXJzaW9uIjogMSwgC....iAgInZpbiI6IFsgewogICAgIC") DOC_END
KV_SERIALIZE_BLOB_AS_BASE64_STRING(unsigned_tx) DOC_DSCR("Base64-encoded unsigned transaction blob, which was received from emit_asset call.") DOC_EXMP("083737bcfd826a973f74bb56a52b4fa562e6579ccaadd2697463498a66de4f1760b2cd40f11c3a00a7a80000") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(eth_sig) DOC_DSCR("HEX-encoded ETH signature (64 bytes)") DOC_EXMP("674bb56a5b4fa562e679ccacc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6add697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(expected_tx_id) DOC_DSCR("The expected transaction id. Tx won't be sent if the calculated one doesn't match this one. Consider using 'verified_tx_id' returned by 'decrypt_tx_details' call.") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END
KV_SERIALIZE(unlock_transfers_on_fail) DOC_DSCR("If true, all locked wallet transfers, corresponding to the transaction, will be unlocked on sending failure. False by default.") DOC_EXMP(false) DOC_END
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string status;
bool transfers_were_unlocked = false;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status) DOC_DSCR("Status of the call") DOC_EXMP("OK") DOC_END
KV_SERIALIZE(transfers_were_unlocked) DOC_DSCR("If true, all input transfers that were locked when preparing this transaction, are now unlocked and may be spent. Can be true only upon sending failure and if requested.") DOC_EXMP(false) DOC_END
END_KV_SERIALIZE_MAP()
};
};
} // namespace wallet_rpc
} // namespace tools

View file

@ -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
@ -212,7 +212,7 @@ namespace tools
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(m_port, m_bind_ip);
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::auth_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context)
bool wallet_rpc_server::auth_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& conn_context)
{
auto it = std::find_if(query_info.m_header_info.m_etc_fields.begin(), query_info.m_header_info.m_etc_fields.end(), [](const auto& element)
@ -252,7 +252,7 @@ namespace tools
m_jwt_used_salts.add(salt, ticks_now + JWT_TOKEN_EXPIRATION_MAXIMUM);
m_jwt_used_salts.remove_if_expiration_less_than(ticks_now);
LOG_PRINT_L0("JWT token OK");
LOG_PRINT_L3("JWT token OK");
return true;
}
catch(const std::exception& e)
@ -266,12 +266,13 @@ namespace tools
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context)
bool wallet_rpc_server::handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, epee::net_utils::connection_context_base& conn_context, bool& call_found, documentation& docs)
{
if (m_jwt_secret.size() && m_conn_context.m_connection_id != RPC_INTERNAL_UI_CONTEXT)
if (m_jwt_secret.size() && conn_context.m_connection_id != RPC_INTERNAL_UI_CONTEXT)
{
if (!auth_http_request(query_info, response, m_conn_context))
if (!auth_http_request(query_info, response, conn_context))
{
call_found = true;
response.m_response_code = 401;
response.m_response_comment = "Unauthorized";
return true;
@ -281,14 +282,13 @@ namespace tools
response.m_response_code = 200;
response.m_response_comment = "Ok";
std::string reference_stub;
bool call_found = false;
if (m_deaf)
{
response.m_response_code = 500;
response.m_response_comment = "Internal Server Error";
return true;
}
if (!handle_http_request_map(query_info, response, m_conn_context, call_found) && response.m_response_code == 200)
if (!handle_http_request_map(query_info, response, conn_context, call_found, docs) && response.m_response_code == 200)
{
response.m_response_code = 500;
response.m_response_comment = "Internal Server Error";
@ -1260,13 +1260,46 @@ namespace tools
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::rpc_destinations_to_currency_destination(const std::list<wallet_public::transfer_destination>& rpc_destinations, std::vector<currency::tx_destination_entry>& currency_destinations)
#define DESTINATIONS_COUNT_FOR_DEPLOY_OR_EMIT 10
static_assert(DESTINATIONS_COUNT_FOR_DEPLOY_OR_EMIT >= CURRENCY_TX_MIN_ALLOWED_OUTS, "DESTINATIONS_COUNT_FOR_DEPLOY_OR_EMIT must be >= min allowed tx outs");
void wallet_rpc_server::rpc_destinations_to_currency_destinations(const std::list<wallet_public::transfer_destination>& rpc_destinations, bool nullify_asset_id, bool try_to_split, std::vector<currency::tx_destination_entry>& result_destinations)
{
GET_WALLET();
std::vector<currency::tx_destination_entry>& dsts = currency_destinations;
for (auto it = rpc_destinations.begin(); it != rpc_destinations.end(); it++)
WLT_THROW_IF_FALSE_WITH_CODE(!rpc_destinations.empty(), "WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT", "WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT");
std::list<wallet_public::transfer_destination> local_destinations;
if (nullify_asset_id && try_to_split)
{
currency::tx_destination_entry de;
bool do_split = true;
uint64_t total_amount = rpc_destinations.front().amount;
std::string first_address = rpc_destinations.front().address;
for(auto it = std::next(rpc_destinations.begin()); it != rpc_destinations.end(); ++it)
{
total_amount += it->amount;
if (first_address != it->address)
{
do_split = false;
break;
}
}
if (do_split)
{
const uint64_t el_amount = total_amount / DESTINATIONS_COUNT_FOR_DEPLOY_OR_EMIT; // approximation, see below
wallet_public::transfer_destination td{};
td.address = first_address;
td.amount = el_amount;
for(size_t i = 0; i < DESTINATIONS_COUNT_FOR_DEPLOY_OR_EMIT - 1; ++i)
local_destinations.push_back(td);
td.amount = total_amount - (DESTINATIONS_COUNT_FOR_DEPLOY_OR_EMIT - 1) * el_amount; // the last element must account for division error
local_destinations.push_back(td);
}
}
const std::list<wallet_public::transfer_destination>& destinations = local_destinations.size() != 0 ? local_destinations : rpc_destinations;
for (auto it = destinations.begin(); it != destinations.end(); ++it)
{
currency::tx_destination_entry de{};
de.addr.resize(1);
std::string embedded_payment_id;
//check if address looks like wrapped address
@ -1274,66 +1307,140 @@ namespace tools
WLT_THROW_IF_FALSE_WITH_CODE(w.get_wallet()->get_transfer_address(it->address, de.addr.back(), embedded_payment_id), "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS", "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS");
WLT_THROW_IF_FALSE_WITH_CODE(embedded_payment_id.size() == 0, "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS", "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS");
de.amount = it->amount;
de.asset_id = it->asset_id;
dsts.push_back(de);
de.asset_id = nullify_asset_id ? currency::null_pkey : it->asset_id;
result_destinations.push_back(de);
}
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_assets_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx)
bool wallet_rpc_server::on_asset_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
currency::transaction result_tx;
std::vector<currency::tx_destination_entry> currency_destinations;
rpc_destinations_to_currency_destination(req.destinations, currency_destinations);
//fix for default asset_id
for (auto& d : currency_destinations)
if (!currency::validate_asset_ticker_and_full_name(req.asset_descriptor))
{
d.asset_id = currency::null_pkey;
er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT;
er.message = "asset ticker or full_name is invalid";
return false;
}
w.get_wallet()->deploy_new_asset(req.asset_descriptor, currency_destinations, result_tx, res.new_asset_id);
res.result_tx = currency::get_transaction_hash(result_tx);
std::vector<currency::tx_destination_entry> currency_destinations;
rpc_destinations_to_currency_destinations(req.destinations, true, !req.do_not_split_destinations, currency_destinations);
currency::finalized_tx ft{};
w.get_wallet()->deploy_new_asset(req.asset_descriptor, currency_destinations, ft, res.new_asset_id);
res.tx_id = ft.tx_id;
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_assets_emit(const wallet_public::COMMAND_ASSETS_EMIT::request& req, wallet_public::COMMAND_ASSETS_EMIT::response& res, epee::json_rpc::error& er, connection_context& cntx)
bool wallet_rpc_server::on_asset_emit(const wallet_public::COMMAND_ASSETS_EMIT::request& req, wallet_public::COMMAND_ASSETS_EMIT::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
currency::transaction result_tx;
currency::asset_descriptor_base last_adb{};
CHECK_AND_ASSERT_THROW_MES(w.get_wallet()->daemon_get_asset_info(req.asset_id, last_adb), "unknown asset_id"); // TODO: bad design, consider refactoring -- sowle
std::vector<currency::tx_destination_entry> currency_destinations;
rpc_destinations_to_currency_destination(req.destinations, currency_destinations);
//fix for default asset_id
for (auto& d : currency_destinations)
rpc_destinations_to_currency_destinations(req.destinations, true, !req.do_not_split_destinations, currency_destinations);
currency::finalized_tx ft{};
w.get_wallet()->emit_asset(req.asset_id, currency_destinations, ft);
res.tx_id = ft.tx_id;
if (last_adb.owner_eth_pub_key.has_value())
{
d.asset_id = currency::null_pkey;
// include additonal info into response, if it's an external signing asset operation
wallet_public::data_for_external_asset_signing_tx data{};
data.unsigned_tx = t_serializable_object_to_blob(ft.tx);
data.tx_secret_key = ft.one_time_key;
std::vector<std::string>& outs_addr = data.outputs_addresses;
for(auto d : ft.ftp.prepared_destinations)
outs_addr.push_back(currency::get_account_address_as_str(d.addr.back()));
data.finalized_tx = t_serializable_object_to_blob(ft);
res.data_for_external_signing = data;
}
w.get_wallet()->emit_asset(req.asset_id, currency_destinations, result_tx);
res.result_tx = currency::get_transaction_hash(result_tx);
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_assets_update(const wallet_public::COMMAND_ASSETS_UPDATE::request& req, wallet_public::COMMAND_ASSETS_UPDATE::response& res, epee::json_rpc::error& er, connection_context& cntx)
bool wallet_rpc_server::on_asset_update(const wallet_public::COMMAND_ASSETS_UPDATE::request& req, wallet_public::COMMAND_ASSETS_UPDATE::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
currency::transaction result_tx;
w.get_wallet()->update_asset(req.asset_id, req.asset_descriptor, result_tx);
res.result_tx = currency::get_transaction_hash(result_tx);
return true;
currency::finalized_tx ft{};
w.get_wallet()->update_asset(req.asset_id, req.asset_descriptor, ft);
res.tx_id = ft.tx_id;
if (req.asset_descriptor.owner_eth_pub_key.has_value())
{
// include additonal info into response, if it's an external signing asset operation
wallet_public::data_for_external_asset_signing_tx data{};
data.unsigned_tx = t_serializable_object_to_blob(ft.tx);
data.tx_secret_key = ft.one_time_key;
std::vector<std::string>& outs_addr = data.outputs_addresses;
for(auto d : ft.ftp.prepared_destinations)
outs_addr.push_back(currency::get_account_address_as_str(d.addr.back()));
data.finalized_tx = t_serializable_object_to_blob(ft);
res.data_for_external_signing = data;
}
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_assets_burn(const wallet_public::COMMAND_ASSETS_BURN::request& req, wallet_public::COMMAND_ASSETS_BURN::response& res, epee::json_rpc::error& er, connection_context& cntx)
bool wallet_rpc_server::on_asset_burn(const wallet_public::COMMAND_ASSETS_BURN::request& req, wallet_public::COMMAND_ASSETS_BURN::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
currency::transaction result_tx;
w.get_wallet()->burn_asset(req.asset_id, req.burn_amount, result_tx);
res.result_tx = currency::get_transaction_hash(result_tx);
currency::finalized_tx ft{};
w.get_wallet()->burn_asset(req.asset_id, req.burn_amount, ft, req.service_entries, req.point_tx_to_address, req.native_amount);
res.tx_id = ft.tx_id;
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_asset_send_ext_signed_tx(const wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request& req, wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
currency::finalized_tx ft{};
if (!t_unserializable_object_from_blob(ft, req.finalized_tx))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT;
er.message = "finalized_tx couldn't be deserialized";
return false;
}
if (t_serializable_object_to_blob(ft.tx) != req.unsigned_tx)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT;
er.message = "unsigned_tx doesn't match finalized_tx";
return false;
}
crypto::hash tx_id = currency::get_transaction_hash(ft.tx);
if (req.expected_tx_id != tx_id)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT;
er.message = std::string("expected_tx_id mismatch, real tx id is ") + epee::string_tools::pod_to_hex(tx_id);
return false;
}
try
{
currency::transaction result_tx{};
w.get_wallet()->submit_externally_signed_asset_tx(ft, req.eth_sig, req.unlock_transfers_on_fail, result_tx, res.transfers_were_unlocked);
}
catch(std::exception& e)
{
// doing this to be able to return 'transfers_were_unlocked' to the caller even in the case of exception
res.status = e.what();
return true;
}
res.status = API_RETURN_CODE_OK;
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
@ -1341,7 +1448,7 @@ namespace tools
bool wallet_rpc_server::on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
i_wallet2_callback* pcallback = w.get_wallet()->get_callback();
std::shared_ptr<i_wallet2_callback> pcallback = w.get_wallet()->get_callback();
if (!pcallback)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@ -1356,7 +1463,7 @@ namespace tools
bool wallet_rpc_server::on_mw_select_wallet(const wallet_public::COMMAND_MW_SELECT_WALLET::request& req, wallet_public::COMMAND_MW_SELECT_WALLET::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
i_wallet2_callback* pcallback = w.get_wallet()->get_callback();
std::shared_ptr<i_wallet2_callback> pcallback = w.get_wallet()->get_callback();
if (!pcallback)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;

View file

@ -57,7 +57,9 @@ namespace tools
{
wallet_provider_simple(std::shared_ptr<wallet2> wallet_ptr) : m_wallet_ptr(wallet_ptr)
{}
virtual std::shared_ptr<wallet2> get_wallet()
// interface i_wallet_provider
virtual std::shared_ptr<wallet2> get_wallet() override
{
return m_wallet_ptr;
}
@ -88,7 +90,18 @@ namespace tools
static void init_options(boost::program_options::options_description& desc);
bool init(const boost::program_options::variables_map& vm);
bool run(bool do_mint, bool offline_mode, const currency::account_public_address& miner_address);
bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context);
virtual bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response_info,
connection_context& conn_context)
{
bool call_found = false;
return this->handle_http_request(query_info, response_info, conn_context, call_found, epee::net_utils::http::i_chain_handler::m_empty_documentation);
}
// interface i_chain_handler
virtual bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response_info,
epee::net_utils::connection_context_base& conn_context, bool& call_found, documentation& docs = epee::net_utils::http::i_chain_handler::m_empty_documentation) override;
void set_jwt_secret(const std::string& jwt);
const std::string& get_jwt_secret();
@ -146,11 +159,11 @@ namespace tools
MAP_JON_RPC_WE("assets_whitelist_add", on_assets_whitelist_add, wallet_public::COMMAND_ASSETS_WHITELIST_ADD)
MAP_JON_RPC_WE("assets_whitelist_remove", on_assets_whitelist_remove, wallet_public::COMMAND_ASSETS_WHITELIST_REMOVE)
MAP_JON_RPC_WE("deploy_asset", on_assets_deploy, wallet_public::COMMAND_ASSETS_DEPLOY)
MAP_JON_RPC_WE("emit_asset", on_assets_emit, wallet_public::COMMAND_ASSETS_EMIT)
MAP_JON_RPC_WE("update_asset", on_assets_update, wallet_public::COMMAND_ASSETS_UPDATE)
MAP_JON_RPC_WE("burn_asset", on_assets_burn, wallet_public::COMMAND_ASSETS_BURN)
MAP_JON_RPC_WE("deploy_asset", on_asset_deploy, wallet_public::COMMAND_ASSETS_DEPLOY)
MAP_JON_RPC_WE("emit_asset", on_asset_emit, wallet_public::COMMAND_ASSETS_EMIT)
MAP_JON_RPC_WE("update_asset", on_asset_update, wallet_public::COMMAND_ASSETS_UPDATE)
MAP_JON_RPC_WE("burn_asset", on_asset_burn, wallet_public::COMMAND_ASSETS_BURN)
MAP_JON_RPC_WE("send_ext_signed_asset_tx", on_asset_send_ext_signed_tx, wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX)
//MULTIWALLET APIs
MAP_JON_RPC_WE("mw_get_wallets", on_mw_get_wallets, wallet_public::COMMAND_MW_GET_WALLETS)
@ -218,10 +231,11 @@ namespace tools
bool on_assets_whitelist_add(const wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request& req, wallet_public::COMMAND_ASSETS_WHITELIST_ADD::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_assets_whitelist_remove(const wallet_public::COMMAND_ASSETS_WHITELIST_REMOVE::request& req, wallet_public::COMMAND_ASSETS_WHITELIST_REMOVE::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_assets_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_assets_emit(const wallet_public::COMMAND_ASSETS_EMIT::request& req, wallet_public::COMMAND_ASSETS_EMIT::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_assets_update(const wallet_public::COMMAND_ASSETS_UPDATE::request& req, wallet_public::COMMAND_ASSETS_UPDATE::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_assets_burn(const wallet_public::COMMAND_ASSETS_BURN::request& req, wallet_public::COMMAND_ASSETS_BURN::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_asset_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_asset_emit(const wallet_public::COMMAND_ASSETS_EMIT::request& req, wallet_public::COMMAND_ASSETS_EMIT::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_asset_update(const wallet_public::COMMAND_ASSETS_UPDATE::request& req, wallet_public::COMMAND_ASSETS_UPDATE::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_asset_burn(const wallet_public::COMMAND_ASSETS_BURN::request& req, wallet_public::COMMAND_ASSETS_BURN::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_asset_send_ext_signed_tx(const wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request& req, wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_mw_select_wallet(const wallet_public::COMMAND_MW_SELECT_WALLET::request& req, wallet_public::COMMAND_MW_SELECT_WALLET::response& res, epee::json_rpc::error& er, connection_context& cntx);
@ -238,7 +252,8 @@ namespace tools
//bool reset_active_wallet(std::shared_ptr<wallet2> w);
bool handle_command_line(const boost::program_options::variables_map& vm);
void rpc_destinations_to_currency_destination(const std::list<wallet_public::transfer_destination>& rpc_destinations, std::vector<currency::tx_destination_entry>& currency_destinations);
void rpc_destinations_to_currency_destinations(const std::list<wallet_public::transfer_destination>& rpc_destinations, bool nullify_asset_id, bool try_to_split, std::vector<currency::tx_destination_entry>& currency_destinations);
private:
std::shared_ptr<i_wallet_provider> m_pwallet_provider_sh_ptr;

View file

@ -7,10 +7,11 @@
#pragma once
#define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR -1
#define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS -2
#define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY -3
#define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR -4
#define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID -5
#define WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT -6
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY -7
#define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR -1
#define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS -2
#define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY -3
#define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR -4
#define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID -5
#define WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT -6
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY -7
#define WALLET_RPC_ERROR_CODE_WRONG_MIXINS_FOR_AUDITABLE_WALLET -8

View file

@ -395,7 +395,13 @@ bool wallets_manager::start()
CATCH_ENTRY_L0("main", false);
}
std::string wallets_manager::set_remote_node_url(const std::string& url)
{
if (m_rpc_proxy)
m_rpc_proxy->set_connection_addr(url);
return API_RETURN_CODE_OK;
}
bool wallets_manager::stop()
{
@ -1045,7 +1051,8 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st
w->set_use_deffered_global_outputs(m_use_deffered_global_outputs);
owr.wallet_id = m_wallet_id_counter++;
w->callback(std::shared_ptr<tools::i_wallet2_callback>(new i_wallet_to_i_backend_adapter(this, owr.wallet_id)));
std::shared_ptr<tools::i_wallet2_callback> w_cb{new i_wallet_to_i_backend_adapter(this, owr.wallet_id)};
w->callback(w_cb);
if (m_remote_node_mode)
{
w->set_core_proxy(m_rpc_proxy);
@ -1106,6 +1113,7 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st
EXCLUSIVE_CRITICAL_REGION_LOCAL(m_wallets_lock);
wallet_vs_options& wo = m_wallets[owr.wallet_id];
**wo.w = w;
wo.w_cb = w_cb;
owr.wallet_file_size = w->get_wallet_file_size();
get_wallet_info(wo, owr.wi);
init_wallet_entry(wo, owr.wallet_id);
@ -1921,7 +1929,10 @@ std::string wallets_manager::stop_pos_mining(uint64_t wallet_id)
std::string wallets_manager::run_wallet(uint64_t wallet_id)
{
GET_WALLET_OPT_BY_ID(wallet_id, wo);
wo.miner_thread = std::thread(boost::bind(&wallets_manager::wallet_vs_options::worker_func, &wo));
if (!wo.major_stop && !wo.miner_thread.joinable())
{
wo.miner_thread = std::thread(boost::bind(&wallets_manager::wallet_vs_options::worker_func, &wo));
}
return API_RETURN_CODE_OK;
}

View file

@ -55,6 +55,7 @@ public:
{
currency::core_runtime_config core_conf;
epee::locked_object<std::shared_ptr<tools::wallet2>, wallet_lock_time_watching_policy> w;
std::shared_ptr<tools::i_wallet2_callback> w_cb; // not using locked_object here, cuz w_cb is accessed only via it's wallet -- sowle
typedef epee::locked_object<std::shared_ptr<tools::wallet2>, wallet_lock_time_watching_policy>::lock_shared_ptr wallet_lock_object;
std::shared_ptr<tools::wallet_rpc_server> rpc_wrapper; //500 bytes of extra data, we can afford it, to have rpc-like invoke map
std::atomic<bool> do_mining;
@ -147,6 +148,7 @@ public:
std::string get_tx_pool_info(currency::COMMAND_RPC_GET_POOL_INFO::response& res);
std::string export_wallet_history(const view::export_wallet_info& ewi);
std::string setup_wallet_rpc(const std::string& jwt_secret);
std::string set_remote_node_url(const std::string& url);
#ifndef MOBILE_WALLET_BUILD
currency::core_rpc_server& get_rpc_server() { return m_rpc_server; }
@ -206,10 +208,10 @@ private:
virtual bool on_mw_select_wallet(uint64_t wallet_id) override;
//----- i_wallet_provider ------
virtual void lock();
virtual void unlock();
virtual void lock() override;
virtual void unlock() override;
//#ifndef MOBILE_WALLET_BUILD
virtual std::shared_ptr<tools::wallet2> get_wallet();
virtual std::shared_ptr<tools::wallet2> get_wallet() override;
//#endif
//--------

View file

@ -52,7 +52,7 @@ bool atomic_base_test::generate(std::vector<test_event_entry>& events) const
test_core_time::adjust(m_genesis_timestamp);
epee::debug::get_set_enable_assert(true, true);
//epee::debug::get_set_enable_assert(true, true);
currency::account_base genesis_acc;
genesis_acc.generate();
@ -68,7 +68,7 @@ bool atomic_base_test::generate(std::vector<test_event_entry>& events) const
REWIND_BLOCKS_N(events, blk_0r, blk_0, m_mining_accunt, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5);
DO_CALLBACK(events, "c1");
epee::debug::get_set_enable_assert(true, false);
//epee::debug::get_set_enable_assert(true, false);
return true;
}
@ -78,8 +78,8 @@ bool atomic_base_test::generate(std::vector<test_event_entry>& events) const
bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
epee::debug::get_set_enable_assert(true, true);
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){epee::debug::get_set_enable_assert(true, false); });
//epee::debug::get_set_enable_assert(true, true);
//misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){epee::debug::get_set_enable_assert(true, false); });
/*

View file

@ -817,3 +817,241 @@ bool chain_switching_when_out_spent_in_alt_chain_ref_id::generate(std::vector<te
return true;
}
alt_chain_and_block_tx_fee_median::alt_chain_and_block_tx_fee_median()
{
REGISTER_CALLBACK_METHOD(alt_chain_and_block_tx_fee_median, check_after_hf4);
REGISTER_CALLBACK_METHOD(alt_chain_and_block_tx_fee_median, check_before_hf4);
}
bool alt_chain_and_block_tx_fee_median::generate(
std::vector<test_event_entry>& events) const
{
/* Test idea: check chain switching rules.
Rules before and after HF4 for PoW blocks are different. There're only PoW
blocks in the test situation. If the last blocks contain transactions (non
empty blocks), then the chain with the largest this_block_tx_fee_median on its
head becomes the main.
0 10 11 21 22
(0 ) - ... - (0r) - (1 ) - ... - (1r) - (2 )
| main | \
| | [tx_0]
| |
| | main
\ - (1a) \ - (2a)
\
[tx_1]
Chain with head blk_1 versus chain with head blk_1a: chain with head blk_1
is the main, because blocks 1, 1a are empty.
Chain with head blk_2 versus chain with head blk_2a: chain with head blk_2a
is the main, because blocks 2, 2a aren't empty and the fee of tx_1 is larger
than the fee of tx_0.
*/
bool success{};
bool hf4_active{};
std::vector<tx_source_entry> sources{};
std::vector<tx_destination_entry> destinations{};
transaction tx_0{}, tx_1{};
uint64_t tx_version{};
crypto::hash top_block{};
GENERATE_ACCOUNT(miner);
MAKE_GENESIS_BLOCK(events,
blk_0,
miner,
test_core_time::get_time());
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner,
CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner);
MAKE_NEXT_BLOCK(events, blk_1a, blk_0r, miner);
/* It is decided which chain will be the main: with the head blk_1 or with the
head blk_1a.
0 10 11
/ - (1 )
|
(0 ) - ... - (0r)
|
\ - (1a)
*/
CHECK_AND_ASSERT_EQ(is_pos_block(blk_1), false);
CHECK_AND_ASSERT_EQ(is_pos_block(blk_1a), false);
CHECK_AND_ASSERT_EQ(get_block_height(blk_1), 11);
CHECK_AND_ASSERT_EQ(get_block_height(blk_1), get_block_height(blk_1a));
/* Blocks blk_1, blk_1a do not contain transactions (they are empty blocks).
Switching to the alternative chain with head blk_1a will not occur. The main
chain is the chain with the head blk_1. */
DO_CALLBACK_PARAMS(events,
"check_top_block",
params_top_block(get_block_height(blk_1),
get_block_hash(blk_1)));
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner,
CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// Transaction tx_0 is constructed and placed in block blk_2.
success = fill_tx_sources_and_destinations(
events,
/* head = */ blk_1r,
/* from = */ miner.get_keys(),
/* to = */ miner.get_public_address(),
/* amount = */ MK_TEST_COINS(10),
/* fee = */ m_fee_tx_0_blk_2,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
tx_version = get_tx_version(get_block_height(blk_1r),
m_hardforks);
success = construct_tx(miner.get_keys(),
sources,
destinations,
empty_attachment,
tx_0,
tx_version,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_0");
ADD_CUSTOM_EVENT(events, tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner, tx_0);
sources.clear();
destinations.clear();
// Transaction tx_1 is constructed and placed in block blk_2a.
tx_version = get_tx_version(get_block_height(blk_1r),
m_hardforks);
success = fill_tx_sources_and_destinations(
events,
/* head = */ blk_1r,
/* from = */ miner.get_keys(),
/* to = */ miner.get_public_address(),
/* amount = */ MK_TEST_COINS(10),
/* fee = */ m_fee_tx_1_blk_2a,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
success = construct_tx(miner.get_keys(),
sources,
destinations,
empty_attachment,
tx_1,
tx_version,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_1");
ADD_CUSTOM_EVENT(events, tx_1);
MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1r, miner, tx_1);
/* It is decided which chain will be the main: with the head blk_2 or with the
head blk_2a.
0 21 22
/ - (2 )
| \
| [tx_0]
|
(0 ) - ... - (1r)
|
|
|
\ - (2a)
\
[tx_1]
*/
CHECK_AND_ASSERT_EQ(is_pos_block(blk_2), false);
CHECK_AND_ASSERT_EQ(is_pos_block(blk_2a), false);
CHECK_AND_ASSERT_GREATER(m_fee_tx_1_blk_2a, m_fee_tx_0_blk_2);
CHECK_AND_ASSERT_EQ(get_block_height(blk_2), 22);
CHECK_AND_ASSERT_EQ(get_block_height(blk_2), get_block_height(blk_2a));
hf4_active =
m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM,
get_block_height(blk_2) + 1);
if (hf4_active)
{
/* With HF4 active, the chain with head blk_2a wins because transaction tx_1
has a greater fee than transaction tx_0. The main chain is the chain with
the head blk_2a. */
DO_CALLBACK(events, "check_after_hf4");
top_block = get_block_hash(blk_2a);
}
else
{
/* The chains have the same commulative difficulty. Therefore, with HF4
inactive, switching to the chain with the blk_2a head will not occur. The
main chain is the chain with the head blk_2. */
DO_CALLBACK(events, "check_before_hf4");
top_block = get_block_hash(blk_2);
}
DO_CALLBACK_PARAMS(events,
"check_top_block",
params_top_block(/* height = */ 22, top_block));
return true;
}
bool alt_chain_and_block_tx_fee_median::check_after_hf4(
currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events)
{
block_extended_info bei{};
const uint64_t height_block{22};
CHECK_AND_ASSERT_EQ(
m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM,
height_block),
true);
c.get_blockchain_storage().get_block_extended_info_by_height(height_block,
bei);
CHECK_AND_ASSERT_EQ(bei.this_block_tx_fee_median, m_fee_tx_1_blk_2a);
return true;
}
bool alt_chain_and_block_tx_fee_median::check_before_hf4(
currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events)
{
block_extended_info bei{};
const uint64_t height_block{22};
CHECK_AND_ASSERT_EQ(
m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM,
height_block),
false);
c.get_blockchain_storage().get_block_extended_info_by_height(height_block,
bei);
CHECK_AND_ASSERT_EQ(bei.this_block_tx_fee_median, m_fee_tx_0_blk_2);
return true;
}

View file

@ -82,3 +82,21 @@ struct chain_switching_when_out_spent_in_alt_chain_ref_id : public test_chain_un
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct alt_chain_and_block_tx_fee_median : public test_chain_unit_enchanced
{
alt_chain_and_block_tx_fee_median();
bool generate(std::vector<test_event_entry>& events) const;
bool check_after_hf4(currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events);
bool check_before_hf4(currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events);
private:
const uint64_t m_fee_tx_0_blk_2{TESTS_DEFAULT_FEE};
const uint64_t m_fee_tx_1_blk_2a{2 * m_fee_tx_0_blk_2};
};

View file

@ -997,6 +997,8 @@ bool test_generator::init_test_wallet(const currency::account_base& account, con
w->set_genesis(genesis_hash);
w->set_core_proxy(m_wallet_test_core_proxy);
w->set_disable_tor_relay(true);
w->set_concise_mode(true);
w->set_concise_mode_reorg_max_reorg_blocks(TESTS_CONCISE_MODE_REORG_MAX_REORG_BLOCK);
result = w;
return true;
@ -1113,7 +1115,7 @@ bool test_generator::construct_pow_block_with_alias_info_in_coinbase(const accou
miner_tx.proofs.emplace_back(std::move(currency::zc_asset_surjection_proof{}));
// range proofs
currency::zc_outs_range_proof range_proofs{};
r = generate_zc_outs_range_proof(tx_id, 0, tx_gen_context, miner_tx.vout, range_proofs);
r = generate_zc_outs_range_proof(tx_id, tx_gen_context, miner_tx.vout, range_proofs);
CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()");
miner_tx.proofs.emplace_back(std::move(range_proofs));
// balance proof
@ -1814,6 +1816,48 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s
return construct_tx(keys_from, sources, destinations, empty_attachment, tx, tx_version, 0);
}
bool construct_tx(const account_keys& sender_account_keys,
const std::vector<tx_source_entry>& sources,
const std::vector<tx_destination_entry>& destinations,
const std::vector<extra_v>& extra,
const std::vector<attachment_v>& attachments,
transaction& tx,
uint64_t tx_version,
crypto::secret_key& one_time_secret_key,
uint64_t unlock_time,
uint64_t expiration_time,
uint8_t tx_outs_attr,
bool shuffle,
uint64_t flags,
uint64_t explicit_consolidated_tx_fee,
tx_generation_context& gen_context)
{
// extra copy operation, but creating transaction is not sensitive to this
finalize_tx_param ftp {};
ftp.tx_version = tx_version;
ftp.sources = sources;
ftp.prepared_destinations = destinations;
ftp.extra = extra;
ftp.attachments = attachments;
ftp.unlock_time = unlock_time;
// ftp.crypt_address = crypt_destination_addr;
ftp.expiration_time = expiration_time;
ftp.tx_outs_attr = tx_outs_attr;
ftp.shuffle = shuffle;
ftp.flags = flags;
ftp.mode_separate_fee = explicit_consolidated_tx_fee;
finalized_tx ft = AUTO_VAL_INIT(ft);
ft.tx = tx;
ft.one_time_key = one_time_secret_key;
ftp.gen_context = gen_context; // ftp, not ft here, this is UGLY -- sowle
bool r = construct_tx(sender_account_keys, ftp, ft);
tx = ft.tx;
one_time_secret_key = ft.one_time_key;
gen_context = ft.ftp.gen_context;
return r;
}
uint64_t get_balance(const currency::account_keys& addr, const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, bool dbg_log)
{
uint64_t res = 0;
@ -1945,22 +1989,27 @@ void balance_via_wallet(const tools::wallet2& w, const crypto::public_key& asset
}
bool check_balance_via_wallet(const tools::wallet2& w, const char* account_name,
uint64_t expected_total, uint64_t expected_mined, uint64_t expected_unlocked, uint64_t expected_awaiting_in, uint64_t expected_awaiting_out, const crypto::public_key& asset_id /* = currency::native_coin_asset_id */)
uint64_t expected_total, uint64_t expected_mined, uint64_t expected_unlocked, uint64_t expected_awaiting_in, uint64_t expected_awaiting_out,
const crypto::public_key& asset_id /* = currency::native_coin_asset_id */, size_t asset_decimal_point /* = CURRENCY_DISPLAY_DECIMAL_POINT */)
{
uint64_t total, unlocked, awaiting_in, awaiting_out, mined;
balance_via_wallet(w, asset_id, &total, &unlocked, &awaiting_in, &awaiting_out, &mined);
std::string asset_id_str;
if (asset_id != currency::native_coin_asset_id)
{
asset_id_str = std::string(", asset_id: ") + epee::string_tools::pod_to_hex(asset_id).erase(4, 56).insert(4, "...");
if (asset_decimal_point == CURRENCY_DISPLAY_DECIMAL_POINT)
asset_decimal_point = w.get_asset_decimal_point(asset_id, asset_decimal_point);
}
LOG_PRINT_CYAN("Balance for wallet " << account_name << " @ height " << w.get_top_block_height() << asset_id_str << ":" << ENDL <<
"unlocked: " << print_money(unlocked) << ENDL <<
"awaiting in: " << print_money(awaiting_in) << ENDL <<
"awaiting out: " << print_money(awaiting_out) << ENDL <<
"mined: " << print_money(mined) << ENDL <<
"unlocked: " << print_money(unlocked, asset_decimal_point) << ENDL <<
"awaiting in: " << print_money(awaiting_in, asset_decimal_point) << ENDL <<
"awaiting out: " << print_money(awaiting_out, asset_decimal_point) << ENDL <<
"mined: " << print_money(mined, asset_decimal_point) << ENDL <<
"-----------------------------------------" << ENDL <<
"total: " << print_money(total) << ENDL,
"total: " << print_money(total, asset_decimal_point) << ENDL,
LOG_LEVEL_0);
bool r = true;
@ -1981,6 +2030,12 @@ bool check_balance_via_wallet(const tools::wallet2& w, const char* account_name,
return r;
}
bool check_balance_via_wallet(const tools::wallet2& w, const char* account_name, uint64_t expected_total, const crypto::public_key& asset_id, size_t asset_decimal_point /* = CURRENCY_DISPLAY_DECIMAL_POINT */)
{
return check_balance_via_wallet(w, account_name, expected_total, INVALID_BALANCE_VAL, INVALID_BALANCE_VAL, INVALID_BALANCE_VAL, INVALID_BALANCE_VAL, asset_id, asset_decimal_point);
}
// In assumption we have only genesis and few blocks with the same reward (==first_blocks_reward),
// this function helps to calculate such amount that many outputs have it, and amount, no output has it.
// It can fail, so check the returning value.
@ -2172,12 +2227,13 @@ bool make_tx_multisig_to_key(const currency::transaction& source_tx,
bool estimate_wallet_balance_blocked_for_escrow(const tools::wallet2& w, uint64_t& result, bool substruct_change_from_result /* = true */)
{
std::deque<tools::transfer_details> transfers;
tools::transfer_container transfers;
w.get_transfers(transfers);
result = 0;
for (const tools::transfer_details& td : transfers)
for (const auto& tr : transfers)
{
const tools::transfer_details& td = tr.second;
if (td.m_flags == (WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION))
result += td.amount();
}

View file

@ -19,8 +19,10 @@
#define TESTS_DEFAULT_FEE ((uint64_t)TX_DEFAULT_FEE)
#define MK_TEST_COINS(amount) (static_cast<uint64_t>(amount) * TX_DEFAULT_FEE)
#define TESTS_POS_CONFIG_MIN_COINSTAKE_AGE 4
#define TESTS_POS_CONFIG_POS_MINIMUM_HEIGH 4
#define TESTS_POS_CONFIG_MIN_COINSTAKE_AGE 4
#define TESTS_POS_CONFIG_POS_MINIMUM_HEIGH 4
#define TESTS_CONCISE_MODE_REORG_MAX_REORG_BLOCK 5
namespace concolor
{
@ -673,6 +675,22 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s
const currency::account_keys& keys_from, const currency::account_public_address& addr_to,
uint64_t total_amount, size_t outputs_count, uint64_t fee, currency::transaction& tx, bool use_ref_by_id = false);
bool construct_tx(const currency::account_keys& sender_account_keys,
const std::vector<currency::tx_source_entry>& sources,
const std::vector<currency::tx_destination_entry>& destinations,
const std::vector<currency::extra_v>& extra,
const std::vector<currency::attachment_v>& attachments,
currency::transaction& tx,
uint64_t tx_version,
crypto::secret_key& one_time_secret_key,
uint64_t unlock_time,
uint64_t expiration_time,
uint8_t tx_outs_attr,
bool shuffle,
uint64_t flags,
uint64_t explicit_consolidated_tx_fee,
currency::tx_generation_context& gen_context);
void get_confirmed_txs(const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs);
bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<currency::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head);
bool fill_tx_sources(std::vector<currency::tx_source_entry>& sources, const std::vector<test_event_entry>& events,
@ -715,7 +733,9 @@ bool check_balance_via_wallet(const tools::wallet2& w, const char* account_name,
uint64_t expected_unlocked = INVALID_BALANCE_VAL,
uint64_t expected_awaiting_in = INVALID_BALANCE_VAL,
uint64_t expected_awaiting_out = INVALID_BALANCE_VAL,
const crypto::public_key& asset_id = currency::native_coin_asset_id);
const crypto::public_key& asset_id = currency::native_coin_asset_id,
size_t asset_decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT);
bool check_balance_via_wallet(const tools::wallet2& w, const char* account_name, uint64_t expected_total, const crypto::public_key& asset_id, size_t asset_decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT);
bool calculate_amounts_many_outs_have_and_no_outs_have(const uint64_t first_blocks_reward, uint64_t& amount_many_outs_have, uint64_t& amount_no_outs_have);
bool find_global_index_for_output(const std::vector<test_event_entry>& events, const crypto::hash& head_block_hash, const currency::transaction& reference_tx, const size_t reference_tx_out_index, uint64_t& global_index);

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2024 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -17,25 +17,26 @@
template<typename t_callbacktype>
inline bool mine_next_pow_block_in_playtime(const currency::account_public_address& miner_addr, currency::core& c, t_callbacktype modify_block_cb, currency::block* output = nullptr)
{
currency::block b = AUTO_VAL_INIT(b);
currency::wide_difficulty_type diff;
uint64_t height;
currency::blobdata extra = AUTO_VAL_INIT(extra);
bool r = c.get_block_template(b, miner_addr, miner_addr, diff, height, extra);
currency::create_block_template_params cbtp{};
cbtp.ignore_pow_ts_check = true;
cbtp.miner_address = miner_addr;
currency::create_block_template_response cbtr{};
bool r = c.get_block_template(cbtp, cbtr);
CHECK_AND_ASSERT_MES(r, false, "get_block_template failed");
currency::block& b = cbtr.b;
// adjust block's timestamp to keep difficulty low
currency::block last_block = AUTO_VAL_INIT(last_block);
currency::block last_block{};
c.get_blockchain_storage().get_top_block(last_block);
b.timestamp = last_block.timestamp + DIFFICULTY_POW_TARGET;
// keep global time up with blocks' timestamps
test_core_time::adjust(b.timestamp);
modify_block_cb(b);
r = currency::miner::find_nonce_for_given_block(b, diff, height);
r = currency::miner::find_nonce_for_given_block(b, cbtr.diffic, cbtr.height);
CHECK_AND_ASSERT_MES(r, false, "find_nonce_for_given_block failed");
currency::block_verification_context bvc = AUTO_VAL_INIT(bvc);
currency::block_verification_context bvc{};
c.handle_incoming_block(t_serializable_object_to_blob(b), bvc);
CHECK_AND_NO_ASSERT_MES(!bvc.m_verification_failed && !bvc.m_marked_as_orphaned && !bvc.m_already_exists, false, "block verification context check failed");
@ -79,21 +80,23 @@ inline bool mine_next_pow_block_in_playtime_with_given_txs(const currency::accou
static epee::critical_section s_locker;
CHECK_AND_ASSERT_MES((height == SIZE_MAX) == (prev_id == currency::null_hash), false, "invalid agruments: height and prev_id should be specified or not specified together");
currency::block b = AUTO_VAL_INIT(b);
currency::wide_difficulty_type diff;
uint64_t height_from_template = 0;
currency::blobdata extra = AUTO_VAL_INIT(extra);
currency::pos_entry pe = AUTO_VAL_INIT(pe);
currency::create_block_template_params cbtp{};
cbtp.ignore_pow_ts_check = true;
cbtp.miner_address = miner_addr;
cbtp.pcustom_fill_block_template_func = loc_helper::fill_block_template_func;
currency::create_block_template_response cbtr{};
bool r = false;
{
CRITICAL_REGION_LOCAL(s_locker);
loc_helper::txs_accessor() = &txs;
r = c.get_blockchain_storage().create_block_template(miner_addr, miner_addr, extra, false, pe, loc_helper::fill_block_template_func, b, diff, height_from_template);
r = c.get_block_template(cbtp, cbtr);
}
CHECK_AND_ASSERT_MES(r, false, "get_block_template failed");
currency::block& b = cbtr.b;
// adjust block's timestamp to keep difficulty low
currency::block last_block = AUTO_VAL_INIT(last_block);
currency::block last_block{};
if (prev_id == currency::null_hash)
r = c.get_blockchain_storage().get_top_block(last_block);
else
@ -109,17 +112,17 @@ inline bool mine_next_pow_block_in_playtime_with_given_txs(const currency::accou
CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() > 0, false, "invalid miner_tx.vin");
CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], currency::txin_gen, in, false);
in.height = height;
set_tx_unlock_time(b.miner_tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
set_tx_unlock_time(b.miner_tx, cbtr.height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
}
else
{
height = height_from_template;
height = cbtr.height;
}
r = currency::miner::find_nonce_for_given_block(b, diff, height);
r = currency::miner::find_nonce_for_given_block(b, cbtr.diffic, cbtr.height);
CHECK_AND_ASSERT_MES(r, false, "find_nonce_for_given_block failed");
currency::block_verification_context bvc = AUTO_VAL_INIT(bvc);
currency::block_verification_context bvc{};
for (auto& tx : txs)
{
crypto::hash tx_id = currency::get_transaction_hash(tx);
@ -153,7 +156,7 @@ inline bool mine_next_pow_blocks_in_playtime_with_given_txs(const currency::acco
std::vector<currency::transaction> txs_local = txs;
crypto::hash prev_id_internal = prev_id;
currency::block prv_block = AUTO_VAL_INIT(prv_block);
currency::block prv_block{};
bool r = c.get_blockchain_storage().get_block_by_hash(prev_id, prv_block);
CHECK_AND_ASSERT_MES(r, false, "block with id " << prev_id << " not found");
@ -172,7 +175,7 @@ inline bool mine_next_pow_blocks_in_playtime_with_given_txs(const currency::acco
// NOTE: stake coins return back to the wallet, newly generated coins go to miner_address (by default they are the same destinations)
inline bool mine_next_pos_block_in_playtime_with_wallet(tools::wallet2& w, const currency::account_public_address& miner_address, size_t& pos_entries_count)
{
tools::wallet2::mining_context ctx = AUTO_VAL_INIT(ctx);
tools::wallet2::mining_context ctx{};
w.fill_mining_context(ctx);
if (!ctx.is_pos_allowed)
return false;

View file

@ -264,13 +264,13 @@ bool generate_and_play(const char* const genclass_name, size_t hardfork_id = SIZ
if (result)
{
LOG_PRINT_GREEN(std::string(100, '=') << std::endl <<
LOG_PRINT_GREEN(std::string(72, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< Succeeded" << std::endl <<
std::string(100, '=') << std::endl, LOG_LEVEL_0 );
}
else
{
LOG_PRINT_RED( std::string(100, '=') << std::endl <<
LOG_PRINT_RED( std::string(72, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< FAILED" << std::endl <<
std::string(100, '=') << std::endl, LOG_LEVEL_0);
result = false;
@ -1070,7 +1070,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_wallet_refreshing_on_chain_switch_2);
GENERATE_AND_PLAY(gen_wallet_unconfirmed_tx_from_tx_pool);
GENERATE_AND_PLAY_HF(gen_wallet_save_load_and_balance, "*");
GENERATE_AND_PLAY_HF(gen_wallet_mine_pos_block, "3");
GENERATE_AND_PLAY_HF(gen_wallet_mine_pos_block, "3-*");
GENERATE_AND_PLAY(gen_wallet_unconfirmed_outdated_tx);
GENERATE_AND_PLAY(gen_wallet_unlock_by_block_and_by_time);
GENERATE_AND_PLAY(gen_wallet_payment_id);
@ -1102,6 +1102,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(wallet_rpc_transfer);
GENERATE_AND_PLAY(wallet_rpc_alias_tests);
GENERATE_AND_PLAY_HF(wallet_rpc_exchange_suite, "3,4");
GENERATE_AND_PLAY_HF(wallet_true_rpc_pos_mining, "4-*");
GENERATE_AND_PLAY(wallet_chain_switch_with_spending_the_same_ki);
GENERATE_AND_PLAY(wallet_sending_to_integrated_address);
GENERATE_AND_PLAY_HF(block_template_blacklist_test, "4-*");
@ -1139,7 +1140,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY_HF(alt_blocks_with_the_same_txs, "3-*");
GENERATE_AND_PLAY_HF(chain_switching_when_out_spent_in_alt_chain_mixin, "3-*");
GENERATE_AND_PLAY_HF(chain_switching_when_out_spent_in_alt_chain_ref_id, "3-*");
GENERATE_AND_PLAY_HF(alt_chain_and_block_tx_fee_median, "3-*");
// miscellaneous tests
GENERATE_AND_PLAY(test_blockchain_vs_spent_keyimges);
@ -1216,6 +1217,10 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(tx_expiration_time_and_block_template);
GENERATE_AND_PLAY(tx_expiration_time_and_chain_switching);
GENERATE_AND_PLAY(tx_key_image_pool_conflict);
//GENERATE_AND_PLAY_HF(tx_version_against_hardfork, "4-*");
/* To execute the check of bare balance (function "check_tx_bare_balance") we need to run the test "tx_pool_semantic_validation" on the HF 3. By default behaviour bare outputs are disallowed on
the heights >= 10. */
GENERATE_AND_PLAY_HF(tx_pool_semantic_validation, "3");
// Double spend
GENERATE_AND_PLAY(gen_double_spend_in_tx<false>);
@ -1288,14 +1293,20 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(zarcanum_block_with_txs);
GENERATE_AND_PLAY(asset_depoyment_and_few_zc_utxos);
GENERATE_AND_PLAY_HF(assets_and_pos_mining, "4-*");
// GENERATE_AND_PLAY_HF(asset_emission_and_unconfirmed_balance, "4-*");
GENERATE_AND_PLAY_HF(asset_emission_and_unconfirmed_balance, "4-*");
GENERATE_AND_PLAY_HF(asset_operation_in_consolidated_tx, "4-*");
GENERATE_AND_PLAY_HF(asset_operation_and_hardfork_checks, "4-*");
GENERATE_AND_PLAY_HF(eth_signed_asset_basics, "5-*"); // TODO: make HF4 version
GENERATE_AND_PLAY_HF(eth_signed_asset_via_rpc, "5-*"); // TODO: make HF4 version
//GENERATE_AND_PLAY_HF(asset_current_and_total_supplies_comparative_constraints, "4-*"); <-- temporary disabled, waiting for Stepan's fix -- sowle
GENERATE_AND_PLAY_HF(several_asset_emit_burn_txs_in_pool, "5-*");
GENERATE_AND_PLAY_HF(pos_fuse_test, "4-*");
GENERATE_AND_PLAY_HF(wallet_reorganize_and_trim_test, "4-*");
GENERATE_AND_PLAY_HF(attachment_isolation_test, "4-*");
// GENERATE_AND_PLAY(gen_block_reward);
// END OF TESTS */

View file

@ -14,7 +14,7 @@ using namespace currency;
cumulative_difficulty_adjustment_test::cumulative_difficulty_adjustment_test()
{
epee::debug::get_set_enable_assert(true, true);
//epee::debug::get_set_enable_assert(true, true);
REGISTER_CALLBACK_METHOD(cumulative_difficulty_adjustment_test, configure_core);
REGISTER_CALLBACK_METHOD(cumulative_difficulty_adjustment_test, configure_check_height1);
REGISTER_CALLBACK_METHOD(cumulative_difficulty_adjustment_test, memorize_main_chain);
@ -25,7 +25,7 @@ cumulative_difficulty_adjustment_test::cumulative_difficulty_adjustment_test()
}
cumulative_difficulty_adjustment_test::~cumulative_difficulty_adjustment_test()
{
epee::debug::get_set_enable_assert(true, false);
//epee::debug::get_set_enable_assert(true, false);
}
#define FIRST_ALIAS_NAME "first"
#define SECOND_ALIAS_NAME "second"

View file

@ -317,7 +317,8 @@ bool escrow_altchain_meta_impl::c1(currency::core& c, size_t ev_index, const std
alice_wlt->scan_tx_pool(stub);
size_t blocks_fetched = 0;
alice_wlt->refresh(blocks_fetched);
CHECK_AND_ASSERT_MES(blocks_fetched == se.expected_blocks, false, "Alice got " << blocks_fetched << " after refresh, but " << se.expected_blocks << " is expected");
//fetched blocks disabled since resync might happened on different situation and number of blocks_fetched might be unexpected
//CHECK_AND_ASSERT_MES(blocks_fetched == se.expected_blocks, false, "Alice got " << blocks_fetched << " after refresh, but " << se.expected_blocks << " is expected");
LOG_PRINT_GREEN("Alice's transfers:" << ENDL << alice_wlt->dump_trunsfers(), LOG_LEVEL_1);
if (se.a_balance != UINT64_MAX)
{
@ -335,7 +336,8 @@ bool escrow_altchain_meta_impl::c1(currency::core& c, size_t ev_index, const std
bob_wlt->scan_tx_pool(stub);
blocks_fetched = 0;
bob_wlt->refresh(blocks_fetched);
CHECK_AND_ASSERT_MES(blocks_fetched == se.expected_blocks, false, "Bob got " << blocks_fetched << " after refresh, but " << se.expected_blocks << " is expected");
//fetched blocks disabled since resync might happened on different situation and number of blocks_fetched might be unexpected
//CHECK_AND_ASSERT_MES(blocks_fetched == se.expected_blocks, false, "Bob got " << blocks_fetched << " after refresh, but " << se.expected_blocks << " is expected");
LOG_PRINT_GREEN("Bob's transfers:" << ENDL << bob_wlt->dump_trunsfers(), LOG_LEVEL_1);
if (se.b_balance != UINT64_MAX)
{

View file

@ -32,7 +32,7 @@ escrow_wallet_test::escrow_wallet_test()
bool escrow_wallet_test::generate(std::vector<test_event_entry>& events) const
{
epee::debug::get_set_enable_assert(true, true);
//epee::debug::get_set_enable_assert(true, true);
currency::account_base genesis_acc;
genesis_acc.generate();
@ -47,7 +47,7 @@ bool escrow_wallet_test::generate(std::vector<test_event_entry>& events) const
DO_CALLBACK(events, "c1");
epee::debug::get_set_enable_assert(true, false);
//epee::debug::get_set_enable_assert(true, false);
return true;
}
@ -272,8 +272,8 @@ bool escrow_wallet_test::exec_test_with_cancel_release_type(currency::core& c, c
bool escrow_wallet_test::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
epee::debug::get_set_enable_assert(true, true);
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){epee::debug::get_set_enable_assert(true, false); });
//epee::debug::get_set_enable_assert(true, true);
//misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){epee::debug::get_set_enable_assert(true, false); });
bool r = exec_test_with_cancel_release_type(c, events);
if (!r)
@ -287,7 +287,7 @@ bool escrow_wallet_test::c1(currency::core& c, size_t ev_index, const std::vecto
if (!r)
return false;
epee::debug::get_set_enable_assert(true, false);
//epee::debug::get_set_enable_assert(true, false);
return r;
}
@ -780,7 +780,7 @@ bool escrow_proposal_expiration::c1(currency::core& c, size_t ev_index, const st
uint64_t alice_post_proposal_balance = alice_wlt->balance();
uint64_t alice_post_proposal_balance_expected = alice_start_balance - TESTS_DEFAULT_FEE;
CHECK_AND_ASSERT_MES(alice_post_proposal_balance == alice_post_proposal_balance_expected, false, "Incorrect alice_post_proposal_balance: " << print_money(alice_post_proposal_balance) << ", expected: " << print_money(alice_post_proposal_balance_expected));
std::deque<tools::transfer_details> transfers;
tools::transfer_container transfers;
alice_wlt->get_transfers(transfers);
CHECK_AND_ASSERT_MES(transfers.size() == 2 && (
(transfers[0].is_spent() && (transfers[1].m_flags & (WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION))) ||
@ -2283,7 +2283,7 @@ bool escrow_proposal_not_enough_money::c1(currency::core& c, size_t ev_index, co
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "Alice", MK_TEST_COINS(30), 0, MK_TEST_COINS(30), 0, 0), false, "");
std::deque<tools::transfer_details> transfers;
tools::transfer_container transfers;
alice_wlt->get_transfers(transfers);
CHECK_AND_ASSERT_MES(transfers.size() == 1, false, "Incorrect transfers size: " << transfers.size());

View file

@ -7,51 +7,7 @@
using namespace currency;
namespace currency
{
bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources,
const std::vector<tx_destination_entry>& destinations,
const std::vector<extra_v>& extra,
const std::vector<attachment_v>& attachments,
transaction& tx,
uint64_t tx_version,
crypto::secret_key& one_time_secret_key,
uint64_t unlock_time,
uint64_t expiration_time,
uint8_t tx_outs_attr,
bool shuffle,
uint64_t flags,
uint64_t explicit_consolidated_tx_fee,
tx_generation_context& gen_context)
{
//extra copy operation, but creating transaction is not sensitive to this
finalize_tx_param ftp{};
ftp.tx_version = tx_version;
ftp.sources = sources;
ftp.prepared_destinations = destinations;
ftp.extra = extra;
ftp.attachments = attachments;
ftp.unlock_time = unlock_time;
// ftp.crypt_address = crypt_destination_addr;
ftp.expiration_time = expiration_time;
ftp.tx_outs_attr = tx_outs_attr;
ftp.shuffle = shuffle;
ftp.flags = flags;
ftp.mode_separate_fee = explicit_consolidated_tx_fee;
finalized_tx ft = AUTO_VAL_INIT(ft);
ft.tx = tx;
ft.one_time_key = one_time_secret_key;
ftp.gen_context = gen_context; // ftp, not ft here, this is UGLY -- sowle
bool r = construct_tx(sender_account_keys, ftp, ft);
tx = ft.tx;
one_time_secret_key = ft.one_time_key;
gen_context = ft.ftp.gen_context;
return r;
}
} // namespace currency
void add_flags_to_all_destination_entries(const uint64_t flags, std::vector<currency::tx_destination_entry>& destinations)
static void add_flags_to_all_destination_entries(const uint64_t flags, std::vector<currency::tx_destination_entry>& destinations)
{
for(auto& de : destinations)
de.flags |= flags;

View file

@ -32,7 +32,7 @@ bool isolate_auditable_and_proof::generate(std::vector<test_event_entry>& events
test_core_time::adjust(m_genesis_timestamp);
epee::debug::get_set_enable_assert(true, true);
//epee::debug::get_set_enable_assert(true, true);
currency::account_base genesis_acc;
genesis_acc.generate();
@ -47,7 +47,7 @@ bool isolate_auditable_and_proof::generate(std::vector<test_event_entry>& events
REWIND_BLOCKS_N(events, blk_0r, blk_0, m_mining_accunt, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 15);
DO_CALLBACK(events, "c1");
epee::debug::get_set_enable_assert(true, false);
//epee::debug::get_set_enable_assert(true, false);
return true;
}
@ -61,8 +61,8 @@ bool isolate_auditable_and_proof::configure_core(currency::core& c, size_t ev_in
bool isolate_auditable_and_proof::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
epee::debug::get_set_enable_assert(true, true);
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){epee::debug::get_set_enable_assert(true, false); });
//epee::debug::get_set_enable_assert(true, true);
//misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){epee::debug::get_set_enable_assert(true, false); });
LOG_PRINT_MAGENTA("Mining Address: " << currency::get_account_address_as_str(m_mining_accunt.get_public_address()), LOG_LEVEL_0);

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2023 Zano Project
// Copyright (c) 2014-2024 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -47,3 +47,77 @@ struct asset_emission_and_unconfirmed_balance : public wallet_test
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct asset_operation_and_hardfork_checks : public wallet_test
{
public:
asset_operation_and_hardfork_checks();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events);
bool c2(currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events);
private:
mutable currency::asset_descriptor_base m_adb_hello{};
mutable currency::asset_descriptor_operation m_ado_hello{};
mutable currency::asset_descriptor_base m_adb_bye{};
mutable currency::asset_descriptor_operation m_ado_bye{};
};
struct asset_operation_in_consolidated_tx : public wallet_test
{
public:
asset_operation_in_consolidated_tx();
bool generate(std::vector<test_event_entry>& events) const;
bool assert_balances(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool assert_alice_currency_not_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
private:
mutable currency::asset_descriptor_base m_adb_alice_currency{};
mutable currency::asset_descriptor_operation m_ado_alice_currency{};
};
struct eth_signed_asset_basics : public wallet_test
{
eth_signed_asset_basics();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct eth_signed_asset_via_rpc : public wallet_test
{
eth_signed_asset_via_rpc();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct asset_current_and_total_supplies_comparative_constraints : public wallet_test
{
public:
asset_current_and_total_supplies_comparative_constraints();
bool generate(std::vector<test_event_entry>& events) const;
bool assert_asset_alpha_not_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool assert_asset_beta_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool emit_asset_beta_with_incorrect_supply(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool assert_asset_beta_not_emitted(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool public_burn_asset_beta_with_incorrect_supply(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool assert_asset_gamma_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
private:
enum asset_position { alpha = 0, beta = 1, gamma = 2 };
mutable std::array<currency::asset_descriptor_base, 3> m_adbs{};
mutable std::array<currency::asset_descriptor_operation, 3> m_ados_register{};
mutable currency::asset_descriptor_operation m_ado_emit{};
};
struct several_asset_emit_burn_txs_in_pool : public wallet_test
{
several_asset_emit_burn_txs_in_pool();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};

View file

@ -2571,7 +2571,7 @@ bool multisig_unconfirmed_transfer_and_multiple_scan_pool_calls::c1(currency::co
LOG_PRINT_YELLOW("%%%%% tx " << get_transaction_hash(tx) << " is spending multisig output " << multisig_id, LOG_LEVEL_0);
bool stub;
std::deque<tools::transfer_details> transfers;
tools::transfer_container transfers;
std::vector<tools::wallet_public::wallet_transfer_info> unconfirmed_transfers;
alice_wlt->scan_tx_pool(stub);

View file

@ -169,6 +169,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
uint64_t block_reward_without_fee = 0;
m_block_reward = 0;
size_t estimated_block_size = m_txs_total_size;
m_block.miner_tx = transaction{};
bool r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee,
reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, m_block_reward, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use);
CHECK_AND_ASSERT_THROW_MES(r, "construct_miner_tx failed");
@ -177,6 +178,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
size_t cumulative_size = 0;
for (size_t try_count = 0; try_count != 10; ++try_count)
{
m_block.miner_tx = transaction{};
r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee,
reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, m_block_reward, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use);
CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed");
@ -280,7 +282,7 @@ void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const cu
// range proofs
currency::zc_outs_range_proof range_proofs{};
r = generate_zc_outs_range_proof(miner_tx_id, 0, m_miner_tx_tgc, m_block.miner_tx.vout, range_proofs);
r = generate_zc_outs_range_proof(miner_tx_id, m_miner_tx_tgc, m_block.miner_tx.vout, range_proofs);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to generate zc_outs_range_proof()");
m_block.miner_tx.proofs.emplace_back(std::move(range_proofs));

View file

@ -9,6 +9,7 @@
#include "offers_tests_common.h"
#include "tx_builder.h"
#include "chaingen_helpers.h"
#include "../../src/currency_core/tx_semantic_validation.h"
using namespace epee;
using namespace crypto;
@ -1620,3 +1621,503 @@ bool tx_key_image_pool_conflict::generate(std::vector<test_event_entry>& events)
return true;
}
//------------------------------------------------------------------
bool tx_version_against_hardfork::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure that tx with incorrect for the activated HF version won't be accepted.
bool r = false;
GENERATE_ACCOUNT(miner_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// 0 ... 10 11 12 13 14 <- height
// (0 )- (0r)- (1 )- !2b!- <- chain A, block 2b is invalid
// tx_0 tx_1 tx_0 is accepted, tx_1 is rejected
// \
// \(2 ) <- chain B
// tx_1 tx_1 is accepted
uint64_t tx_version_good = 0, tx_version_bad = 0;
// select good and bad tx versions based on active hardfork
size_t most_recent_hardfork_id = m_hardforks.get_the_most_recent_hardfork_id_for_height(get_block_height(blk_0r));
switch(most_recent_hardfork_id)
{
case ZANO_HARDFORK_04_ZARCANUM:
case ZANO_HARDFORK_05:
tx_version_good = TRANSACTION_VERSION_POST_HF4;
tx_version_bad = TRANSACTION_VERSION_PRE_HF4;
break;
default:
LOG_ERROR("hardfork " << most_recent_hardfork_id << " is not supported by this test");
return false;
}
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
//
// 1/2 tx with good version, should go okay
//
r = fill_tx_sources_and_destinations(events, blk_0r, miner_acc.get_keys(), miner_acc.get_public_address(), MK_TEST_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources_and_destinations failed");
transaction tx_0{};
r = construct_tx(miner_acc.get_keys(), sources, destinations, empty_attachment, tx_0, tx_version_good, 0);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
sources.clear();
destinations.clear();
//
// 2/2 tx with bad version, should be rejected by tx pool and by the core
//
r = fill_tx_sources_and_destinations(events, blk_0, miner_acc.get_keys(), miner_acc.get_public_address(), MK_TEST_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources_and_destinations failed");
transaction tx_1{};
r = construct_tx(miner_acc.get_keys(), sources, destinations, empty_attachment, tx_0, tx_version_bad, 0);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
// check tx pool rejection
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(tx_1);
// now add tx_1 as onboard transaction (directly to a block, skipping tx pool)
events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, true));
events.push_back(tx_1);
events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, false));
// make sure the block with tx_1 is invalid
DO_CALLBACK(events, "mark_invalid_block");
MAKE_NEXT_BLOCK_TX1(events, blk_2b, blk_1, miner_acc, tx_1);
// just one more block to make sure everyting is okay
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc);
return true;
}
bool tx_pool_semantic_validation::generate(std::vector<test_event_entry>& events) const
{
// Test idea: ensure that the checks contained in the function "validate_tx_semantic" body are performed.
GENERATE_ACCOUNT(miner);
MAKE_GENESIS_BLOCK(events, blk_0, miner, test_core_time::get_time());
DO_CALLBACK(events, "configure_core");
CHECK_AND_ASSERT_EQ(validate_tx_semantic(transaction{}, CURRENCY_MAX_TRANSACTION_BLOB_SIZE), false);
// No inputs.
{
transaction tx{};
tx.vin = {};
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Unsupported input type.
{
transaction tx{};
tx.vin.emplace_back(txin_gen{});
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Unsupported output type.
{
transaction tx{};
tx.vin.push_back(txin_to_key{});
tx.vout.emplace_back();
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Inputs amount overflow.
{
point_t point_public_key{};
txout_to_key target{};
std::array<txin_to_key, 2> inputs{};
transaction tx{};
CHECK_AND_ASSERT_EQ(point_public_key.from_string("499790c3302b9f0514e2db09b390679283d43d971383d33dc24c7991ea4cf6d7"), true);
target.key = point_public_key.to_public_key();
inputs.at(0).amount = 1;
inputs.at(1).amount = UINT64_MAX;
for (const auto& input : inputs)
{
tx.vin.push_back(input);
}
CHECK_AND_ASSERT_GREATER(inputs.at(0).amount, inputs.at(0).amount + inputs.at(1).amount);
CHECK_AND_ASSERT_GREATER(inputs.at(1).amount, inputs.at(0).amount + inputs.at(1).amount);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Outputs amount overflow.
{
point_t point_public_key{};
txout_to_key target{};
std::array<tx_out_bare, 2> outputs{};
transaction tx{};
CHECK_AND_ASSERT_EQ(point_public_key.from_string("78ef3d9af7b5e3d09556d57820cf68c2b3553a9d8205c01fe40fc70aae86bb4f"), true);
target.key = point_public_key.to_public_key();
outputs.at(0).amount = 1;
outputs.at(1).amount = UINT64_MAX;
for (auto& output : outputs)
{
output.target = target;
tx.vout.push_back(output);
}
tx.vin.push_back(txin_to_key{});
CHECK_AND_ASSERT_GREATER(outputs.at(0).amount, outputs.at(0).amount + outputs.at(1).amount);
CHECK_AND_ASSERT_GREATER(outputs.at(1).amount, outputs.at(0).amount + outputs.at(1).amount);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Equal key images in inputs.
{
transaction tx{};
{
txin_zc_input input_zc{};
txin_htlc input_htlc{};
txin_to_key input_to_key{};
point_t point_key_image{};
CHECK_AND_ASSERT_EQ(point_key_image.from_string("93fa59f43fb9cff98e6867d20cf200c98b29cae406acdbde798ffb3e30d3503a"), true);
input_zc.k_image = point_key_image.to_key_image();
CHECK_AND_ASSERT_EQ(point_key_image.from_string("ad1226e3fd1be15e26b119fa80380e580a498e5fa3421b63fded89672b526a44"), true);
input_htlc.k_image = point_key_image.to_key_image();
CHECK_AND_ASSERT_EQ(point_key_image.from_string("8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96"), true);
input_to_key.k_image = point_key_image.to_key_image();
tx.vin.push_back(input_zc);
tx.vin.push_back(input_htlc);
tx.vin.push_back(input_to_key);
}
{
txin_to_key input{};
input.amount = 0;
tx.vin.push_back(input);
}
for (int8_t step{}; step < 3; ++step)
{
tx.vin.push_back(tx.vin.front());
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
tx.vin.erase(tx.vin.begin(), tx.vin.begin() + 1);
}
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
}
// Two entries of the same type in extra.
{
transaction tx{};
std::array<txin_to_key, 2> inputs{};
std::array<point_t, 2> key_image_points{};
CHECK_AND_ASSERT_EQ(key_image_points.at(0).from_string("de3c22a62f15e6de8abe6b217085b2aead196daf5ddd67d9c4b366330736fbeb"), true);
CHECK_AND_ASSERT_EQ(key_image_points.at(1).from_string("9f3eef913921ca35239e696725595e3686bb0d69e3e805791c5aa93d5754aa5c"), true);
for (int position{}; position < 2; ++position)
{
inputs.at(position).k_image = key_image_points.at(position).to_key_image();
tx.vin.push_back(inputs.at(position));
}
tx.extra.push_back(null_pkey);
tx.extra.push_back(null_pkey);
CHECK_AND_ASSERT_EQ(tx.extra.size(), 2);
CHECK_AND_ASSERT_EQ(typeid(tx.extra.front()), typeid(tx.extra.back()));
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// tx.version <= TRANSACTION_VERSION_PRE_HF4. Balance check fail: sum of inputs <= sum of outputs.
{
tx_out_bare output{};
transaction tx{};
std::array<point_t, 2> key_image_points{};
std::array<txin_to_key, 2> inputs{};
output.amount = 3;
tx.vout.push_back(output);
tx.version = 0;
CHECK_AND_ASSERT_EQ(key_image_points.at(0).from_string("8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96"), true);
CHECK_AND_ASSERT_EQ(key_image_points.at(1).from_string("dc48b741dacda5ac026ad0a7d193b816049eb08724907a1ff6f95839cfb0efa5"), true);
for (int position{}; position < 2; ++position)
{
auto& input{inputs.at(position)};
input.amount = 1;
input.k_image = key_image_points.at(position).to_key_image();
tx.vin.push_back(input);
}
const uint64_t sum_inputs{std::accumulate(inputs.begin(), inputs.end(), std::uint64_t{}, [](uint64_t sum, const txin_to_key& input) { return sum + input.amount; })};
CHECK_AND_ASSERT_LESS(sum_inputs, output.amount);
CHECK_AND_ASSERT_EQ(output.amount - sum_inputs, 1);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Semantically valid transaction.
{
tx_out_bare output;
transaction tx{};
std::array<point_t, 2> key_image_points{};
std::array<txin_to_key, 2> inputs{};
output.amount = 3'000'000'000'000;
tx.version = 0;
tx.vout.push_back(output);
CHECK_AND_ASSERT_EQ(key_image_points.at(0).from_string("fe1ef4a48a69804652324dc071cb72f49c22cd97479583950eaff746c936f72c"), true);
CHECK_AND_ASSERT_EQ(key_image_points.at(1).from_string("0bf1d8bb0988069f2c2b4f0dc89c81830c1178e04450d1da31ef020660732279"), true);
for (int position{}; position < 2; ++position)
{
auto& input{inputs.at(position)};
input.amount = 2'000'000'000'000;
input.k_image = key_image_points.at(position).to_key_image();
tx.vin.push_back(input);
}
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// Construct a valid transaction and then modify it so that the transaction is no longer semantically correct.
// No inputs.
{
MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
tx.vin = {};
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Unsupported input type.
{
MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
tx.vin.emplace_back(txin_gen{});
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Unsupported output type.
{
MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
tx.vout.emplace_back();
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Inputs amount overflow.
{
point_t point_public_key{};
txout_to_key target{};
std::array<txin_to_key, 2> inputs{};
MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
CHECK_AND_ASSERT_EQ(point_public_key.from_string("499790c3302b9f0514e2db09b390679283d43d971383d33dc24c7991ea4cf6d7"), true);
target.key = point_public_key.to_public_key();
inputs.at(0).amount = 1;
inputs.at(1).amount = UINT64_MAX;
for (const auto& input : inputs)
{
tx.vin.push_back(input);
}
CHECK_AND_ASSERT_GREATER(inputs.at(0).amount, inputs.at(0).amount + inputs.at(1).amount);
CHECK_AND_ASSERT_GREATER(inputs.at(1).amount, inputs.at(0).amount + inputs.at(1).amount);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Outputs amount overflow.
{
point_t point_public_key{};
txout_to_key target{};
std::array<tx_out_bare, 2> outputs{};
MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
CHECK_AND_ASSERT_EQ(point_public_key.from_string("78ef3d9af7b5e3d09556d57820cf68c2b3553a9d8205c01fe40fc70aae86bb4f"), true);
target.key = point_public_key.to_public_key();
outputs.at(0).amount = 1;
outputs.at(1).amount = UINT64_MAX;
for (auto& output : outputs)
{
output.target = target;
tx.vout.push_back(output);
}
tx.vin.push_back(txin_to_key{});
CHECK_AND_ASSERT_GREATER(outputs.at(0).amount, outputs.at(0).amount + outputs.at(1).amount);
CHECK_AND_ASSERT_GREATER(outputs.at(1).amount, outputs.at(0).amount + outputs.at(1).amount);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// Equal key images in inputs.
{
MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r);
{
txin_zc_input input_zc{};
txin_htlc input_htlc{};
txin_to_key input_to_key{};
point_t point_key_image{};
CHECK_AND_ASSERT_EQ(point_key_image.from_string("93fa59f43fb9cff98e6867d20cf200c98b29cae406acdbde798ffb3e30d3503a"), true);
input_zc.k_image = point_key_image.to_key_image();
CHECK_AND_ASSERT_EQ(point_key_image.from_string("ad1226e3fd1be15e26b119fa80380e580a498e5fa3421b63fded89672b526a44"), true);
input_htlc.k_image = point_key_image.to_key_image();
CHECK_AND_ASSERT_EQ(point_key_image.from_string("8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96"), true);
input_to_key.k_image = point_key_image.to_key_image();
tx.vin.push_back(input_zc);
tx.vin.push_back(input_htlc);
tx.vin.push_back(input_to_key);
}
for (int8_t step{}; step < 3; ++step)
{
tx.vin.push_back(tx.vin.front());
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
tx.vin.erase(tx.vin.begin(), tx.vin.begin() + 1);
}
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
}
// Two entries of the same type in extra.
{
MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
tx.extra.push_back(null_pkey);
tx.extra.push_back(null_pkey);
CHECK_AND_ASSERT_GREATER(tx.extra.size(), 2);
CHECK_AND_ASSERT_EQ(typeid(tx.extra.back()), typeid(tx.extra.at(tx.extra.size() - 2)));
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
// tx.version <= TRANSACTION_VERSION_PRE_HF4. Balance check fail: sum of inputs <= sum of outputs.
{
tx_out_bare output{};
std::array<point_t, 2> key_image_points{};
std::array<txin_to_key, 2> inputs{};
MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(1), TESTS_DEFAULT_FEE, blk_0r);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true);
output.amount = 10'000'000'003;
tx.vout.push_back(output);
tx.version = 0;
CHECK_AND_ASSERT_EQ(key_image_points.at(0).from_string("8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96"), true);
CHECK_AND_ASSERT_EQ(key_image_points.at(1).from_string("dc48b741dacda5ac026ad0a7d193b816049eb08724907a1ff6f95839cfb0efa5"), true);
for (int position{}; position < 2; ++position)
{
auto& input{inputs.at(position)};
input.amount = 1;
input.k_image = key_image_points.at(position).to_key_image();
tx.vin.push_back(input);
}
const auto inputs_sum{[](const uint64_t sum, const txin_v& input) -> uint64_t
{
if (input.type() == typeid(txin_to_key))
{
return sum + boost::get<txin_to_key>(input).amount;
}
if (input.type() == typeid(txin_multisig))
{
return sum + boost::get<txin_multisig>(input).amount;
}
return sum;
}
};
const auto outputs_sum{[](const uint64_t sum, const tx_out_v& output) -> uint64_t
{
if (output.type() == typeid(tx_out_bare))
{
return sum + boost::get<tx_out_bare>(output).amount;
}
return sum;
}
};
const uint64_t inputs_amount{std::accumulate(tx.vin.begin(), tx.vin.end(), std::uint64_t{}, inputs_sum)};
const uint64_t outputs_amount{std::accumulate(tx.vout.begin(), tx.vout.end(), std::uint64_t{}, outputs_sum)};
CHECK_AND_ASSERT_LESS(inputs_amount, outputs_amount);
CHECK_AND_ASSERT_EQ(outputs_amount - inputs_amount, 1);
CHECK_AND_ASSERT(tx.version <= TRANSACTION_VERSION_PRE_HF4, false);
CHECK_AND_ASSERT_EQ(get_block_height(blk_0r), 10);
CHECK_AND_ASSERT_EQ(m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_03, 10), true);
CHECK_AND_ASSERT_EQ(m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, 10), false);
CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false);
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx);
}
return true;
}

Some files were not shown because too many files have changed in this diff Show more