1
0
Fork 0
forked from lthn/blockchain

Merge branch 'develop' into concise

# Conflicts:
#	src/wallet/wallet2.cpp
#	src/wallet/wallet2_base.h
This commit is contained in:
sowle 2024-10-09 01:03:02 +02:00
commit 3a6e0c0f2f
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
77 changed files with 3750 additions and 434 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

@ -5,6 +5,14 @@ add_subdirectory(zlib)
add_subdirectory(db)
add_subdirectory(ethereum)
option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." OFF)
option(SECP256K1_BUILD_TESTS "Build tests." OFF)
option(SECP256K1_BUILD_EXHAUSTIVE_TESTS "Build exhaustive tests." OFF)
option(SECP256K1_BUILD_CTIME_TESTS "Build constant-time tests." OFF)
option(SECP256K1_BUILD_EXAMPLES "Build examples." OFF)
set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1)
add_subdirectory(bitcoin-secp256k1)
if( NOT DISABLE_TOR)
add_subdirectory(tor-connect)
endif()
@ -23,6 +31,9 @@ set_property(TARGET libminiupnpc-static PROPERTY FOLDER "contrib")
set_property(TARGET zlibstatic PROPERTY FOLDER "contrib")
set_property(TARGET mdbx PROPERTY FOLDER "contrib")
set_property(TARGET lmdb PROPERTY FOLDER "contrib")
set_property(TARGET secp256k1 PROPERTY FOLDER "contrib")
set_property(TARGET secp256k1_precomputed PROPERTY FOLDER "contrib")
if( NOT DISABLE_TOR)
set_property(TARGET tor-connect PROPERTY FOLDER "contrib")
endif()

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

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

@ -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.
//
@ -473,6 +474,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 +520,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,8 @@ else()
endif()
add_library(crypto ${CRYPTO})
add_dependencies(crypto secp256k1)
target_link_libraries(crypto secp256k1)
add_library(currency_core ${CURRENCY_CORE})
add_dependencies(currency_core version ${PCH_LIB_NAME})

View file

@ -4,7 +4,7 @@
#pragma once
#define BEGIN_BOOST_SERIALIZATION() template <class t_archive> inline void serialize(t_archive &_arch, const unsigned int ver) {
#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");

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,15 @@ 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);
}
} // namespace serialization
} // namespace boost

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_2829200.pak", "ea874a67934f22d500658fe603f6a25e85f6a64c51669ae78bf9e1c5be6d6d87", 5824475897, 9663528960 };
static constexpr pre_download_entry c_pre_download_lmdb = { "http://95.217.42.247/pre-download/zano_lmdb_95_2829200.pak", "d8d50c2aa832ed4ae8ca00ef9a0de0eb72a402791ebf8b3f64e546036879545e", 7204761982, 9414295552 };
#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

@ -8,6 +8,7 @@
#include <string>
#include <boost/multiprecision/cpp_int.hpp>
#include "crypto.h"
#include "eth_signature.h"
namespace crypto
{
@ -1209,6 +1210,16 @@ namespace crypto
m_elements.emplace_back(pk);
}
void add_eth_pub_key(const crypto::eth_public_key& epk)
{
static_assert(sizeof(item_t) == 32, "unexpected size of hs_t::item_t");
static_assert(sizeof epk.data == 33, "unexpected size of eth_public_key");
m_elements.emplace_back(c_scalar_0);
m_elements.emplace_back(c_scalar_0);
char* p = m_elements[m_elements.size() - 2].c; // pointer to the first of the two added items
memcpy(p, &epk.data, sizeof epk.data);
}
void add_key_image(const crypto::key_image& ki)
{
m_elements.emplace_back(ki);

View file

@ -0,0 +1,166 @@
// Copyright (c) 2024 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "eth_signature.h"
#include "crypto.h"
#include "bitcoin-secp256k1/include/secp256k1.h"
#include "random.h"
#include "misc_language.h"
#include <string_tools.h>
namespace crypto
{
bool generate_eth_key_pair(eth_secret_key& sec_key, eth_public_key& pub_key) noexcept
{
try
{
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){
secp256k1_context_destroy(ctx);
ctx = nullptr;
});
uint8_t randomness[32];
crypto::generate_random_bytes(sizeof randomness, randomness);
if (!secp256k1_context_randomize(ctx, randomness))
return false;
for(size_t i = 1024; i != 0; --i)
{
crypto::generate_random_bytes(sizeof sec_key, sec_key.data);
if (secp256k1_ec_seckey_verify(ctx, sec_key.data))
break;
if (i == 1)
return false;
}
secp256k1_pubkey uncompressed_pub_key{};
if (!secp256k1_ec_pubkey_create(ctx, &uncompressed_pub_key, sec_key.data))
return false;
size_t output_len = sizeof pub_key;
if (!secp256k1_ec_pubkey_serialize(ctx, pub_key.data, &output_len, &uncompressed_pub_key, SECP256K1_EC_COMPRESSED))
return false;
return true;
}
catch(...)
{
return false;
}
}
bool eth_secret_key_to_public_key(const eth_secret_key& sec_key, eth_public_key& pub_key) noexcept
{
try
{
// TODO: do we need this? consider using static context
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){
secp256k1_context_destroy(ctx);
ctx = nullptr;
});
secp256k1_pubkey uncompressed_pub_key{};
if (!secp256k1_ec_pubkey_create(ctx, &uncompressed_pub_key, sec_key.data))
return false;
size_t output_len = sizeof pub_key;
if (!secp256k1_ec_pubkey_serialize(ctx, pub_key.data, &output_len, &uncompressed_pub_key, SECP256K1_EC_COMPRESSED))
return false;
return true;
}
catch(...)
{
return false;
}
}
// generates secp256k1 ECDSA signature
bool generate_eth_signature(const hash& m, const eth_secret_key& sec_key, eth_signature& sig) noexcept
{
try
{
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){
secp256k1_context_destroy(ctx);
ctx = nullptr;
});
uint8_t randomness[32];
crypto::generate_random_bytes(sizeof randomness, randomness);
if (!secp256k1_context_randomize(ctx, randomness))
return false;
secp256k1_ecdsa_signature secp256k1_ecdsa_sig{};
if (!secp256k1_ecdsa_sign(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)m.data, sec_key.data, NULL, NULL))
return false;
if (!secp256k1_ecdsa_signature_serialize_compact(ctx, sig.data, &secp256k1_ecdsa_sig))
return false;
return true;
}
catch(...)
{
return false;
}
}
// verifies secp256k1 ECDSA signature
bool verify_eth_signature(const hash& m, const eth_public_key& pub_key, const eth_signature& sig) noexcept
{
try
{
// TODO (performance) consider using secp256k1_context_static for verification -- sowle
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){
secp256k1_context_destroy(ctx);
ctx = nullptr;
});
uint8_t randomness[32];
crypto::generate_random_bytes(sizeof randomness, randomness);
if (!secp256k1_context_randomize(ctx, randomness))
return false;
secp256k1_ecdsa_signature secp256k1_ecdsa_sig{};
secp256k1_pubkey uncompressed_pub_key{};
if (!secp256k1_ecdsa_signature_parse_compact(ctx, &secp256k1_ecdsa_sig, sig.data))
return false;
if (!secp256k1_ec_pubkey_parse(ctx, &uncompressed_pub_key, pub_key.data, sizeof pub_key))
return false;
// verify a signature
if (!secp256k1_ecdsa_verify(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)m.data, &uncompressed_pub_key))
return false;
return true;
}
catch(...)
{
return false;
}
}
std::ostream& operator<<(std::ostream& o, const eth_secret_key& v)
{
return o << epee::string_tools::pod_to_hex(v);
}
std::ostream& operator<<(std::ostream& o, const eth_public_key& v)
{
return o << epee::string_tools::pod_to_hex(v);
}
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

@ -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,16 +71,23 @@ 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())
{
//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);
@ -92,7 +99,7 @@ namespace currency
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;

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

@ -3880,11 +3880,9 @@ 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(), "asset_descriptor_history unexpectedly have 0 size");
CHECK_AND_ASSERT_THROW_MES(asset_descriptor_history.size(), "asset_descriptor_history unexpectedly have 0 size, asset_id: " << asset_id);
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;
@ -4010,7 +4008,8 @@ bool blockchain_storage::put_alias_info(const transaction & tx, extra_alias_entr
//@@ remove get_tx_fee_median();
LOG_PRINT_MAGENTA("[ALIAS_REGISTERED]: " << ai.m_alias << ": " << get_account_address_as_str(ai.m_address) << ", fee median: " << get_tx_fee_median(), LOG_LEVEL_1);
rise_core_event(CORE_EVENT_ADD_ALIAS, alias_info_to_rpc_alias_info(ai));
}else
}
else
{
//update procedure
CHECK_AND_ASSERT_MES(ai.m_sign.size() == 1, false, "alias " << ai.m_alias << " can't be update, wrong ai.m_sign.size() count: " << ai.m_sign.size());
@ -4103,16 +4102,38 @@ bool blockchain_storage::pop_asset_info(const crypto::public_key& asset_id)
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(avc.asset_op_history->size() != 0, false, "asset with id " << avc.asset_id << " has invalid history size() == 0");
if (is_hardfork_active(ZANO_HARDFORK_05)) // TODO: consider changing to height-specific check
{
if (last_ado.descriptor.owner_eth_pub_key.has_value())
{
CHECK_AND_ASSERT_MES(last_ado.descriptor.owner == null_pkey, false, "owner_eth_pub_key is set but owner pubkey is nonzero");
asset_operation_ownership_proof_eth aoop_eth{};
r = get_type_in_variant_container(avc.tx.proofs, aoop_eth);
CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed: asset_operation_ownership_proof_eth is missing");
if (!crypto::verify_eth_signature(avc.tx_id, last_ado.descriptor.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_ado.descriptor.owner, aoop.gss);
}
//------------------------------------------------------------------
bool blockchain_storage::validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const
@ -4124,11 +4145,12 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state
const asset_descriptor_operation& ado = avc.ado;
bool need_to_validate_ao_amount_commitment = true;
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER)
{
CHECK_AND_ASSERT_MES(!avc.asset_op_history, false, "asset with id " << avc.asset_id << " has already been registered");
avc.amount_to_validate = ado.descriptor.current_supply;
CHECK_AND_ASSERT_MES(validate_asset_operation_amount_commitment(avc), false, "validate_asset_operation_amount_commitment failed!");
if(this->is_hardfork_active(ZANO_HARDFORK_05))
{
CHECK_AND_ASSERT_MES(validate_ado_initial(ado.descriptor), false, "validate_ado_initial failed!");
@ -4136,48 +4158,48 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state
}
else
{
CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size(), false, "asset with id " << avc.asset_id << " has not been registered");
CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size() > 0, false, "asset with id " << avc.asset_id << " has not been registered");
const asset_descriptor_operation& last_ado = avc.asset_op_history->back();
// check ownership permission
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE /*|| ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN*/)
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
{
bool r = validate_ado_ownership(avc);
CHECK_AND_ASSERT_MES(r, false, "Faild to validate ownership of asset_descriptor_operation, rejecting");
CHECK_AND_ASSERT_MES(r, false, "Failed to validate ownership of asset_descriptor_operation, rejecting");
}
avc.amount_to_validate = 0;
bool need_to_validate_balance_proof = true;
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
{
//check that total current_supply haven't changed
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply == avc.asset_op_history->back().descriptor.current_supply, false, "update operation attempted to change emission, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "update operation attempted to change fileds that shouldn't be modified, failed");
need_to_validate_balance_proof = false;
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply == last_ado.descriptor.current_supply, false, "update operation attempted to change emission, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "update operation modifies asset descriptor in a prohibited manner");
need_to_validate_ao_amount_commitment = false;
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT)
{
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > avc.asset_op_history->back().descriptor.current_supply, false, "emit operation does not increase the current supply, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "emit operation is not allowed to update fields");
CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == avc.asset_op_history->back().descriptor.meta_info, false, "emit operation is not allowed to update meta info");
avc.amount_to_validate = ado.descriptor.current_supply - avc.asset_op_history->back().descriptor.current_supply;
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > last_ado.descriptor.current_supply, false, "emit operation does not increase the current supply, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "emit operation modifies asset descriptor in a prohibited manner");
CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_ado.descriptor.meta_info, false, "emit operation is not allowed to update meta info");
avc.amount_to_validate = ado.descriptor.current_supply - last_ado.descriptor.current_supply;
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN)
{
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < avc.asset_op_history->back().descriptor.current_supply, false, "burn operation does not decrease the current supply, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "burn operation is not allowed to update fields");
CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == avc.asset_op_history->back().descriptor.meta_info, false, "burn operation is not allowed to update meta info");
avc.amount_to_validate = avc.asset_op_history->back().descriptor.current_supply - ado.descriptor.current_supply;
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < last_ado.descriptor.current_supply, false, "burn operation does not decrease the current supply, failed");
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "burn operation modifies asset descriptor in a prohibited manner");
CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_ado.descriptor.meta_info, false, "burn operation is not allowed to update meta info");
avc.amount_to_validate = last_ado.descriptor.current_supply - ado.descriptor.current_supply;
}
else
{
LOG_ERROR("Unknown operation type: " << (int)ado.operation_type);
return false;
}
}
if (need_to_validate_balance_proof)
{
bool r = validate_asset_operation_amount_commitment(avc);
CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation");
}
if (need_to_validate_ao_amount_commitment)
{
bool r = validate_asset_operation_amount_commitment(avc);
CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation");
}
return true;
@ -5803,6 +5825,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 +5955,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;
}
//------------------------------------------------------------------

View file

@ -380,6 +380,7 @@ namespace currency
bool for_altchain,
const alt_chain_type& alt_chain = alt_chain_type(),
uint64_t split_height = 0)const;
bool validate_ado_ownership(asset_op_verification_context& avc) const;
bool validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const;
void set_core_runtime_config(const core_runtime_config& pc) const;

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"
@ -695,6 +696,9 @@ namespace currency
}
};
#define ASSET_DESCRIPTOR_BASE_STRUCTURE_VER 1
typedef boost::variant<crypto::public_key, crypto::eth_public_key> asset_owner_pub_key_v;
struct asset_descriptor_base
{
@ -706,9 +710,11 @@ namespace currency
std::string meta_info;
crypto::public_key owner = currency::null_pkey; // consider premultipling by 1/8
bool hidden_supply = false;
uint8_t version = 0;
boost::optional<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
BEGIN_VERSIONED_SERIALIZE(0, version)
uint8_t version = ASSET_DESCRIPTOR_BASE_STRUCTURE_VER;
BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_BASE_STRUCTURE_VER, version)
FIELD(total_max_supply)
FIELD(current_supply)
FIELD(decimal_point)
@ -717,9 +723,10 @@ namespace currency
FIELD(meta_info)
FIELD(owner)
FIELD(hidden_supply)
END_VERSION_UNDER(1)
FIELD(owner_eth_pub_key)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(total_max_supply)
BOOST_SERIALIZE(current_supply)
@ -729,17 +736,20 @@ namespace currency
BOOST_SERIALIZE(meta_info)
BOOST_SERIALIZE(owner)
BOOST_SERIALIZE(hidden_supply)
BOOST_END_VERSION_UNDER(1)
BOOST_SERIALIZE(owner_eth_pub_key)
END_BOOST_SERIALIZATION()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(total_max_supply) DOC_DSCR("Maximum possible supply for given asset, can't be changed after deployment") DOC_EXMP(1000000000000000000) DOC_END
KV_SERIALIZE(current_supply) DOC_DSCR("Currently emitted supply for given asset (ignored for REGISTER operation)") DOC_EXMP(500000000000000000) DOC_END
KV_SERIALIZE(decimal_point) DOC_DSCR("Decimal point") DOC_EXMP(12) DOC_END
KV_SERIALIZE(ticker) DOC_DSCR("Ticker associated with asset") DOC_EXMP("ZUSD") DOC_END
KV_SERIALIZE(full_name) DOC_DSCR("Full name of the asset") DOC_EXMP("Zano wrapped USD") DOC_END
KV_SERIALIZE(meta_info) DOC_DSCR("Any other information assetiaded with asset in a free form") DOC_EXMP("Stable and private") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Owner's key, used only for EMIT and UPDATE validation, could be changed by transferring asset ownership") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE(hidden_supply) DOC_DSCR("This one reserved for future use, will be documented later") DOC_END
KV_SERIALIZE(total_max_supply) DOC_DSCR("Maximum possible supply for a given asset, cannot be changed after deployment.") DOC_EXMP(1000000000000000000) DOC_END
KV_SERIALIZE(current_supply) DOC_DSCR("Currently emitted supply for the given asset (ignored for REGISTER operation).") DOC_EXMP(500000000000000000) DOC_END
KV_SERIALIZE(decimal_point) DOC_DSCR("Decimal point.") DOC_EXMP(12) DOC_END
KV_SERIALIZE(ticker) DOC_DSCR("Ticker associated with the asset.") DOC_EXMP("ZABC") DOC_END
KV_SERIALIZE(full_name) DOC_DSCR("Full name of the asset.") DOC_EXMP("Zano wrapped ABC") DOC_END
KV_SERIALIZE(meta_info) DOC_DSCR("Any other information associated with the asset in free form.") DOC_EXMP("Stable and private") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Owner's key, used only for EMIT and UPDATE validation, can be changed by transferring asset ownership.") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE(hidden_supply) DOC_DSCR("This field is reserved for future use and will be documented later.") DOC_END
KV_SERIALIZE_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()
};
@ -840,6 +850,26 @@ namespace currency
};
struct asset_operation_ownership_proof_eth
{
crypto::eth_signature eth_sig; // 64 bytes
uint8_t version = 0;
BEGIN_VERSIONED_SERIALIZE(0, version)
FIELD(eth_sig)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(eth_sig)
BOOST_SERIALIZE(version)
END_BOOST_SERIALIZATION()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_POD_AS_HEX_STRING(eth_sig) DOC_DSCR("HEX-encoded ETH signature (64 bytes)") DOC_EXMP("674bb56a5b4fa562e679ccacc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6add697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE(version) DOC_DSCR("Structure version") DOC_EXMP(0) DOC_END
END_KV_SERIALIZE_MAP()
};
struct extra_padding
{
std::vector<uint8_t> buff; //stub
@ -943,7 +973,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
@ -1202,6 +1232,10 @@ SET_VARIANT_TAGS(currency::zc_balance_proof, 48, "zc_balance_proof");
SET_VARIANT_TAGS(currency::asset_descriptor_operation, 49, "asset_descriptor_base");
SET_VARIANT_TAGS(currency::asset_operation_proof, 50, "asset_operation_proof");
SET_VARIANT_TAGS(currency::asset_operation_ownership_proof, 51, "asset_operation_ownership_proof");
SET_VARIANT_TAGS(currency::asset_operation_ownership_proof_eth, 52, "asset_operation_ownership_proof_eth");
SET_VARIANT_TAGS(crypto::eth_public_key, 60, "eth_public_key");
//SET_VARIANT_TAGS(crypto::eth_signature, 61, "eth_signature");s

View file

@ -258,25 +258,29 @@
#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 343
#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_05_AFTER_HEIGHT 241750
#define ZANO_HARDFORK_05_MIN_BUILD_VER 343
#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

@ -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;
@ -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{};
@ -2194,6 +2196,8 @@ namespace currency
hsc.add_scalar(crypto::scalar_t(ado.descriptor.total_max_supply));
hsc.add_scalar(crypto::scalar_t(ado.descriptor.decimal_point));
hsc.add_pub_key(ado.descriptor.owner);
if (ado.descriptor.owner_eth_pub_key.has_value())
hsc.add_eth_pub_key(ado.descriptor.owner_eth_pub_key.value());
crypto::hash h = hsc.calc_hash_no_reduce();
// this hash function needs to be computationally expensive (s.a. the whitepaper)
@ -2246,7 +2250,8 @@ namespace currency
// asset_control_key = Hs(CRYPTO_HDS_ASSET_CONTROL_KEY, 8 * tx_key.sec * sender_account_keys.account_address.spend_public_key, 0)
// ado.descriptor.owner = asset_control_key * G
ado.descriptor.owner = sender_account_keys.account_address.spend_public_key;
if (!ado.descriptor.owner_eth_pub_key.has_value())
ado.descriptor.owner = sender_account_keys.account_address.spend_public_key;
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(ado, &gen_context.ao_asset_id_pt, &gen_context.ao_asset_id), false, "get_or_calculate_asset_id failed");
@ -2335,25 +2340,6 @@ namespace currency
}
if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_handle_asset_descriptor_operation_before_seal{ &ado });
ftp.need_to_generate_ado_proof = true;
/*
//seal it with owners signature
crypto::signature sig = currency::null_sig;
crypto::hash h = get_signature_hash_for_asset_operation(ado);
if (ftp.pthirdparty_sign_handler)
{
bool r = ftp.pthirdparty_sign_handler->sign(h, ftp.ado_current_asset_owner, sig);
CHECK_AND_ASSERT_MES(r, false, "asset thirparty sign failed");
}
else
{
crypto::public_key pub_k = currency::null_pkey;
crypto::secret_key_to_public_key(sender_account_keys.spend_secret_key, pub_k);
CHECK_AND_ASSERT_MES(ftp.ado_current_asset_owner == pub_k, false, "asset owner key not matched with provided private key for asset operation signing");
crypto::generate_signature(h, pub_k, account_keys.spend_secret_key, sig);
}
ado.opt_proof = sig;
*/
}
return true;
}
@ -2603,8 +2589,7 @@ namespace currency
// ASSET oprations handling
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
{
asset_descriptor_operation* pado = nullptr;
pado = get_type_in_variant_container<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);
@ -2717,7 +2702,8 @@ namespace currency
// generate proofs and signatures
// (any changes made below should only affect the signatures/proofs and should not impact the prefix hash calculation)
//
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
result.tx_id = get_transaction_prefix_hash(tx);
const crypto::hash &tx_prefix_hash = result.tx_id;
// ring signatures (per-input proofs)
r = false;
@ -2773,35 +2759,30 @@ namespace currency
CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
tx.proofs.emplace_back(std::move(balance_proof));
// asset operation proof (if necessary)
// optional asset operation proofs: amount commitment proof (required for register, emit, public burn)
if (gen_context.ao_asset_id != currency::null_pkey)
{
// construct the asset operation proof
// TODO @#@# add support for hidden supply
// asset amount commitment g proof (TODO @#@# add support for hidden supply)
crypto::signature aop_g_sig{};
crypto::generate_signature(tx_prefix_hash, crypto::point_t(gen_context.ao_amount_blinding_mask * crypto::c_point_G).to_public_key(), gen_context.ao_amount_blinding_mask, aop_g_sig);
asset_operation_proof aop{};
aop.opt_amount_commitment_g_proof = aop_g_sig;
tx.proofs.emplace_back(std::move(aop));
}
if(ftp.need_to_generate_ado_proof)
{
asset_operation_ownership_proof aoop = AUTO_VAL_INIT(aoop);
if (ftp.pthirdparty_sign_handler)
// optional asset operation proofs: ownership proof for standard (non-eth) owner (using generic Shnorr signature with the spend secret key)
const asset_descriptor_operation* pado = get_type_in_variant_container<asset_descriptor_operation>(tx.extra);
if (pado != nullptr)
{
if ((pado->operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || pado->operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) &&
!pado->descriptor.owner_eth_pub_key.has_value())
{
//ask third party to generate proof
r = ftp.pthirdparty_sign_handler->sign(tx_prefix_hash, ftp.ado_current_asset_owner, aoop.gss);
CHECK_AND_ASSERT_MES(r, false, "Failed to sign ado by thirdparty");
}
else
{
//generate signature by wallet account
r = crypto::generate_schnorr_sig(tx_prefix_hash, ftp.ado_current_asset_owner, sender_account_keys.spend_secret_key, aoop.gss);
asset_operation_ownership_proof aoop{};
r = crypto::generate_schnorr_sig(tx_prefix_hash, sender_account_keys.spend_secret_key, aoop.gss);
CHECK_AND_ASSERT_MES(r, false, "Failed to sign ado proof");
if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_after_asset_ownership_proof_generated{ &aoop });
tx.proofs.emplace_back(aoop);
}
if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_after_asset_ownership_proof_generated{ &aoop });
tx.proofs.emplace_back(aoop);
}
}

View file

@ -139,11 +139,13 @@ namespace currency
bool hltc_our_out_is_before_expiration;
};
struct thirdparty_sign_handler
struct asset_eth_signer_i
{
virtual bool sign(const crypto::hash& h, const crypto::public_key& owner_public_key, crypto::generic_schnorr_sig& sig);
virtual bool sign(const crypto::hash& h, const crypto::eth_public_key& asset_owner, crypto::eth_signature& sig) = 0;
};
typedef boost::variant<crypto::public_key, crypto::eth_public_key> asset_owner_key_v;
struct finalize_tx_param
{
uint64_t unlock_time;
@ -165,10 +167,6 @@ namespace currency
epee::misc_utils::events_dispatcher* pevents_dispatcher;
tx_generation_context gen_context{}; // solely for consolidated txs
//crypto::secret_key asset_control_key = currency::null_skey;
crypto::public_key ado_current_asset_owner = null_pkey;
thirdparty_sign_handler* pthirdparty_sign_handler = nullptr;
mutable bool need_to_generate_ado_proof = false;
BEGIN_SERIALIZE_OBJECT()
@ -191,14 +189,13 @@ namespace currency
{
FIELD(gen_context);
}
FIELD(ado_current_asset_owner)
FIELD(need_to_generate_ado_proof)
END_SERIALIZE()
};
struct finalized_tx
{
currency::transaction tx;
crypto::hash tx_id;
crypto::secret_key one_time_key;
finalize_tx_param ftp;
std::string htlc_origin;
@ -208,6 +205,7 @@ namespace currency
BEGIN_SERIALIZE_OBJECT()
FIELD(tx)
FIELD(tx_id)
FIELD(one_time_key)
FIELD(ftp)
FIELD(htlc_origin)
@ -546,7 +544,7 @@ namespace currency
{
assets_list.push_back(currency::asset_descriptor_with_id());
assets_list.back().asset_id = pr.first;
epee::misc_utils::cast_assign_a_to_b(assets_list.back(), static_cast<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;
}
}
@ -948,6 +946,12 @@ 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);

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,13 +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
{
@ -96,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;
@ -110,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
@ -129,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;
@ -989,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);
@ -1003,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

@ -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 748e8e96d8f2653e6e698a11f67c172c1f84c2b2
Subproject commit 5c878005ace55484eafe2985d204cd51e90b203b

View file

@ -523,7 +523,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 +1391,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)
{
@ -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))
@ -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))
@ -1308,7 +1376,7 @@ namespace currency
LOCAL_CHECK(req.address != account_public_address{}, "address is missing");
LOCAL_CHECK(req.viewkey != null_skey, "viewkey is missing");
LOCAL_CHECK(0 <= req.blocks_limit && req.blocks_limit <= 5, "blocks_limit is out of allowed bounds");
LOCAL_CHECK(req.blocks_limit <= 5, "blocks_limit is out of allowed bounds");
// verify addess keys
crypto::point_t view_pk, spend_pk;
@ -1332,7 +1400,7 @@ namespace currency
{
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, req.blocks_limit, recent_blocks), "cannot get 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)

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,6 +89,7 @@ namespace currency
bool on_get_votes(const COMMAND_RPC_GET_VOTES::request& req, COMMAND_RPC_GET_VOTES::response& res, connection_context& cntx);
bool on_get_asset_info(const COMMAND_RPC_GET_ASSET_INFO::request& req, COMMAND_RPC_GET_ASSET_INFO::response& res, connection_context& cntx);
bool on_get_assets_list(const COMMAND_RPC_GET_ASSETS_LIST::request& req, COMMAND_RPC_GET_ASSETS_LIST::response& res, connection_context& cntx);
bool on_decrypt_tx_details(const COMMAND_RPC_DECRYPT_TX_DETAILS::request& req, COMMAND_RPC_DECRYPT_TX_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_main_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_alt_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
@ -153,9 +154,11 @@ namespace currency
MAP_JON_RPC ("getrandom_outs1", on_get_random_outs1, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
MAP_JON_RPC ("getrandom_outs3", on_get_random_outs3, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3)
MAP_JON_RPC ("get_votes", on_get_votes, COMMAND_RPC_GET_VOTES)
//assets api
MAP_JON_RPC ("get_asset_info", on_get_asset_info, COMMAND_RPC_GET_ASSET_INFO)
MAP_JON_RPC ("get_assets_list", on_get_assets_list, COMMAND_RPC_GET_ASSETS_LIST)
MAP_JON_RPC_WE("decrypt_tx_details", on_decrypt_tx_details, COMMAND_RPC_DECRYPT_TX_DETAILS)
MAP_JON_RPC_WE("get_main_block_details", on_get_main_block_details, COMMAND_RPC_GET_BLOCK_DETAILS)
MAP_JON_RPC_WE("get_alt_block_details", on_get_alt_block_details, COMMAND_RPC_GET_BLOCK_DETAILS)

View file

@ -179,6 +179,58 @@ namespace currency
};
};
struct COMMAND_RPC_DECRYPT_TX_DETAILS
{
DOC_COMMAND("Decrypts transaction private information. Should be used only with your own local daemon for security reasons.");
struct request
{
std::string tx_id;
currency::blobdata tx_blob;
crypto::secret_key tx_secret_key;
std::vector<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");
@ -636,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()
};
};
//-----------------------------------------------
@ -672,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

@ -125,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", "");
@ -2095,11 +2096,11 @@ bool simple_wallet::deploy_new_asset(const std::vector<std::string> &args)
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
@ -2874,7 +2875,63 @@ 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()
{
@ -3144,6 +3201,8 @@ int main(int argc, char* argv[])
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, arg_seed_doctor);
command_line::add_arg(desc_params, arg_derive_custom_seed);
tools::wallet_rpc_server::init_options(desc_params);
@ -3221,12 +3280,15 @@ 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))
{

View file

@ -194,7 +194,6 @@ namespace currency
std::string m_restore_wallet;
std::string m_voting_config_file;
bool m_no_password_confirmations = false;
crypto::hash m_password_hash;
uint64_t m_password_salt;

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 337
#define PROJECT_VERSION_BUILD_NO 349
#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

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

@ -129,7 +129,7 @@ namespace tools
//----------------------------------------------------------------------------------------------------
std::string wallet2::transfer_flags_to_str(uint32_t flags)
{
std::string result(5, ' ');
std::string result(7, ' ');
if (flags & WALLET_TRANSFER_DETAIL_FLAG_SPENT)
result[0] = 's';
if (flags & WALLET_TRANSFER_DETAIL_FLAG_BLOCKED)
@ -140,6 +140,10 @@ std::string wallet2::transfer_flags_to_str(uint32_t flags)
result[3] = 'm';
if (flags & WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION)
result[4] = 'c';
if (flags & WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM)
result[5] = 'h';
if (flags & WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION)
result[6] = 'a';
return result;
}
//----------------------------------------------------------------------------------------------------
@ -390,6 +394,10 @@ const crypto::public_key& wallet2::out_get_pub_key(const currency::tx_out_v& out
//----------------------------------------------------------------------------------------------------
void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_operation& ado, process_transaction_context& ptc)
{
auto print_ado_owner = [ado](std::ostream& o){
ado.descriptor.owner_eth_pub_key.has_value() ? o << ado.descriptor.owner_eth_pub_key.value() << " (ETH)" : o << ado.descriptor.owner;
};
do
{
crypto::public_key asset_id{};
@ -398,17 +406,22 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER)
{
if (ado.descriptor.owner != m_account.get_public_address().spend_public_key)
// Add an asset to ownership list if either:
// 1) we're the owner of the asset;
// or
// 2) we spent native coins in the tx (i.e. we sent it) AND it registers an asset with third-party ownership.
if (ado.descriptor.owner != m_account.get_public_address().spend_public_key &&
(!ado.descriptor.owner_eth_pub_key.has_value() || !ptc.spent_own_native_inputs))
break;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(m_own_asset_descriptors.count(asset_id) == 0, "asset with asset_id " << asset_id << " has already been registered in the wallet as own asset");
wallet_own_asset_context& asset_context = m_own_asset_descriptors[asset_id];
epee::misc_utils::cast_assign_a_to_b(asset_context, ado.descriptor);
//*static_cast<asset_descriptor_base*>(&asset_context) = ado.descriptor;
epee::misc_utils::cast_assign_a_to_b(ado.descriptor, asset_context);
std::stringstream ss;
ss << "New Asset Registered:"
<< ENDL << "asset id: " << asset_id
<< ENDL << "Owner: " << print_ado_owner
<< ENDL << "Name: " << asset_context.full_name
<< ENDL << "Ticker: " << asset_context.ticker
<< ENDL << "Total Max Supply: " << print_asset_money(asset_context.total_max_supply, asset_context.decimal_point)
@ -428,8 +441,8 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op
break;
//asset had been updated
add_rollback_event(ptc.height, asset_update_event{ it->first, it->second });
epee::misc_utils::cast_assign_a_to_b(it->second, ado.descriptor);
epee::misc_utils::cast_assign_a_to_b(ado.descriptor, it->second);
}
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
{
@ -441,7 +454,7 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op
// ownership of the asset acquired
wallet_own_asset_context& asset_context = m_own_asset_descriptors[asset_id];
epee::misc_utils::cast_assign_a_to_b(asset_context, ado.descriptor);
epee::misc_utils::cast_assign_a_to_b(ado.descriptor, asset_context);
std::stringstream ss;
ss << "Asset ownership acquired:"
@ -466,8 +479,8 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op
}
else
{
//update event for asset that we control, check if ownership is still ours
if (ado.descriptor.owner != m_account.get_public_address().spend_public_key && !it->second.thirdparty_custody)
// check our ownership status: we lost it if the asset has new non-null owner (null means a third-party ownership, and in such a case we retain it in the own list whatever happens)
if (ado.descriptor.owner != null_pkey && ado.descriptor.owner != m_account.get_public_address().spend_public_key)
{
//ownership of the asset had been transfered
add_rollback_event(ptc.height, asset_unown_event{ it->first, it->second });
@ -476,7 +489,7 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op
std::stringstream ss;
ss << "Asset ownership lost:"
<< ENDL << "asset id: " << asset_id
<< ENDL << "New owner: " << ado.descriptor.owner
<< ENDL << "New owner: " << print_ado_owner
<< ENDL << "Name: " << ado.descriptor.full_name
<< ENDL << "Ticker: " << ado.descriptor.ticker
<< ENDL << "Total Max Supply: " << print_asset_money(ado.descriptor.total_max_supply, ado.descriptor.decimal_point)
@ -492,8 +505,7 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op
{
//just an update of the asset
add_rollback_event(ptc.height, asset_update_event{ it->first, it->second });
epee::misc_utils::cast_assign_a_to_b(it->second, ado.descriptor);
epee::misc_utils::cast_assign_a_to_b(ado.descriptor, it->second);
}
}
}
@ -1151,7 +1163,7 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept
throw;
}
print_tx_sent_message(tx, "(contract <" + epee::string_tools::pod_to_hex(contract_id) + ">)", construct_param.fee);
print_tx_sent_message(tx, "contract <" + epee::string_tools::pod_to_hex(contract_id) + ">", true, construct_param.fee);
if (p_acceptance_tx != nullptr)
*p_acceptance_tx = tx;
@ -1290,7 +1302,7 @@ void wallet2::request_cancel_contract(const crypto::hash& contract_id, uint64_t
throw;
}
print_tx_sent_message(tx, "(transport for cancel proposal)", fee);
print_tx_sent_message(tx, "transport for cancel proposal", true, fee);
if (p_cancellation_proposal_tx != nullptr)
*p_cancellation_proposal_tx = tx;
@ -3800,13 +3812,10 @@ bool wallet2::balance(std::unordered_map<crypto::public_key, wallet_public::asse
for (const auto& emp_entry : utx.second.employed_entries.receive)
{
auto it_employed_entry = subtransfers_by_assets_map.find(emp_entry.asset_id);
if (it_employed_entry == subtransfers_by_assets_map.end())
{
LOG_ERROR("Intenral error, check the wallet code at give location");
continue;
}
if (!(it_employed_entry->second)) // if is_incoming == false, then we need to check for change and add it to total
if (it_employed_entry == subtransfers_by_assets_map.end() || !(it_employed_entry->second)) // if is_incoming == false, then we need to check for change and add it to total
{
//it_employed_entry == subtransfers_by_assets_map.end() is a case when amount sent exactly equal amount received (someone producing more outputs for example)
//still need to add to total as it is a change
wallet_public::asset_balance_entry_base& e = balances[emp_entry.asset_id];
e.total += emp_entry.amount;
}
@ -4103,7 +4112,7 @@ bool wallet2::generate_utxo_defragmentation_transaction_if_needed(currency::tran
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/, bool show_only_unknown /*= false*/, const std::string& filter_asset_ticker /*= std::string{}*/) const
{
static const char* header = " index amount ticker g_index flags block tx out# asset id";
static const char* header = " index amount ticker g_index flags block tx out# asset id";
std::stringstream ss;
ss << header << ENDL;
size_t count = 0;
@ -4136,7 +4145,7 @@ std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool inclu
std::setw(6) << std::left << (native_coin ? std::string(" ") : adb.ticker) << " " << std::right <<
std::setw(7) << td.m_global_output_index << " " <<
std::setw(2) << std::setfill('0') << td.m_flags << std::setfill(' ') << ":" <<
std::setw(5) << transfer_flags_to_str(td.m_flags) << " " <<
std::setw(7) << transfer_flags_to_str(td.m_flags) << " " <<
std::setw(7) << td.m_ptx_wallet_info->m_block_height << " " <<
get_transaction_hash(td.m_ptx_wallet_info->m_tx) << " " <<
std::setw(4) << td.m_internal_output_index << " ";
@ -4400,6 +4409,39 @@ bool wallet2::get_utxo_distribution(std::map<uint64_t, uint64_t>& distribution)
return false;
}
//----------------------------------------------------------------------------------------------------
void wallet2::submit_externally_signed_asset_tx(const finalized_tx& ft, const crypto::eth_signature& eth_sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked)
{
transaction tx = ft.tx;
currency::asset_operation_ownership_proof_eth aoop_eth{};
aoop_eth.eth_sig = eth_sig;
tx.proofs.push_back(std::move(aoop_eth));
// foolproof
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(ft.ftp.spend_pub_key == m_account.get_keys().account_address.spend_public_key, "The given tx was created in a different wallet, keys missmatch, tx hash: " << ft.tx_id);
try
{
send_transaction_to_network(tx);
}
catch (...)
{
// clear transfers flags if smth went wrong and it was requested
if (unlock_transfers_on_fail)
{
uint32_t flag = WALLET_TRANSFER_DETAIL_FLAG_SPENT | WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION;
clear_transfers_from_flag(ft.ftp.selected_transfers, flag, "broadcasting tx " + epee::string_tools::pod_to_hex(ft.tx_id) + " was unsuccessful");
transfers_unlocked = true;
}
throw;
}
m_tx_keys.insert(std::make_pair(ft.tx_id, ft.one_time_key));
add_sent_tx_detailed_info(tx, ft.ftp.attachments, ft.ftp.prepared_destinations, ft.ftp.selected_transfers);
print_tx_sent_message(tx, "from submit_externally_signed_asset_tx", true, get_tx_fee(tx));
}
//----------------------------------------------------------------------------------------------------
void wallet2::submit_transfer(const std::string& signed_tx_blob, currency::transaction& tx)
{
// decrypt sources
@ -4490,7 +4532,7 @@ void wallet2::submit_transfer(const std::string& signed_tx_blob, currency::trans
}
// TODO: print inputs' key images
print_tx_sent_message(tx, "(from submit_transfer)");
print_tx_sent_message(tx, "from submit_transfer", true);
}
//----------------------------------------------------------------------------------------------------
void wallet2::submit_transfer_files(const std::string& signed_tx_file, currency::transaction& tx)
@ -5461,125 +5503,159 @@ void wallet2::request_alias_registration(currency::extra_alias_entry& ai, curren
transfer(destinations, 0, 0, fee, extra, attachments, get_current_split_strategy(), tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx, CURRENCY_TO_KEY_OUT_RELAXED, false);
}
//----------------------------------------------------------------------------------------------------
void wallet2::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 wallet2::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)
{
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(asset_info.decimal_point <= 18, "too big decimal point: " << asset_info.decimal_point);
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(asset_info.decimal_point <= 18, "too big decimal point: " << (int)asset_info.decimal_point);
asset_descriptor_operation asset_reg_info = AUTO_VAL_INIT(asset_reg_info);
asset_descriptor_operation asset_reg_info{};
asset_reg_info.descriptor = asset_info;
asset_reg_info.operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER;
construct_tx_param ctp = get_default_construct_tx_param();
ctp.dsts = destinations;
ctp.extra.push_back(asset_reg_info);
ctp.need_at_least_1_zc = true;
ctp.tx_meaning_for_logs = "asset registration";
finalized_tx ft = AUTO_VAL_INIT(ft);
this->transfer(ctp, ft, true, nullptr);
result_tx = ft.tx;
//get generated asset id
currency::asset_descriptor_operation ado = AUTO_VAL_INIT(ado);
bool r = get_type_in_variant_container(result_tx.extra, ado);
CHECK_AND_ASSERT_THROW_MES(r, "Failed find asset info in tx");
currency::asset_descriptor_operation ado{};
bool r = get_type_in_variant_container(ft.tx.extra, ado);
CHECK_AND_ASSERT_THROW_MES(r, "asset_descriptor_operation cannot be found in tx extra as expected");
CHECK_AND_ASSERT_THROW_MES(get_or_calculate_asset_id(ado, nullptr, &new_asset_id), "get_or_calculate_asset_id failed");
m_custom_assets[new_asset_id] = ado.descriptor;
}
//----------------------------------------------------------------------------------------------------
void wallet2::emit_asset(const crypto::public_key asset_id, std::vector<currency::tx_destination_entry>& destinations, currency::transaction& result_tx)
void wallet2::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)
{
finalized_tx ft{};
deploy_new_asset(asset_info, destinations, ft, new_asset_id);
result_tx = ft.tx;
}
//----------------------------------------------------------------------------------------------------
void wallet2::emit_asset(const crypto::public_key& asset_id, const std::vector<currency::tx_destination_entry>& destinations, currency::finalized_tx& ft)
{
auto own_asset_entry_it = m_own_asset_descriptors.find(asset_id);
CHECK_AND_ASSERT_THROW_MES(own_asset_entry_it != m_own_asset_descriptors.end(), "Failed find asset_id " << asset_id << " in own assets list");
COMMAND_RPC_GET_ASSET_INFO::request req;
req.asset_id = asset_id;
COMMAND_RPC_GET_ASSET_INFO::response rsp;
bool r = m_core_proxy->call_COMMAND_RPC_GET_ASSET_INFO(req, rsp);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to call_COMMAND_RPC_GET_ASSET_INFO");
currency::asset_descriptor_base last_adb{};
bool r = daemon_get_asset_info(asset_id, last_adb);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to get asset info from daemon");
asset_descriptor_operation asset_emmit_info = AUTO_VAL_INIT(asset_emmit_info);
asset_emmit_info.descriptor = rsp.asset_descriptor;
asset_descriptor_operation asset_emmit_info{};
asset_emmit_info.descriptor = last_adb;
asset_emmit_info.operation_type = ASSET_DESCRIPTOR_OPERATION_EMIT;
asset_emmit_info.opt_asset_id = asset_id;
construct_tx_param ctp = get_default_construct_tx_param();
ctp.dsts = destinations;
ctp.extra.push_back(asset_emmit_info);
ctp.need_at_least_1_zc = true;
ctp.ado_current_asset_owner = rsp.asset_descriptor.owner;
//ctp.asset_deploy_control_key = own_asset_entry_it->second.control_key;
ctp.tx_meaning_for_logs = "asset emission";
for (auto& dst : ctp.dsts)
bool send_to_network = true;
if (last_adb.owner_eth_pub_key.has_value())
{
dst.asset_id = null_pkey; // emit operation requires null_pkey for emitting asset outputs, fix it ad-hoc here
send_to_network = false;
ctp.additional_transfer_flags_to_mark = WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION;
ctp.tx_meaning_for_logs = "asset eth emission";
}
finalized_tx ft = AUTO_VAL_INIT(ft);
this->transfer(ctp, ft, true, nullptr);
this->transfer(ctp, ft, send_to_network, nullptr);
}
//----------------------------------------------------------------------------------------------------
void wallet2::emit_asset(const crypto::public_key& asset_id, std::vector<currency::tx_destination_entry>& destinations, currency::transaction& result_tx)
{
finalized_tx ft{};
emit_asset(asset_id, destinations, ft);
result_tx = ft.tx;
}
//----------------------------------------------------------------------------------------------------
void wallet2::update_asset(const crypto::public_key asset_id, const currency::asset_descriptor_base new_descriptor, currency::transaction& result_tx)
void wallet2::update_asset(const crypto::public_key& asset_id, const currency::asset_descriptor_base& new_descriptor, currency::finalized_tx& ft)
{
auto own_asset_entry_it = m_own_asset_descriptors.find(asset_id);
CHECK_AND_ASSERT_THROW_MES(own_asset_entry_it != m_own_asset_descriptors.end(), "Failed find asset_id " << asset_id << " in own assets list");
currency::asset_descriptor_base last_adb{};
bool r = daemon_get_asset_info(asset_id, last_adb);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to get asset info from daemon");
asset_descriptor_operation asset_update_info = AUTO_VAL_INIT(asset_update_info);
asset_descriptor_operation asset_update_info{};
asset_update_info.descriptor = new_descriptor;
asset_update_info.operation_type = ASSET_DESCRIPTOR_OPERATION_UPDATE;
asset_update_info.opt_asset_id = asset_id;
construct_tx_param ctp = get_default_construct_tx_param();
ctp.extra.push_back(asset_update_info);
ctp.need_at_least_1_zc = true;
currency::asset_descriptor_base adb = AUTO_VAL_INIT(adb);
bool r = this->daemon_get_asset_info(asset_id, adb);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to get asset info from daemon");
ctp.ado_current_asset_owner = adb.owner;
ctp.tx_meaning_for_logs = "asset update";
finalized_tx ft = AUTO_VAL_INIT(ft);
this->transfer(ctp, ft, true, nullptr);
bool send_to_network = true;
if (last_adb.owner_eth_pub_key.has_value())
{
send_to_network = false;
ctp.additional_transfer_flags_to_mark = WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION;
ctp.tx_meaning_for_logs = "asset eth update";
}
this->transfer(ctp, ft, send_to_network, nullptr);
}
//----------------------------------------------------------------------------------------------------
void wallet2::update_asset(const crypto::public_key& asset_id, const currency::asset_descriptor_base new_descriptor, currency::transaction& result_tx)
{
currency::finalized_tx ft{};
update_asset(asset_id, new_descriptor, ft);
result_tx = ft.tx;
}
//----------------------------------------------------------------------------------------------------
void wallet2::transfer_asset_ownership(const crypto::public_key asset_id, const crypto::public_key& new_owner, currency::transaction& result_tx)
void wallet2::transfer_asset_ownership(const crypto::public_key& asset_id, const currency::asset_owner_pub_key_v& new_owner_v, currency::finalized_tx& ft)
{
auto own_asset_entry_it = m_own_asset_descriptors.find(asset_id);
CHECK_AND_ASSERT_THROW_MES(own_asset_entry_it != m_own_asset_descriptors.end(), "Failed find asset_id " << asset_id << " in own assets list");
currency::asset_descriptor_base adb = AUTO_VAL_INIT(adb);
bool r = this->daemon_get_asset_info(asset_id, adb);
currency::asset_descriptor_base last_adb{};
bool r = this->daemon_get_asset_info(asset_id, last_adb);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to get asset info from daemon");
asset_descriptor_operation asset_update_info = AUTO_VAL_INIT(asset_update_info);
asset_update_info.descriptor = adb;
asset_descriptor_operation asset_update_info{};
asset_update_info.descriptor = last_adb;
asset_update_info.operation_type = ASSET_DESCRIPTOR_OPERATION_UPDATE;
asset_update_info.opt_asset_id = asset_id;
asset_update_info.descriptor.owner = new_owner;
construct_tx_param ctp = get_default_construct_tx_param();
ctp.ado_current_asset_owner = adb.owner;
ctp.extra.push_back(asset_update_info);
finalized_tx ft = AUTO_VAL_INIT(ft);
this->transfer(ctp, ft, true, nullptr);
if (new_owner_v.type() == typeid(crypto::public_key))
asset_update_info.descriptor.owner = boost::get<crypto::public_key>(new_owner_v);
else
asset_update_info.descriptor.owner_eth_pub_key = boost::get<crypto::eth_public_key>(new_owner_v);
construct_tx_param ctp = get_default_construct_tx_param();
ctp.extra.push_back(asset_update_info);
ctp.tx_meaning_for_logs = "transfer asset ownership";
bool send_to_network = true;
if (last_adb.owner_eth_pub_key.has_value())
{
send_to_network = false;
ctp.additional_transfer_flags_to_mark = WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION;
ctp.tx_meaning_for_logs = "transfer asset eth ownership";
}
this->transfer(ctp, ft, send_to_network, nullptr);
}
//----------------------------------------------------------------------------------------------------
void wallet2::transfer_asset_ownership(const crypto::public_key& asset_id, const currency::asset_owner_pub_key_v& new_owner_v, currency::transaction& result_tx)
{
finalized_tx ft{};
transfer_asset_ownership(asset_id, new_owner_v, ft);
result_tx = ft.tx;
}
//----------------------------------------------------------------------------------------------------
void wallet2::burn_asset(const crypto::public_key asset_id, uint64_t amount_to_burn, currency::transaction& result_tx)
void wallet2::burn_asset(const crypto::public_key& asset_id, uint64_t amount_to_burn, currency::finalized_tx& ft)
{
//auto own_asset_entry_it = m_own_asset_descriptors.find(asset_id);
//CHECK_AND_ASSERT_THROW_MES(own_asset_entry_it != m_own_asset_descriptors.end(), "Failed find asset_id " << asset_id << " in own assets list");
COMMAND_RPC_GET_ASSET_INFO::request req;
req.asset_id = asset_id;
COMMAND_RPC_GET_ASSET_INFO::response rsp;
bool r = m_core_proxy->call_COMMAND_RPC_GET_ASSET_INFO(req, rsp);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to call_COMMAND_RPC_GET_ASSET_INFO");
currency::asset_descriptor_base last_adb{};
bool r = this->daemon_get_asset_info(asset_id, last_adb);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to get asset info from daemon");
asset_descriptor_operation asset_burn_info{};
asset_burn_info.descriptor = last_adb;
CHECK_AND_ASSERT_THROW_MES(last_adb.current_supply >= amount_to_burn, "amount_to_burn is incorrect: " << print_money_brief(amount_to_burn, last_adb.decimal_point) << ", current_supply: " << print_money_brief(last_adb.current_supply, last_adb.decimal_point));
asset_descriptor_operation asset_burn_info = AUTO_VAL_INIT(asset_burn_info);
asset_burn_info.descriptor = rsp.asset_descriptor;
CHECK_AND_ASSERT_THROW_MES(asset_burn_info.descriptor.current_supply >= amount_to_burn, "Wrong amount to burn (current_supply" << asset_burn_info.descriptor.current_supply << " is less then " << amount_to_burn << ")");
currency::tx_destination_entry dst_to_burn = AUTO_VAL_INIT(dst_to_burn);
currency::tx_destination_entry dst_to_burn{};
dst_to_burn.amount = amount_to_burn;
dst_to_burn.asset_id = asset_id;
@ -5588,11 +5664,16 @@ void wallet2::burn_asset(const crypto::public_key asset_id, uint64_t amount_to_b
construct_tx_param ctp = get_default_construct_tx_param();
ctp.extra.push_back(asset_burn_info);
ctp.need_at_least_1_zc = true;
ctp.ado_current_asset_owner = rsp.asset_descriptor.owner;
ctp.dsts.push_back(dst_to_burn);
ctp.tx_meaning_for_logs = "asset burn";
finalized_tx ft = AUTO_VAL_INIT(ft);
this->transfer(ctp, ft, true, nullptr);
}
//----------------------------------------------------------------------------------------------------
void wallet2::burn_asset(const crypto::public_key& asset_id, uint64_t amount_to_burn, currency::transaction& result_tx)
{
finalized_tx ft{};
burn_asset(asset_id, amount_to_burn, ft);
result_tx = ft.tx;
}
//----------------------------------------------------------------------------------------------------
@ -6069,7 +6150,7 @@ void wallet2::send_escrow_proposal(const bc_services::contract_private_details&
mark_transfers_as_spent(ftp.selected_transfers, std::string("escrow proposal sent, tx <") + epee::string_tools::pod_to_hex(get_transaction_hash(tx)) + ">, contract: " + epee::string_tools::pod_to_hex(ms_id));
add_sent_tx_detailed_info(tx, ftp.attachments, ftp.prepared_destinations, ftp.selected_transfers);
print_tx_sent_message(tx, "(from multisig)", fee);
print_tx_sent_message(tx, "from multisig", true, fee);
}
//----------------------------------------------------------------------------------------------------
void wallet2::create_htlc_proposal(uint64_t amount, const currency::account_public_address& addr, uint64_t lock_blocks_count, currency::transaction& tx, const crypto::hash& htlc_hash, std::string& origin)
@ -7540,20 +7621,20 @@ void wallet2::set_genesis(const crypto::hash& genesis_hash)
m_chain.set_genesis(genesis_hash);
}
//----------------------------------------------------------------------------------------------------
void wallet2::print_tx_sent_message(const currency::transaction& tx, const std::string& description, uint64_t fee /* = UINT64_MAX */)
void wallet2::print_tx_sent_message(const currency::transaction& tx, const std::string& description, bool broadcasted, uint64_t fee /* = UINT64_MAX */)
{
//uint64_t balance_unlocked = 0;
//uint64_t balance_total = balance(balance_unlocked);
std::stringstream ss;
if (fee != UINT64_MAX)
ss << "Commission: " << std::setw(21) << std::right << print_money(fee) << ENDL;
ss << "Fee: " << std::setw(21) << std::right << print_money_brief(fee) << ENDL;
WLT_LOG_CYAN("Transaction " << get_transaction_hash(tx) << " was successfully sent " << description << ENDL
WLT_LOG_CYAN("Transaction " << get_transaction_hash(tx) << " was successfully " << (broadcasted ? "sent" : "created") << " (" << description << ")" << ENDL
<< ss.str()
// << "Balance: " << std::setw(21) << print_money(balance_total) << ENDL
// << "Unlocked: " << std::setw(21) << print_money(balance_unlocked) << ENDL
<< "Please, wait for confirmation for your balance to be unlocked.",
// << "Balance: " << std::setw(21) << print_money(balance_total) << ENDL
// << "Unlocked: " << std::setw(21) << print_money(balance_unlocked) << ENDL
<< (broadcasted ? "Please wait for the transaction to be confirmed before your balance is unlocked." : ""),
LOG_LEVEL_0);
}
//----------------------------------------------------------------------------------------------------
@ -7724,8 +7805,8 @@ bool wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx
{
WLT_THROW_IF_FALSE_WITH_CODE(false, "WALLET_RPC_ERROR_CODE_WRONG_MIXINS_FOR_AUDITABLE_WALLET", "WALLET_RPC_ERROR_CODE_WRONG_MIXINS_FOR_AUDITABLE_WALLET");
}
ftp.ado_current_asset_owner = ctp.ado_current_asset_owner;
ftp.pthirdparty_sign_handler = ctp.pthirdparty_sign_handler;
//ftp.asset_owner = ctp.asset_owner;
//ftp.p_eth_signer = ctp.p_eth_signer;
//
// TODO @#@# need to do refactoring over this part to support hidden amounts and asset_id
//
@ -7858,7 +7939,7 @@ void wallet2::finalize_transaction(currency::finalize_tx_param& ftp, currency::f
THROW_IF_FALSE_WALLET_EX_MES(tx_blob_size < CURRENCY_MAX_TRANSACTION_BLOB_SIZE, error::tx_too_big, "Transaction size: " << tx_blob_size << " bytes, transaction size limit: " << CURRENCY_MAX_TRANSACTION_BLOB_SIZE << " bytes.");
if (store_tx_secret_key)
m_tx_keys.insert(std::make_pair(get_transaction_hash(result.tx), result.one_time_key));
m_tx_keys.insert(std::make_pair(result.tx_id, result.one_time_key));
//TIME_MEASURE_START(send_transaction_to_network_time);
if (broadcast_tx)
@ -8036,8 +8117,11 @@ void wallet2::transfer(construct_tx_param& ctp,
return;
}
std::string tx_description = ctp.tx_meaning_for_logs.empty() ? std::string("transfer") : ctp.tx_meaning_for_logs;
uint32_t transfers_flags = ctp.additional_transfer_flags_to_mark | WALLET_TRANSFER_DETAIL_FLAG_SPENT;
TIME_MEASURE_START(mark_transfers_as_spent_time);
mark_transfers_as_spent(ftp.selected_transfers, std::string("money transfer, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(result.tx)));
mark_transfers_with_flag(ftp.selected_transfers, transfers_flags, std::string("preparing for ") + tx_description);
TIME_MEASURE_FINISH(mark_transfers_as_spent_time);
TIME_MEASURE_START(finalize_transaction_time);
@ -8047,7 +8131,7 @@ void wallet2::transfer(construct_tx_param& ctp,
}
catch (...)
{
clear_transfers_from_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_SPENT, std::string("exception on money transfer, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(result.tx)));
clear_transfers_from_flag(ftp.selected_transfers, transfers_flags, std::string("exception during") + tx_description + ", tx (maybe incorrect if tx is incomplete): " + epee::string_tools::pod_to_hex(get_transaction_hash(result.tx)));
throw;
}
TIME_MEASURE_FINISH(finalize_transaction_time);
@ -8060,7 +8144,7 @@ void wallet2::transfer(construct_tx_param& ctp,
<< ", mark_transfers_as_spent_time: " << print_fixed_decimal_point(mark_transfers_as_spent_time, 3)
, LOG_LEVEL_0);
print_tx_sent_message(result.tx, std::string() + "(transfer)", ctp.fee);
print_tx_sent_message(result.tx, tx_description, send_to_network, ctp.fee);
}
//----------------------------------------------------------------------------------------------------
@ -8349,7 +8433,7 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public
{
crypto::secret_key sk{};
finalize_transaction(ftp, *p_tx, sk, true);
print_tx_sent_message(*p_tx, "(sweep_below)", get_tx_fee(*p_tx));
print_tx_sent_message(*p_tx, "sweep_below", true, get_tx_fee(*p_tx));
}
catch (...)
{

View file

@ -438,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);
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);
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);
@ -609,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);
@ -859,7 +866,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,

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>
@ -53,6 +52,7 @@
#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
@ -225,9 +225,10 @@ 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;
};
struct mode_separate_context
@ -357,7 +358,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

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

@ -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();
@ -1982,7 +1982,7 @@ namespace wallet_public
struct COMMAND_ASSETS_DEPLOY
{
DOC_COMMAND("Deploy new asset in the system.");
struct request
{
std::list<transfer_destination> destinations;
@ -1999,16 +1999,32 @@ namespace wallet_public
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.");
@ -2026,13 +2042,14 @@ namespace wallet_public
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()
};
};
@ -2054,10 +2071,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()
};
};
@ -2079,13 +2098,47 @@ namespace wallet_public
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;
crypto::hash expected_tx_id;
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;
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
@ -1312,51 +1312,128 @@ namespace tools
}
}
//------------------------------------------------------------------------------------------------------------------------------
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_destinations(req.destinations, true, !req.do_not_split_destinations, currency_destinations);
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);
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_destinations(req.destinations, true, !req.do_not_split_destinations, currency_destinations);
w.get_wallet()->emit_asset(req.asset_id, currency_destinations, result_tx);
res.result_tx = currency::get_transaction_hash(result_tx);
return true;
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())
{
// 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_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);
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();
}

View file

@ -146,11 +146,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 +218,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);

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

@ -1816,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;
@ -1947,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;
@ -1983,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.

View file

@ -675,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,
@ -717,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

@ -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;
@ -1140,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);
@ -1218,6 +1218,7 @@ int main(int argc, char* argv[])
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-*");
GENERATE_AND_PLAY_HF(tx_pool_semantic_validation, "4-*");
// Double spend
GENERATE_AND_PLAY(gen_double_spend_in_tx<false>);
@ -1291,14 +1292,17 @@ int main(int argc, char* argv[])
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_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(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

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

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,51 @@ 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);
};

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");

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;
@ -1701,3 +1702,214 @@ bool tx_version_against_hardfork::generate(std::vector<test_event_entry>& events
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();
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{};
tx_out_bare output{};
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);
}
output.amount = 1;
output.target = target;
tx.vout.push_back(output);
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.
{
tx_out_bare output;
transaction tx{};
std::array<txin_to_key, 2> inputs{};
point_t key_image_point{};
key_image image{};
output.amount = 1;
tx.vout.push_back(output);
CHECK_AND_ASSERT_EQ(key_image_point.from_string("8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96"), true);
image = key_image_point.to_key_image();
for (int position{}; position < 2; ++position)
{
inputs.at(position).k_image = image;
tx.vin.push_back(inputs.at(position));
}
CHECK_AND_ASSERT_EQ(tx.vin.at(0).type(), typeid(txin_to_key));
CHECK_AND_ASSERT_EQ(tx.vin.at(0).type(), tx.vin.at(1).type());
CHECK_AND_ASSERT_EQ(boost::get<txin_to_key>(tx.vin.at(0)).k_image, boost::get<txin_to_key>(tx.vin.at(1)).k_image);
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);
}
// Two entries of the same type in extra.
{
tx_out_bare output;
transaction tx{};
std::array<txin_to_key, 2> inputs{};
std::array<point_t, 2> key_image_points{};
key_image image{};
output.amount = 1;
tx.vout.push_back(output);
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.
{
transaction tx{};
tx_out_bare output;
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);
}
CHECK_AND_ASSERT_GREATER(output.amount, std::accumulate(inputs.begin(), inputs.end(), std::uint64_t{}, [](uint64_t sum, const txin_to_key& input) { return sum + input.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);
}
// 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);
}
return true;
}

View file

@ -160,3 +160,8 @@ struct tx_version_against_hardfork : public test_chain_unit_enchanced
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct tx_pool_semantic_validation : public test_chain_unit_enchanced
{
bool generate(std::vector<test_event_entry>& events) const;
};

View file

@ -29,6 +29,9 @@ struct wallet_test : virtual public test_chain_unit_enchanced
template<typename wallet_t>
std::shared_ptr<wallet_t> init_playtime_test_wallet_t(const std::vector<test_event_entry>& events, currency::core& c, const currency::account_base& acc, bool true_http_rpc = false) const
{
#define LOCAL_HOST_CSTR "127.0.0.1"
#define LOCAL_PORT_CSTR "33777"
CHECK_AND_ASSERT_THROW_MES(events.size() > 0 && events[0].type() == typeid(currency::block), "Invalid events queue, can't find genesis block at the beginning");
crypto::hash genesis_hash = get_block_hash(boost::get<currency::block>(events[0]));
@ -36,14 +39,14 @@ struct wallet_test : virtual public test_chain_unit_enchanced
{
m_core_proxy = std::make_shared<tools::default_http_core_proxy>();
m_core_proxy->set_connectivity(100, 1);
CHECK_AND_ASSERT_THROW_MES(m_core_proxy->set_connection_addr("127.0.0.1:33777"), "set_connection_addr failed");
CHECK_AND_ASSERT_THROW_MES(m_core_proxy->set_connection_addr(LOCAL_HOST_CSTR ":" LOCAL_PORT_CSTR), "set_connection_addr failed");
if (!m_core_proxy->check_connection())
{
// if there's not http rpc core server yet, create one
boost::program_options::options_description desc_options;
currency::core_rpc_server::init_options(desc_options);
boost::program_options::variables_map vm{};
char* argv[] = {"--rpc-bind-ip=127.0.0.1", "--rpc-bind-port=33777"};
const char* const argv[] = {"--rpc-bind-ip=" LOCAL_HOST_CSTR, "--rpc-bind-port=" LOCAL_PORT_CSTR};
boost::program_options::store(boost::program_options::parse_command_line(sizeof argv / sizeof argv[0], argv, desc_options), vm);
m_http_rpc_server = std::make_shared<core_http_rpc_server_details>(c, vm);
}
@ -60,6 +63,9 @@ struct wallet_test : virtual public test_chain_unit_enchanced
w->set_concise_mode(true);
w->set_concise_mode_reorg_max_reorg_blocks(TESTS_CONCISE_MODE_REORG_MAX_REORG_BLOCK);
return w;
#undef LOCAL_HOST_CSTR
#undef LOCAL_PORT_CSTR
}

View file

@ -20,6 +20,7 @@
#include "crypto/range_proofs.h"
#include "../core_tests/random_helper.h"
#include "crypto_torsion_elements.h"
#include "crypto/eth_signature.h"
using namespace crypto;
@ -1896,6 +1897,93 @@ TEST(crypto, generators_precomp)
#undef CHECK_PRECOMP
}
#include "bitcoin-secp256k1/include/secp256k1.h"
TEST(crypto, secp256k1_ecdsa_native)
{
bool r = false;
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
uint8_t randomness[32];
crypto::generate_random_bytes(sizeof randomness, randomness);
secp256k1_context_randomize(ctx, randomness);
uint8_t seckey[32] = {};
while(true)
{
crypto::generate_random_bytes(sizeof seckey, seckey);
if (secp256k1_ec_seckey_verify(ctx, seckey))
break;
}
secp256k1_pubkey pubkey{};
ASSERT_TRUE(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey));
uint8_t compressed_pubkey[33] = {};
size_t output_len = sizeof compressed_pubkey;
ASSERT_TRUE(secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &output_len, &pubkey, SECP256K1_EC_COMPRESSED));
ASSERT_TRUE(output_len == sizeof compressed_pubkey);
secp256k1_ecdsa_signature secp256k1_ecdsa_sig{};
hash msg_hash = hash_helper_t::h("message");
ASSERT_TRUE(secp256k1_ecdsa_sign(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)&msg_hash, seckey, NULL, NULL));
// Serialize the signature in a compact form.
unsigned char secp256k1_ecdsa_sig_serialized[64] = {};
ASSERT_TRUE(secp256k1_ecdsa_signature_serialize_compact(ctx, secp256k1_ecdsa_sig_serialized, &secp256k1_ecdsa_sig));
//
// Verification
//
secp256k1_ecdsa_sig = secp256k1_ecdsa_signature{};
pubkey = secp256k1_pubkey{};
// Deserialize the signature.
ASSERT_TRUE(secp256k1_ecdsa_signature_parse_compact(ctx, &secp256k1_ecdsa_sig, secp256k1_ecdsa_sig_serialized));
// Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */
ASSERT_TRUE(secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey)));
// verify a signature
ASSERT_TRUE(secp256k1_ecdsa_verify(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)&msg_hash, &pubkey));
// verify using a static context
ASSERT_TRUE(secp256k1_ecdsa_verify(secp256k1_context_static, &secp256k1_ecdsa_sig, (const unsigned char*)&msg_hash, &pubkey));
// Epilogue
secp256k1_context_destroy(ctx);
return true;
}
TEST(crypto, eth_signature_basics)
{
eth_secret_key sk{};
eth_public_key pk{};
ASSERT_TRUE(generate_eth_key_pair(sk, pk));
eth_signature sig{};
hash m = hash_helper_t::h("How many of you have ever felt personally victimized by elliptic curves?");
ASSERT_TRUE(generate_eth_signature(m, sk, sig));
const eth_signature const_sig = sig;
ASSERT_TRUE(verify_eth_signature(m, pk, const_sig));
for(size_t i = 0; i < sizeof sig; ++i)
{
eth_signature bad_sig = sig;
bad_sig.data[i] ^= 1 + (rand() % 254); // xor with a number fom [1; 255] to make sure this byte will change
ASSERT_FALSE(verify_eth_signature(m, pk, bad_sig));
}
return true;
}
//

View file

@ -399,7 +399,7 @@ TEST(multiassets, get_or_calculate_asset_id_public_burn)
TEST(multiassets, get_or_calculate_asset_id_undefined)
{
bool success{false};
bool success{false};
crypto::point_t pt_public_key{};
success = pt_public_key.from_string(
@ -448,8 +448,6 @@ TEST(multiassets, get_or_calculate_asset_id_undefined)
&calculated_asset_id_key);
ASSERT_FALSE(success);
ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt);
ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key);
}
TEST(multiassets, get_or_calculate_asset_id_register_serialization)
@ -680,18 +678,6 @@ TEST(multiassets, get_or_calculate_asset_id_undefined_serialization)
&calculated_asset_id_pt,
&calculated_asset_id_key);
ASSERT_FALSE(success);
const std::string expected_asset_id_str{
"979eb706ace2eb83f9125658b23fb352208480cb3b90c43e2df0d298f9754ebc"};
crypto::point_t expected_asset_id_pt{};
success = expected_asset_id_pt.from_string(expected_asset_id_str);
ASSERT_TRUE(success);
crypto::public_key expected_asset_id_key{};
expected_asset_id_pt.to_public_key(expected_asset_id_key);
ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt);
ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key);
}
TEST(multiassets, get_or_calculate_asset_id_register_boost_serialization)
@ -940,7 +926,7 @@ TEST(multiassets, get_or_calculate_asset_id_undefined_boost_serialization)
'i', 'o', 'n', ':', ':', 'a', 'r', 'c', 'h',
'i', 'v', 'e', '\x11', '\x00', '\x04', '\x08', '\x04', '\x08',
'\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
'\x04', '\x00', '\x00', '\x00', '\x00', '\x00', 'd', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'd', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '2', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x03', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', 'H', 'L', 'O', '\x0b', '\x00',
@ -949,20 +935,20 @@ TEST(multiassets, get_or_calculate_asset_id_undefined_boost_serialization)
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'e',
'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l',
'd', '!', '\x00', '\x00', '\x00', '\x00', '\x00', ' ', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x0c', '@', '\x8c',
'\xf8', '\xb7', '\xfb', '\x80', '\x8f', '@', 'Y', '=', 'n',
'\xb7', 'X', '\x90', '\xe2', '\xab', '=', '\x0c', '\xcd', '\xc7',
'\x01', 'J', '\x7f', '\xc6', '\xb6', '\xab', '\x05', '\x16', ';',
'\xe0', '`', '\x00', ' ', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xe9', '\x1b', '\x9a',
's', ')', '-', 'n', '\xa4', 'o', '\xb3', '\xd4', '\xf4',
'\xcc', 'y', '\xc3', 'K', '\xfb', '}', '\x14', '\xc2', '\xe6',
'\x84', '\xe5', '\x80', '\x93', '\xa2', 'G', '\x1c', '\x92', '\xe5',
'\x1c', '\x16', '\x00', ' ', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01',
'\x00', '\x00', '\x00', '\x01', ' ', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', 'T', '\xf3', '\xf7', ',', 'r', '\xe5',
'\xb0', '\x14', '\xad', '+', '+', '\x90', '\x01', '\xac', '\xef',
'\x95', 'O', '\xe8', '-', '\xd3', '\xed', 'V', '\xa3', '\x8c',
'\xd9', '\xdd', '\xc5', '\xdb', 'W', 'g', '?', '\x8f'};
'\x00', '\x00', '\x00', '\x97', '\x9e', '\xb7', '\x06', '\xac', '\xe2',
'\xeb', '\x83', '\xf9', '\x12', 'V', 'X', '\xb2', '?', '\xb3',
'R', ' ', '\x84', '\x80', '\xcb', ';', '\x90', '\xc4', '>',
'-', '\xf0', '\xd2', '\x98', '\xf9', 'u', 'N', '\xbc'};
success = tools::unserialize_obj_from_buff(
asset_descriptor_operation,

View file

@ -5,6 +5,24 @@
#include "gtest/gtest.h"
#include "common/util.h"
bool check_parse_client_version(const std::string& str, int expected_major, int expected_minor, int expected_revision, int expected_build_number, const std::string& expected_commit_id, bool expected_dirty)
{
int major = -1, minor = -1, revision = -1, build_number = -1;
std::string commit_id;
bool dirty = false;
if (!tools::parse_client_version(str, major, minor, revision, build_number, commit_id, dirty))
return false;
return major == expected_major && minor == expected_minor && revision == expected_revision && build_number == expected_build_number && commit_id == expected_commit_id && dirty == expected_dirty;
}
TEST(p2p_client_version, test_0)
{
ASSERT_TRUE(check_parse_client_version("10.101.999.28391[deadbeef31337-dirty]", 10, 101, 999, 28391, "deadbeef31337", true));
}
TEST(p2p_client_version, test_1)
{
using namespace tools;

View file

@ -757,30 +757,30 @@ struct A
struct A_v1 : public A
{
std::vector<std::string> vector_two;
uint8_t current_version = 1;
BEGIN_SERIALIZE()
FIELD(one)
FIELD(two)
FIELD(vector_one)
VERSION(1)
if (s_version < 1) return true;
VERSION_TO_MEMBER(1, current_version)
FIELD(vector_two)
END_SERIALIZE()
};
struct A_v2 : public A_v1
{
std::vector<std::string> vector_3;
std::vector<std::string> vector_4;
uint8_t current_version = 2;
BEGIN_SERIALIZE()
//CURRENT_VERSION(2)
FIELD(one)
FIELD(two)
FIELD(vector_one)
VERSION(2)
VERSION_TO_MEMBER(2, current_version)
if (s_version < 1) return true;
FIELD(vector_two)
if (s_version < 2) return true;
@ -792,13 +792,14 @@ struct A_v2 : public A_v1
struct A_v3 : public A_v2
{
std::vector<std::string> vector_5;
uint8_t current_version = 3;
BEGIN_SERIALIZE()
//CURRENT_VERSION(3)
FIELD(one)
FIELD(two)
FIELD(vector_one)
VERSION(3)
VERSION_TO_MEMBER(3, current_version)
if (s_version < 1) return true;
FIELD(vector_two)
if (s_version < 2) return true;

2
utils/JS/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/node_modules*
.vscode

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

192
utils/JS/package-lock.json generated Normal file
View file

@ -0,0 +1,192 @@
{
"name": "JS",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"axios": "^1.7.7",
"ethers": "^6.13.2"
}
},
"node_modules/@adraffy/ens-normalize": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
"integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw=="
},
"node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"dependencies": {
"@noble/hashes": "1.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@types/node": {
"version": "18.15.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz",
"integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q=="
},
"node_modules/aes-js": {
"version": "4.0.0-beta.5",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
"integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q=="
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ethers": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz",
"integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/ethers-io/"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@adraffy/ens-normalize": "1.10.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.2",
"@types/node": "18.15.13",
"aes-js": "4.0.0-beta.5",
"tslib": "2.4.0",
"ws": "8.17.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

6
utils/JS/package.json Normal file
View file

@ -0,0 +1,6 @@
{
"dependencies": {
"axios": "^1.7.7",
"ethers": "^6.13.2"
}
}

View file

@ -0,0 +1,8 @@
{
"id": 0,
"jsonrpc": "2.0",
"result": {
"status": "",
"transfers_were_unlocked": false
}
}

248
utils/JS/test_eth_sig.js Normal file
View file

@ -0,0 +1,248 @@
const axios = require('axios');
const { ethers } = require("ethers");
const { exit } = require('process');
const fs = require('fs');
/// Define an async function that takes method name and parameters
async function callJsonRpc(requestData, port = 22222) {
try {
const response = await axios.post('http://127.0.0.1:' + port +'/json_rpc', requestData, {
headers: {
'Content-Type': 'application/json',
},
});
// Return the result from the JSON-RPC response
return response.data;
} catch (error) {
console.error('Error:', error);
throw error; // Optionally rethrow the error to handle it in the calling function
}
}
function reverseHexBytes(hexString) {
// Ensure the hex string length is even
if (hexString.length % 2 !== 0) {
throw new Error("Invalid hex string length");
}
// Split the hex string into chunks of 2 characters (1 byte)
const bytes = hexString.match(/.{1,2}/g);
// Reverse the array of bytes and join them back into a string
const reversedHex = bytes.reverse().join('');
return reversedHex;
}
async function deploy_asset()
{
try {
//Generated Private Key: 0x17a938099954cee510d7fc9eb2366f0762b093d9be547acabf8be85f774ef154
//Generated Address: 0x0886bA9F5b117D2A3C1ce18106F2Ce759f5D34C8
const loadedWallet = new ethers.Wallet("0x17a938099954cee510d7fc9eb2366f0762b093d9be547acabf8be85f774ef154");
console.log("Loaded Address:", loadedWallet.address);
console.log("Public key:", loadedWallet.signingKey.compressedPublicKey);
const owner_eth_pub_key = loadedWallet.signingKey.compressedPublicKey.substring(2);
console.log("Generated Public key HEX:", owner_eth_pub_key);
const jsonObject = {
id: 0,
jsonrpc: "2.0",
method: "deploy_asset",
params: {
asset_descriptor: {
//current_supply: 1000000000000000,
decimal_point: 12,
full_name: "Zano wrapped ABC",
hidden_supply: false,
meta_info: "Stable and private",
owner: "",
ticker: "ZABC",
total_max_supply: 1000000000000000000,
owner_eth_pub_key: owner_eth_pub_key
},
destinations: [
{
address: "ZxC1U6hoCRM9PBSwrBTrWD8XgcHqLNJN9NWqXs9o994eZuHHBvSAyBpQ4TbWSNoabUDPdD8iEM5ZjPoMM7jE48mp2iKcVHLSK",
amount: 1000000000000000,
asset_id: ""
},
{
address: "ZxC1U6hoCRM9PBSwrBTrWD8XgcHqLNJN9NWqXs9o994eZuHHBvSAyBpQ4TbWSNoabUDPdD8iEM5ZjPoMM7jE48mp2iKcVHLSK",
amount: 1000000000000000,
asset_id: ""
}
],
do_not_split_destinations: false
}
};
const res = await callJsonRpc(jsonObject);
console.log("deploy_asset response: " + JSON.stringify(res, null, 2));
/*
deploy_asset response:
{
"id": 0,
"jsonrpc": "2.0",
"result": {
"new_asset_id": "7d51ecaad2e3458e0d62b146f33079c6ea307841b09a44b777e0c01eb11b98bf",
"tx_id": "73ff52bf4d85153f2b25033dd76e9e92e63214ed983682182e6e2b2ce0ecf46c"
}
}
*/
}
catch (error) {
console.error('Error occurred:', error);
}
}
async function emmit_asset()
{
try {
//Generated Private Key: 0x17a938099954cee510d7fc9eb2366f0762b093d9be547acabf8be85f774ef154
//Generated Address: 0x0886bA9F5b117D2A3C1ce18106F2Ce759f5D34C8
// asset_id 7d51ecaad2e3458e0d62b146f33079c6ea307841b09a44b777e0c01eb11b98bf
//var use_pregenerated_files = false;
const loadedWallet = new ethers.Wallet("0x17a938099954cee510d7fc9eb2366f0762b093d9be547acabf8be85f774ef154");
console.log("Loaded Address:", loadedWallet.address);
console.log("Public key:", loadedWallet.signingKey.compressedPublicKey);
const owner_eth_pub_key = loadedWallet.signingKey.compressedPublicKey.substring(2);
console.log("Generated Public key HEX:", owner_eth_pub_key);
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//this part is performed on coordinator node:
var res_emmit;
//if(!use_pregenerated_files)
//{
const requestDataEmit = {
id: 0,
jsonrpc: "2.0",
method: "emit_asset",
params: {
asset_id: "7d51ecaad2e3458e0d62b146f33079c6ea307841b09a44b777e0c01eb11b98bf",
destinations: [{
address: "ZxC1U6hoCRM9PBSwrBTrWD8XgcHqLNJN9NWqXs9o994eZuHHBvSAyBpQ4TbWSNoabUDPdD8iEM5ZjPoMM7jE48mp2iKcVHLSK",
amount: 100000000000,
asset_id: ""
}],
do_not_split_destinations: false
}
};
res_emmit = await callJsonRpc(requestDataEmit);
fs.writeFileSync('emmit_response.json', JSON.stringify(res_emmit, null, 2));
console.log("emmit_response response: " + JSON.stringify(res_emmit, null, 2));
//}else
//{
// const data = fs.readFileSync('emmit_response.json', 'utf8');
// res_emmit = JSON.parse(data);
//}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//this part is performed on validator node:
var res_decrypt;
//if(!use_pregenerated_files)
//{
const requestDataDecrypt = {
id: 0,
jsonrpc: "2.0",
method: "decrypt_tx_details",
params: {
outputs_addresses: res_emmit.result.data_for_external_signing.outputs_addresses,
tx_blob: res_emmit.result.data_for_external_signing.unsigned_tx,
tx_id: "",
tx_secret_key: res_emmit.result.data_for_external_signing.tx_secret_key
}
};
res_decrypt = await callJsonRpc(requestDataDecrypt, 12111); //request to daemon
fs.writeFileSync('decrypt_response.json', JSON.stringify(res_decrypt, null, 2));
console.log("decrypt_response : " + JSON.stringify(res_decrypt, null, 2));
//TODO: response holds all information about what this transaction actually transfer and to what addresses
//}else
//{
// const data = fs.readFileSync('decrypt_response.json', 'utf8');
// res_decrypt = JSON.parse(data);
//}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//this part is performed with TSS scheme:
const bytesToSign = ethers.getBytes('0x' + res_decrypt.result.verified_tx_id);
const signature = loadedWallet.signingKey.sign(bytesToSign).serialized;
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//this part is performed on coordinator node with given signature:
const signature_without_0x = signature.substring(2);
console.log("Generated signature: " + signature_without_0x);
// Strip the last byte (recovery parameter) to get 64 bytes
const strippedSignature = signature_without_0x.slice(0, -2); // Remove the last byte (2 hex chars)
const requestSendSigned = {
id: 0,
jsonrpc: "2.0",
method: "send_ext_signed_asset_tx",
params: {
eth_sig: strippedSignature,
expected_tx_id: res_decrypt.result.verified_tx_id,
finalized_tx: res_emmit.result.data_for_external_signing.finalized_tx,
unlock_transfers_on_fail: false,
unsigned_tx: res_emmit.result.data_for_external_signing.unsigned_tx
}
}
const res_sign = await callJsonRpc(requestSendSigned);
fs.writeFileSync('sign_response.json', JSON.stringify(res_sign, null, 2));
console.log("sign_response response: " + JSON.stringify(res_sign, null, 2));
}
catch (error) {
console.error('Error occurred:', error);
}
}
async function main()
{
try {
/*
await deploy_asset();
TODO: wait for 10 confirmations
//wait for 10 confirmations
//asset id 7d51ecaad2e3458e0d62b146f33079c6ea307841b09a44b777e0c01eb11b98bf
*/
await emmit_asset();
} catch (error) {
console.error('Error occurred:', error);
}
}
main();