Merge branch 'secp256k1' into develop
This commit is contained in:
commit
f17dba64ce
40 changed files with 1778 additions and 262 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
1
contrib/bitcoin-secp256k1
Submodule
1
contrib/bitcoin-secp256k1
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit a5269373fa13ff845f654d81b90629dd78495641
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
162
src/crypto/eth_signature.cpp
Normal file
162
src/crypto/eth_signature.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// Copyright (c) 2024 Zano Project
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#include "eth_signature.h"
|
||||
#include "crypto.h"
|
||||
#include "bitcoin-secp256k1/include/secp256k1.h"
|
||||
#include "random.h"
|
||||
#include "misc_language.h"
|
||||
#include <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);
|
||||
}
|
||||
|
||||
|
||||
} // namespace crypto
|
||||
66
src/crypto/eth_signature.h
Normal file
66
src/crypto/eth_signature.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2024 Zano Project
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#pragma once
|
||||
#include <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);
|
||||
|
||||
} // namespace crypto
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -4008,7 +4008,8 @@ bool blockchain_storage::put_alias_info(const transaction & tx, extra_alias_entr
|
|||
//@@ remove get_tx_fee_median();
|
||||
LOG_PRINT_MAGENTA("[ALIAS_REGISTERED]: " << ai.m_alias << ": " << get_account_address_as_str(ai.m_address) << ", fee median: " << get_tx_fee_median(), LOG_LEVEL_1);
|
||||
rise_core_event(CORE_EVENT_ADD_ALIAS, alias_info_to_rpc_alias_info(ai));
|
||||
}else
|
||||
}
|
||||
else
|
||||
{
|
||||
//update procedure
|
||||
CHECK_AND_ASSERT_MES(ai.m_sign.size() == 1, false, "alias " << ai.m_alias << " can't be update, wrong ai.m_sign.size() count: " << ai.m_sign.size());
|
||||
|
|
@ -4103,14 +4104,25 @@ bool blockchain_storage::pop_asset_info(const crypto::public_key& asset_id)
|
|||
//------------------------------------------------------------------
|
||||
bool validate_ado_ownership(asset_op_verification_context& avc)
|
||||
{
|
||||
asset_operation_ownership_proof aoop = AUTO_VAL_INIT(aoop);
|
||||
bool r = get_type_in_variant_container(avc.tx.proofs, aoop);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed - missing signature (asset_operation_ownership_proof)");
|
||||
bool r = false;
|
||||
CHECK_AND_ASSERT_MES(avc.asset_op_history->size() != 0, false, "asset with id " << avc.asset_id << " has empty history record");
|
||||
const asset_descriptor_operation& last_ado = avc.asset_op_history->back();
|
||||
|
||||
CHECK_AND_ASSERT_MES(avc.asset_op_history->size() != 0, false, "asset with id " << avc.asset_id << " has invalid history size() == 0");
|
||||
if (last_ado.descriptor.owner_eth_pub_key.has_value())
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(last_ado.descriptor.owner == null_pkey, false, "owner_eth_pub_key is set but owner pubkey is nonzero");
|
||||
asset_operation_ownership_proof_eth aoop_eth{};
|
||||
r = get_type_in_variant_container(avc.tx.proofs, aoop_eth);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed: asset_operation_ownership_proof_eth is missing");
|
||||
return crypto::verify_eth_signature(avc.tx_id, last_ado.descriptor.owner_eth_pub_key.value(), aoop_eth.eth_sig);
|
||||
}
|
||||
|
||||
crypto::public_key owner_key = avc.asset_op_history->back().descriptor.owner;
|
||||
return crypto::verify_schnorr_sig(avc.tx_id, owner_key, aoop.gss);
|
||||
// owner_eth_pub_key has no value -- fallback to default
|
||||
asset_operation_ownership_proof aoop{};
|
||||
r = get_type_in_variant_container(avc.tx.proofs, aoop);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed: asset_operation_ownership_proof is missing");
|
||||
|
||||
return crypto::verify_schnorr_sig(avc.tx_id, last_ado.descriptor.owner, aoop.gss);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const
|
||||
|
|
@ -4122,11 +4134,12 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state
|
|||
|
||||
const asset_descriptor_operation& ado = avc.ado;
|
||||
|
||||
bool need_to_validate_ao_amount_commitment = true;
|
||||
|
||||
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(!avc.asset_op_history, false, "asset with id " << avc.asset_id << " has already been registered");
|
||||
avc.amount_to_validate = ado.descriptor.current_supply;
|
||||
CHECK_AND_ASSERT_MES(validate_asset_operation_amount_commitment(avc), false, "validate_asset_operation_amount_commitment failed!");
|
||||
if(this->is_hardfork_active(ZANO_HARDFORK_05))
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(validate_ado_initial(ado.descriptor), false, "validate_ado_initial failed!");
|
||||
|
|
@ -4134,48 +4147,48 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state
|
|||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size(), false, "asset with id " << avc.asset_id << " has not been registered");
|
||||
CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size() > 0, false, "asset with id " << avc.asset_id << " has not been registered");
|
||||
const asset_descriptor_operation& last_ado = avc.asset_op_history->back();
|
||||
// check ownership permission
|
||||
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE /*|| ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN*/)
|
||||
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
|
||||
{
|
||||
bool r = validate_ado_ownership(avc);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Faild to validate ownership of asset_descriptor_operation, rejecting");
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to validate ownership of asset_descriptor_operation, rejecting");
|
||||
}
|
||||
|
||||
avc.amount_to_validate = 0;
|
||||
bool need_to_validate_balance_proof = true;
|
||||
if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE)
|
||||
{
|
||||
//check that total current_supply haven't changed
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply == avc.asset_op_history->back().descriptor.current_supply, false, "update operation attempted to change emission, failed");
|
||||
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "update operation attempted to change fileds that shouldn't be modified, failed");
|
||||
need_to_validate_balance_proof = false;
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply == last_ado.descriptor.current_supply, false, "update operation attempted to change emission, failed");
|
||||
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "update operation modifies asset descriptor in a prohibited manner");
|
||||
need_to_validate_ao_amount_commitment = false;
|
||||
}
|
||||
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > avc.asset_op_history->back().descriptor.current_supply, false, "emit operation does not increase the current supply, failed");
|
||||
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "emit operation is not allowed to update fields");
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == avc.asset_op_history->back().descriptor.meta_info, false, "emit operation is not allowed to update meta info");
|
||||
avc.amount_to_validate = ado.descriptor.current_supply - avc.asset_op_history->back().descriptor.current_supply;
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > last_ado.descriptor.current_supply, false, "emit operation does not increase the current supply, failed");
|
||||
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "emit operation modifies asset descriptor in a prohibited manner");
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_ado.descriptor.meta_info, false, "emit operation is not allowed to update meta info");
|
||||
avc.amount_to_validate = ado.descriptor.current_supply - last_ado.descriptor.current_supply;
|
||||
}
|
||||
else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < avc.asset_op_history->back().descriptor.current_supply, false, "burn operation does not decrease the current supply, failed");
|
||||
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "burn operation is not allowed to update fields");
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == avc.asset_op_history->back().descriptor.meta_info, false, "burn operation is not allowed to update meta info");
|
||||
avc.amount_to_validate = avc.asset_op_history->back().descriptor.current_supply - ado.descriptor.current_supply;
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < last_ado.descriptor.current_supply, false, "burn operation does not decrease the current supply, failed");
|
||||
CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "burn operation modifies asset descriptor in a prohibited manner");
|
||||
CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_ado.descriptor.meta_info, false, "burn operation is not allowed to update meta info");
|
||||
avc.amount_to_validate = last_ado.descriptor.current_supply - ado.descriptor.current_supply;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Unknown operation type: " << (int)ado.operation_type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_to_validate_balance_proof)
|
||||
{
|
||||
bool r = validate_asset_operation_amount_commitment(avc);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation");
|
||||
}
|
||||
if (need_to_validate_ao_amount_commitment)
|
||||
{
|
||||
bool r = validate_asset_operation_amount_commitment(avc);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -5932,6 +5945,10 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO @#@#: add check for descriptor.owner_eth_pub_key
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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(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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2196,6 +2196,8 @@ namespace currency
|
|||
hsc.add_scalar(crypto::scalar_t(ado.descriptor.total_max_supply));
|
||||
hsc.add_scalar(crypto::scalar_t(ado.descriptor.decimal_point));
|
||||
hsc.add_pub_key(ado.descriptor.owner);
|
||||
if (ado.descriptor.owner_eth_pub_key.has_value())
|
||||
hsc.add_eth_pub_key(ado.descriptor.owner_eth_pub_key.value());
|
||||
crypto::hash h = hsc.calc_hash_no_reduce();
|
||||
|
||||
// this hash function needs to be computationally expensive (s.a. the whitepaper)
|
||||
|
|
@ -2248,7 +2250,8 @@ namespace currency
|
|||
// asset_control_key = Hs(CRYPTO_HDS_ASSET_CONTROL_KEY, 8 * tx_key.sec * sender_account_keys.account_address.spend_public_key, 0)
|
||||
// ado.descriptor.owner = asset_control_key * G
|
||||
|
||||
ado.descriptor.owner = sender_account_keys.account_address.spend_public_key;
|
||||
if (!ado.descriptor.owner_eth_pub_key.has_value())
|
||||
ado.descriptor.owner = sender_account_keys.account_address.spend_public_key;
|
||||
|
||||
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(ado, &gen_context.ao_asset_id_pt, &gen_context.ao_asset_id), false, "get_or_calculate_asset_id failed");
|
||||
|
||||
|
|
@ -2337,25 +2340,6 @@ namespace currency
|
|||
}
|
||||
if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_handle_asset_descriptor_operation_before_seal{ &ado });
|
||||
|
||||
ftp.need_to_generate_ado_proof = true;
|
||||
/*
|
||||
//seal it with owners signature
|
||||
crypto::signature sig = currency::null_sig;
|
||||
crypto::hash h = get_signature_hash_for_asset_operation(ado);
|
||||
if (ftp.pthirdparty_sign_handler)
|
||||
{
|
||||
bool r = ftp.pthirdparty_sign_handler->sign(h, ftp.ado_current_asset_owner, sig);
|
||||
CHECK_AND_ASSERT_MES(r, false, "asset thirparty sign failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
crypto::public_key pub_k = currency::null_pkey;
|
||||
crypto::secret_key_to_public_key(sender_account_keys.spend_secret_key, pub_k);
|
||||
CHECK_AND_ASSERT_MES(ftp.ado_current_asset_owner == pub_k, false, "asset owner key not matched with provided private key for asset operation signing");
|
||||
crypto::generate_signature(h, pub_k, account_keys.spend_secret_key, sig);
|
||||
}
|
||||
ado.opt_proof = sig;
|
||||
*/
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2605,8 +2589,7 @@ namespace currency
|
|||
// ASSET oprations handling
|
||||
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
|
||||
{
|
||||
asset_descriptor_operation* pado = nullptr;
|
||||
pado = get_type_in_variant_container<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);
|
||||
|
|
@ -2719,7 +2702,8 @@ namespace currency
|
|||
// generate proofs and signatures
|
||||
// (any changes made below should only affect the signatures/proofs and should not impact the prefix hash calculation)
|
||||
//
|
||||
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
|
||||
result.tx_id = get_transaction_prefix_hash(tx);
|
||||
const crypto::hash &tx_prefix_hash = result.tx_id;
|
||||
|
||||
// ring signatures (per-input proofs)
|
||||
r = false;
|
||||
|
|
@ -2775,35 +2759,30 @@ namespace currency
|
|||
CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
|
||||
tx.proofs.emplace_back(std::move(balance_proof));
|
||||
|
||||
// asset operation proof (if necessary)
|
||||
// optional asset operation proofs: amount commitment proof (required for register, emit, public burn)
|
||||
if (gen_context.ao_asset_id != currency::null_pkey)
|
||||
{
|
||||
// construct the asset operation proof
|
||||
// TODO @#@# add support for hidden supply
|
||||
// asset amount commitment g proof (TODO @#@# add support for hidden supply)
|
||||
crypto::signature aop_g_sig{};
|
||||
crypto::generate_signature(tx_prefix_hash, crypto::point_t(gen_context.ao_amount_blinding_mask * crypto::c_point_G).to_public_key(), gen_context.ao_amount_blinding_mask, aop_g_sig);
|
||||
asset_operation_proof aop{};
|
||||
aop.opt_amount_commitment_g_proof = aop_g_sig;
|
||||
tx.proofs.emplace_back(std::move(aop));
|
||||
}
|
||||
if(ftp.need_to_generate_ado_proof)
|
||||
{
|
||||
asset_operation_ownership_proof aoop = AUTO_VAL_INIT(aoop);
|
||||
|
||||
if (ftp.pthirdparty_sign_handler)
|
||||
// optional asset operation proofs: ownership proof for standard (non-eth) owner (using generic Shnorr signature with the spend secret key)
|
||||
const asset_descriptor_operation* pado = get_type_in_variant_container<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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 6d212d4eefaf6d13c72799cb89be2c80b1813d38
|
||||
Subproject commit 748e8e96d8f2653e6e698a11f67c172c1f84c2b2
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
@ -643,12 +695,14 @@ namespace currency
|
|||
struct request
|
||||
{
|
||||
std::string tx_as_hex;
|
||||
std::string tx_as_json;
|
||||
|
||||
request() {}
|
||||
explicit request(const transaction &);
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_as_hex) DOC_DSCR("The transaction data as a hexadecimal string, ready for network broadcast.") DOC_EXMP("00018ed1535b8b4862e.....368cdc5a86") DOC_END
|
||||
KV_SERIALIZE(tx_as_hex) DOC_DSCR("[either] The transaction data as a hexadecimal string, ready for network broadcast.") DOC_EXMP("00018ed1535b8b4862e.....368cdc5a86") DOC_END
|
||||
KV_SERIALIZE_BLOB_AS_BASE64_STRING(tx_as_json) DOC_DSCR("[or] The transaction data as a base64-encoded json, ready for network broadcast.") DOC_EXMP("ARMBgKCUpY0dBBoAAAAAAAAAABoCAAAAA.......AAAAAAAAABoPAAAAAAAAACVA4FRLH") DOC_END
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -125,7 +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<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", "");
|
||||
|
|
@ -2094,11 +2094,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
|
||||
|
|
@ -3200,7 +3200,7 @@ int main(int argc, char* argv[])
|
|||
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);
|
||||
|
||||
|
|
@ -3278,7 +3278,6 @@ 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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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.get() << " (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,7 +441,7 @@ 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;
|
||||
|
|
@ -4004,7 +4016,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;
|
||||
|
|
@ -4036,7 +4048,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 << " ";
|
||||
|
|
@ -4300,6 +4312,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
|
||||
|
|
@ -4390,7 +4435,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)
|
||||
|
|
@ -5360,125 +5405,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");
|
||||
|
||||
asset_descriptor_operation asset_emmit_info = AUTO_VAL_INIT(asset_emmit_info);
|
||||
asset_emmit_info.descriptor = rsp.asset_descriptor;
|
||||
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{};
|
||||
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;
|
||||
|
||||
|
|
@ -5487,11 +5566,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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
@ -5961,7 +6045,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)
|
||||
|
|
@ -7418,20 +7502,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.",
|
||||
<< (broadcasted ? "Please wait for the transaction to be confirmed before your balance is unlocked." : ""),
|
||||
LOG_LEVEL_0);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
@ -7602,8 +7686,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
|
||||
//
|
||||
|
|
@ -7736,7 +7820,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)
|
||||
|
|
@ -7914,8 +7998,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);
|
||||
|
|
@ -7925,7 +8012,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);
|
||||
|
|
@ -7938,7 +8025,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);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
@ -8226,7 +8313,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 (...)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -420,10 +420,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);
|
||||
|
|
@ -589,6 +595,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);
|
||||
|
|
@ -836,7 +843,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,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include <boost/serialization/deque.hpp>
|
||||
#include <boost/serialization/singleton.hpp>
|
||||
#include <boost/serialization/extended_type_info.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/optional.hpp>
|
||||
#include <atomic>
|
||||
|
||||
|
|
@ -52,6 +51,7 @@
|
|||
#define WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER uint32_t(1 << 3)
|
||||
#define WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION uint32_t(1 << 4) // transfer is reserved for cold-signing (unsigned tx was created and passed for signing)
|
||||
#define WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM uint32_t(1 << 5) // for htlc keeps info if this htlc belong as redeem or as refund
|
||||
#define WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION uint32_t(1 << 6) // transfer is reserved for an ongoing asset operation with external signing
|
||||
|
||||
|
||||
|
||||
|
|
@ -224,9 +224,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
|
||||
|
|
@ -356,7 +357,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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
return true;
|
||||
WALLET_RPC_CATCH_TRY_ENTRY();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1987,22 +1987,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;
|
||||
|
|
@ -2023,6 +2028,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.
|
||||
|
|
|
|||
|
|
@ -731,7 +731,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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -1293,7 +1293,9 @@ int main(int argc, char* argv[])
|
|||
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, "4-*"); // TODO
|
||||
GENERATE_AND_PLAY_HF(eth_signed_asset_via_rpc, "4-*");
|
||||
|
||||
GENERATE_AND_PLAY_HF(pos_fuse_test, "4-*");
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "random_helper.h"
|
||||
#include "wallet/wallet_debug_events_definitions.h"
|
||||
#include "wallet/wallet_rpc_server.h"
|
||||
#include "wallet/wallet_public_structs_defs.h"
|
||||
using namespace currency;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
@ -1326,3 +1328,627 @@ bool asset_operation_in_consolidated_tx::assert_alice_currency_not_registered(cu
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
eth_signed_asset_basics::eth_signed_asset_basics()
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(eth_signed_asset_basics, c1);
|
||||
}
|
||||
|
||||
bool eth_signed_asset_basics::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
//
|
||||
// Test idea: register an asset, then emit, public burn, transfer ownership, and then emit it once again.
|
||||
// Update (ownership transferring) and emit operations are done using third-party external asset operation signing (ETH signature)
|
||||
// Public burn operation is done by an entity, who isn't controlling the asset.
|
||||
//
|
||||
|
||||
uint64_t ts = test_core_time::get_time();
|
||||
m_accounts.resize(TOTAL_ACCS_COUNT);
|
||||
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts);
|
||||
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts);
|
||||
miner_acc.generate();
|
||||
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts);
|
||||
DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks
|
||||
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
|
||||
|
||||
DO_CALLBACK(events, "c1");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eth_signed_asset_basics::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
bool r = false;
|
||||
|
||||
crypto::eth_secret_key eth_sk{};
|
||||
crypto::eth_public_key eth_pk{};
|
||||
r = crypto::generate_eth_key_pair(eth_sk, eth_pk);
|
||||
CHECK_AND_ASSERT_MES(r, false, "generate_eth_key_pair failed");
|
||||
|
||||
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
|
||||
miner_wlt->refresh();
|
||||
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
|
||||
alice_wlt->refresh();
|
||||
|
||||
//
|
||||
// register
|
||||
//
|
||||
asset_descriptor_base adb{};
|
||||
adb.decimal_point = 2;
|
||||
adb.total_max_supply = 10000;
|
||||
adb.full_name = "Either";
|
||||
adb.ticker = "EITH";
|
||||
adb.owner_eth_pub_key = eth_pk; // note setting owner eth pub key here
|
||||
|
||||
uint64_t initial_emit_amount = adb.total_max_supply / 2;
|
||||
|
||||
std::vector<tx_destination_entry> destinations{tx_destination_entry{initial_emit_amount, m_accounts[ALICE_ACC_IDX].get_public_address(), null_pkey}};
|
||||
finalized_tx ft{};
|
||||
crypto::public_key asset_id = currency::null_pkey;
|
||||
miner_wlt->deploy_new_asset(adb, destinations, ft, asset_id);
|
||||
|
||||
const transaction& tx = ft.tx;
|
||||
|
||||
LOG_PRINT_L0("Deployed new asset: " << asset_id << ", tx_id: " << ft.tx_id);
|
||||
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
miner_wlt->refresh();
|
||||
|
||||
//
|
||||
// emit
|
||||
//
|
||||
destinations.clear();
|
||||
destinations.emplace_back(initial_emit_amount, m_accounts[ALICE_ACC_IDX].get_public_address(), null_pkey); // asset
|
||||
destinations.emplace_back(MK_TEST_COINS(100), m_accounts[ALICE_ACC_IDX].get_public_address()); // some native coins for fee
|
||||
uint64_t emit_amount = adb.total_max_supply - initial_emit_amount;
|
||||
ft = finalized_tx{};
|
||||
miner_wlt->emit_asset(asset_id, destinations, ft);
|
||||
|
||||
// make sure the transaction didn't get into tx pool (as it's not fully signed)
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// make sure emit transaction cannot be added to the transaction pool (it's not fully signed atm)
|
||||
tx_verification_context tvc{};
|
||||
r = c.get_tx_pool().add_tx(ft.tx, tvc, false);
|
||||
CHECK_AND_ASSERT_MES(!r, false, "unsigned emit tx was able to be added to the pool");
|
||||
|
||||
// sign asset emit operation with ETH signature
|
||||
crypto::eth_signature eth_sig{};
|
||||
r = crypto::generate_eth_signature(ft.tx_id, eth_sk, eth_sig);
|
||||
CHECK_AND_ASSERT_MES(r, false, "generate_eth_signature failed");
|
||||
|
||||
transaction emit_tx{};
|
||||
bool transfers_unlocked = false;
|
||||
miner_wlt->submit_externally_signed_asset_tx(ft, eth_sig, true, emit_tx, transfers_unlocked);
|
||||
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// Alice checks her asset balance
|
||||
size_t blocks_fetched = 0;
|
||||
alice_wlt->refresh(blocks_fetched);
|
||||
CHECK_AND_ASSERT_EQ(blocks_fetched, 2);
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", adb.total_max_supply, asset_id, adb.decimal_point), false, "");
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", MK_TEST_COINS(100)), false, "");
|
||||
|
||||
|
||||
//
|
||||
// public burn (by Alice, as anyone can do public burn)
|
||||
//
|
||||
uint64_t amount_to_burn = initial_emit_amount / 2;
|
||||
uint64_t total_amount_expected = adb.total_max_supply - amount_to_burn;
|
||||
|
||||
r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
|
||||
miner_wlt->refresh();
|
||||
alice_wlt->refresh();
|
||||
|
||||
ft = finalized_tx{};
|
||||
alice_wlt->burn_asset(asset_id, amount_to_burn, ft);
|
||||
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
asset_descriptor_base adb_recent{};
|
||||
r = c.get_blockchain_storage().get_asset_info(asset_id, adb_recent);
|
||||
CHECK_AND_ASSERT_MES(r, false, "get_asset_info failed");
|
||||
|
||||
// make sure the current supply of the asset did change accordingly
|
||||
CHECK_AND_ASSERT_EQ(adb_recent.current_supply, total_amount_expected);
|
||||
|
||||
|
||||
//
|
||||
// ownership transfer (to another ETH key)
|
||||
//
|
||||
crypto::eth_secret_key eth_sk_2{};
|
||||
crypto::eth_public_key eth_pk_2{};
|
||||
r = crypto::generate_eth_key_pair(eth_sk_2, eth_pk_2);
|
||||
CHECK_AND_ASSERT_MES(r, false, "generate_eth_key_pair failed");
|
||||
|
||||
ft = finalized_tx{};
|
||||
miner_wlt->transfer_asset_ownership(asset_id, eth_pk_2, ft);
|
||||
|
||||
// make sure the transaction didn't get into tx pool (as it's not fully signed)
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// make sure ownership transaction cannot be added to the transaction pool (it's not fully signed atm)
|
||||
tvc = tx_verification_context{};
|
||||
r = c.get_tx_pool().add_tx(ft.tx, tvc, false);
|
||||
CHECK_AND_ASSERT_MES(!r, false, "unsigned tx was able to be added to the pool");
|
||||
|
||||
// sign asset emit operation with ETH signature
|
||||
eth_sig = crypto::eth_signature{};
|
||||
r = crypto::generate_eth_signature(ft.tx_id, eth_sk, eth_sig); // signing with old eth key
|
||||
CHECK_AND_ASSERT_MES(r, false, "generate_eth_signature failed");
|
||||
|
||||
transaction to_tx{};
|
||||
transfers_unlocked = false;
|
||||
miner_wlt->submit_externally_signed_asset_tx(ft, eth_sig, true, to_tx, transfers_unlocked);
|
||||
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
miner_wlt->refresh();
|
||||
|
||||
//
|
||||
// emit #2
|
||||
//
|
||||
emit_amount = adb.total_max_supply - total_amount_expected; // up to max
|
||||
destinations.clear();
|
||||
destinations.emplace_back(emit_amount, m_accounts[ALICE_ACC_IDX].get_public_address(), null_pkey); // asset
|
||||
ft = finalized_tx{};
|
||||
miner_wlt->emit_asset(asset_id, destinations, ft);
|
||||
|
||||
// make sure the transaction didn't get into tx pool (as it's not fully signed)
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// make sure emit transaction cannot be added to the transaction pool (it's not fully signed atm)
|
||||
r = c.get_tx_pool().add_tx(ft.tx, tvc, false);
|
||||
CHECK_AND_ASSERT_MES(!r, false, "unsigned emit tx was able to be added to the pool");
|
||||
|
||||
// sign asset emit operation with ETH signature
|
||||
eth_sig = crypto::eth_signature{};
|
||||
r = crypto::generate_eth_signature(ft.tx_id, eth_sk_2, eth_sig); // note using ETH key #2 here
|
||||
CHECK_AND_ASSERT_MES(r, false, "generate_eth_signature failed");
|
||||
|
||||
emit_tx = transaction{};
|
||||
transfers_unlocked = false;
|
||||
miner_wlt->submit_externally_signed_asset_tx(ft, eth_sig, true, emit_tx, transfers_unlocked);
|
||||
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// Alice checks her asset balance
|
||||
alice_wlt->refresh(blocks_fetched);
|
||||
CHECK_AND_ASSERT_EQ(blocks_fetched, 3);
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", adb.total_max_supply, asset_id, adb.decimal_point), false, "");
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", MK_TEST_COINS(100) - TESTS_DEFAULT_FEE), false, "");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
eth_signed_asset_via_rpc::eth_signed_asset_via_rpc()
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(eth_signed_asset_via_rpc, c1);
|
||||
}
|
||||
|
||||
bool eth_signed_asset_via_rpc::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
//
|
||||
// Test idea: make sure register, emit, and ownership transfer operations with external signing (ETH signature)
|
||||
// can be done entirely using JSON RPC calls (both, the core RPC and the wallet RPC)
|
||||
//
|
||||
uint64_t ts = test_core_time::get_time();
|
||||
m_accounts.resize(TOTAL_ACCS_COUNT);
|
||||
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts);
|
||||
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts);
|
||||
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts);
|
||||
miner_acc.generate();
|
||||
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts);
|
||||
DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks
|
||||
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
|
||||
|
||||
DO_CALLBACK(events, "c1");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eth_signed_asset_via_rpc::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
bool r = false;
|
||||
|
||||
crypto::eth_secret_key eth_sk{};
|
||||
crypto::eth_public_key eth_pk{};
|
||||
r = crypto::generate_eth_key_pair(eth_sk, eth_pk);
|
||||
CHECK_AND_ASSERT_MES(r, false, "generate_eth_key_pair failed");
|
||||
|
||||
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
|
||||
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
|
||||
miner_wlt->refresh();
|
||||
alice_wlt->refresh();
|
||||
|
||||
// wallet RPC server
|
||||
tools::wallet_rpc_server miner_wlt_rpc(miner_wlt);
|
||||
epee::json_rpc::error jerr{};
|
||||
tools::wallet_rpc_server::connection_context ctx{};
|
||||
|
||||
// core RPC server
|
||||
currency::t_currency_protocol_handler<currency::core> m_cprotocol(c, nullptr);
|
||||
nodetool::node_server<currency::t_currency_protocol_handler<currency::core> > p2p(m_cprotocol);
|
||||
bc_services::bc_offers_service of(nullptr);
|
||||
currency::core_rpc_server core_rpc_wrapper(c, p2p, of);
|
||||
core_rpc_wrapper.set_ignore_connectivity_status(true);
|
||||
|
||||
// asset description
|
||||
asset_descriptor_base adb{};
|
||||
adb.decimal_point = 2;
|
||||
adb.total_max_supply = 45000;
|
||||
adb.full_name = "P450";
|
||||
adb.ticker = "P450";
|
||||
adb.owner_eth_pub_key = eth_pk; // note setting owner eth pub key here
|
||||
|
||||
uint64_t initial_register_amount = 10000;
|
||||
|
||||
// 1. Miner deploys initial amount of the asset (all go to Alice)
|
||||
// deploy operation don't require eth proof and therefore the corresponding tx will be generated and added to the tx pool as usual
|
||||
|
||||
tools::wallet_public::COMMAND_ASSETS_DEPLOY::request deploy_req{};
|
||||
deploy_req.asset_descriptor = adb;
|
||||
deploy_req.destinations.push_back(tools::wallet_public::transfer_destination{initial_register_amount, m_accounts[ALICE_ACC_IDX].get_public_address_str(), null_pkey});
|
||||
deploy_req.do_not_split_destinations = false;
|
||||
tools::wallet_public::COMMAND_ASSETS_DEPLOY::response deploy_resp{};
|
||||
r = miner_wlt_rpc.on_asset_deploy(deploy_req, deploy_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC on_asset_deploy failed");
|
||||
|
||||
const crypto::public_key asset_id = deploy_resp.new_asset_id;
|
||||
LOG_PRINT_GREEN_L0("Asset " << asset_id << " was successfully deployed with tx " << deploy_resp.tx_id);
|
||||
|
||||
// make sure tx was added to the pool
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// confirm the tx with a block
|
||||
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c), false, "");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
size_t blocks_fetched = 0;
|
||||
miner_wlt->refresh(blocks_fetched);
|
||||
CHECK_AND_ASSERT_EQ(blocks_fetched, 1);
|
||||
|
||||
// Alice checks her asset balance
|
||||
alice_wlt->refresh(blocks_fetched);
|
||||
CHECK_AND_ASSERT_EQ(blocks_fetched, 1);
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", initial_register_amount, asset_id, adb.decimal_point), false, "");
|
||||
|
||||
|
||||
//
|
||||
// 2. Miner emits additional amount of the asset and transfers it to Alice
|
||||
//
|
||||
uint64_t additional_emit_amount = 20000;
|
||||
uint64_t total_asset_amount = initial_register_amount + additional_emit_amount;
|
||||
|
||||
tools::wallet_public::COMMAND_ASSETS_EMIT::request emit_req{};
|
||||
emit_req.asset_id = asset_id;
|
||||
emit_req.destinations.push_back(tools::wallet_public::transfer_destination{additional_emit_amount, m_accounts[ALICE_ACC_IDX].get_public_address_str(), asset_id});
|
||||
emit_req.do_not_split_destinations = false;
|
||||
|
||||
tools::wallet_public::COMMAND_ASSETS_EMIT::response emit_resp{};
|
||||
r = miner_wlt_rpc.on_asset_emit(emit_req, emit_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC on_asset_emit failed: " << jerr.message);
|
||||
|
||||
// make sure tx was NOT added to the pool (because it's only partially signed)
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// unserialize transaction that we got from RPC
|
||||
transaction emit_tx{};
|
||||
CHECK_AND_ASSERT_MES(emit_resp.data_for_external_signing.has_value(), false, "data_for_external_signing has no value");
|
||||
r = t_unserializable_object_from_blob(emit_tx, emit_resp.data_for_external_signing->unsigned_tx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "t_unserializable_object_from_blob failed");
|
||||
CHECK_AND_ASSERT_EQ(get_transaction_hash(emit_tx), emit_resp.tx_id);
|
||||
|
||||
// make sure emit transaction cannot be added to the transaction pool (it's not fully signed atm)
|
||||
tx_verification_context tvc{};
|
||||
r = c.get_tx_pool().add_tx(emit_tx, tvc, false);
|
||||
CHECK_AND_ASSERT_MES(!r, false, "emit tx was able to be added to the pool");
|
||||
|
||||
//
|
||||
// decrypt emission transaction outputs prior to ETH signing to make sure it's valid
|
||||
//
|
||||
currency::COMMAND_RPC_DECRYPT_TX_DETAILS::request decrypt_req{};
|
||||
decrypt_req.tx_secret_key = emit_resp.data_for_external_signing->tx_secret_key;
|
||||
decrypt_req.tx_blob = epee::string_encoding::base64_encode(emit_resp.data_for_external_signing->unsigned_tx);
|
||||
// note: decrypt_req.outputs_addresses can be populated using emit_resp.data_for_external_signing->outputs_addresses but we fill it manually here
|
||||
decrypt_req.outputs_addresses.push_back(m_accounts[MINER_ACC_IDX].get_public_address_str()); // we expect that the first output is the cashback and addressed to miner
|
||||
for(size_t i = 0, size = emit_tx.vout.size() - 1; i < size; ++i)
|
||||
decrypt_req.outputs_addresses.push_back(m_accounts[ALICE_ACC_IDX].get_public_address_str()); // we expect all other outputs are asset emission and addresses to Alice
|
||||
currency::COMMAND_RPC_DECRYPT_TX_DETAILS::response decrypt_resp{};
|
||||
r = core_rpc_wrapper.on_decrypt_tx_details(decrypt_req, decrypt_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC on_decrypt_tx_details failed: " << jerr.message);
|
||||
// make sure that verified_tx_id is the one we expect
|
||||
CHECK_AND_ASSERT_EQ(decrypt_resp.verified_tx_id, emit_resp.tx_id);
|
||||
|
||||
// after a successfull tx outputs decryption, examine them
|
||||
CHECK_AND_ASSERT_EQ(decrypt_resp.decoded_outputs.size(), emit_tx.vout.size());
|
||||
uint64_t decrypted_emission_sum = 0;
|
||||
for(auto& el : decrypt_resp.decoded_outputs)
|
||||
{
|
||||
if (el.asset_id == asset_id)
|
||||
decrypted_emission_sum += el.amount;
|
||||
else
|
||||
CHECK_AND_ASSERT_EQ(el.asset_id, native_coin_asset_id);
|
||||
}
|
||||
// make sure the transaction emits the expected amount of asset
|
||||
CHECK_AND_ASSERT_EQ(decrypted_emission_sum, additional_emit_amount);
|
||||
|
||||
LOG_PRINT_YELLOW(decrypt_resp.tx_in_json, LOG_LEVEL_0); // tx with still missing ownership proof
|
||||
|
||||
//
|
||||
// as everything is allright, sign emit_tx with ETH signature.
|
||||
//
|
||||
crypto::eth_signature eth_sig{};
|
||||
crypto::generate_eth_signature(emit_resp.tx_id, eth_sk, eth_sig);
|
||||
// instant verification, just in case
|
||||
r = crypto::verify_eth_signature(emit_resp.tx_id, eth_pk, eth_sig);
|
||||
CHECK_AND_ASSERT_MES(r, false, "verify_eth_signature failed");
|
||||
|
||||
//
|
||||
// send ETH signature alogn with all previous data to a wallet RPC call for final tx assembling and broadcasting
|
||||
//
|
||||
tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request send_signed_req{};
|
||||
send_signed_req.unsigned_tx = emit_resp.data_for_external_signing->unsigned_tx;
|
||||
send_signed_req.eth_sig = eth_sig;
|
||||
send_signed_req.expected_tx_id = decrypt_resp.verified_tx_id;
|
||||
send_signed_req.finalized_tx = emit_resp.data_for_external_signing->finalized_tx;
|
||||
send_signed_req.unlock_transfers_on_fail = true;
|
||||
tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response send_signed_resp{};
|
||||
r = miner_wlt_rpc.on_asset_send_ext_signed_tx(send_signed_req, send_signed_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: " << jerr.message);
|
||||
|
||||
// make sure tx was broadcasted
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// confirm the tx with a block
|
||||
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c), false, "");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// Alice checks her asset balance
|
||||
alice_wlt->refresh(blocks_fetched);
|
||||
CHECK_AND_ASSERT_EQ(blocks_fetched, 1);
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", total_asset_amount, asset_id, adb.decimal_point), false, "");
|
||||
|
||||
|
||||
//
|
||||
// 3. Miner transfer ownership of the asset to another ETH key (#2)
|
||||
//
|
||||
crypto::eth_secret_key eth_sk_2{};
|
||||
crypto::eth_public_key eth_pk_2{};
|
||||
r = crypto::generate_eth_key_pair(eth_sk_2, eth_pk_2);
|
||||
CHECK_AND_ASSERT_MES(r, false, "generate_eth_key_pair failed");
|
||||
|
||||
tools::wallet_public::COMMAND_ASSETS_UPDATE::request to_req{}; // 'to' means transfer of ownership
|
||||
to_req.asset_id = asset_id;
|
||||
{
|
||||
// request the most recent asset info from core to correctly fill to_req
|
||||
currency::COMMAND_RPC_GET_ASSET_INFO::request req{};
|
||||
req.asset_id = asset_id;
|
||||
currency::COMMAND_RPC_GET_ASSET_INFO::response resp{};
|
||||
CHECK_AND_ASSERT_MES(core_rpc_wrapper.on_get_asset_info(req, resp, ctx), false, "on_get_asset_info failed");
|
||||
to_req.asset_descriptor = resp.asset_descriptor;
|
||||
}
|
||||
to_req.asset_descriptor.owner_eth_pub_key = eth_pk_2; // new owner, note using another ETH pub key
|
||||
to_req.asset_descriptor.meta_info = "owner: eth_pk_2"; // it's also possible to change meta_info with this update operation
|
||||
tools::wallet_public::COMMAND_ASSETS_UPDATE::response to_resp{};
|
||||
r = miner_wlt_rpc.on_asset_update(to_req, to_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: " << jerr.message);
|
||||
|
||||
// make sure tx was NOT added to the pool (because it's only partially signed)
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
CHECK_AND_ASSERT_MES(to_resp.data_for_external_signing.has_value(), false, "data_for_external_signing has no value");
|
||||
transaction to_tx{};
|
||||
r = t_unserializable_object_from_blob(to_tx, to_resp.data_for_external_signing->unsigned_tx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "t_unserializable_object_from_blob failed");
|
||||
CHECK_AND_ASSERT_EQ(get_transaction_hash(to_tx), to_resp.tx_id);
|
||||
|
||||
// make sure ownership transferring transaction cannot be added to the transaction pool (it's not fully signed atm)
|
||||
tvc = tx_verification_context{};
|
||||
r = c.get_tx_pool().add_tx(to_tx, tvc, false);
|
||||
CHECK_AND_ASSERT_MES(!r, false, "ownership transferring tx was able to be added to the pool");
|
||||
|
||||
//
|
||||
// decrypt ownership transfer transaction prior to ETH signing to make sure it's valid
|
||||
//
|
||||
decrypt_req = currency::COMMAND_RPC_DECRYPT_TX_DETAILS::request{};
|
||||
decrypt_req.tx_secret_key = to_resp.data_for_external_signing->tx_secret_key;
|
||||
decrypt_req.tx_blob = epee::string_encoding::base64_encode(to_resp.data_for_external_signing->unsigned_tx);
|
||||
// note: decrypt_req.outputs_addresses can be populated using to_resp.data_for_external_signing->outputs_addresses but we fill it manually here
|
||||
for(size_t i = 0, size = to_tx.vout.size(); i < size; ++i)
|
||||
decrypt_req.outputs_addresses.push_back(m_accounts[MINER_ACC_IDX].get_public_address_str()); // we expect all outputs goes to Miner
|
||||
decrypt_resp = currency::COMMAND_RPC_DECRYPT_TX_DETAILS::response{};
|
||||
r = core_rpc_wrapper.on_decrypt_tx_details(decrypt_req, decrypt_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC on_decrypt_tx_details failed: " << jerr.message);
|
||||
// make sure that verified_tx_id is the one we expect
|
||||
CHECK_AND_ASSERT_EQ(decrypt_resp.verified_tx_id, to_resp.tx_id);
|
||||
|
||||
// after a successfull tx outputs decryption, examine them
|
||||
CHECK_AND_ASSERT_EQ(decrypt_resp.decoded_outputs.size(), to_tx.vout.size());
|
||||
uint64_t asset_sum = 0;
|
||||
for(auto& el : decrypt_resp.decoded_outputs)
|
||||
{
|
||||
if (el.asset_id != native_coin_asset_id)
|
||||
asset_sum += el.amount;
|
||||
}
|
||||
// make sure the transaction don't send assets
|
||||
CHECK_AND_ASSERT_EQ(asset_sum, 0);
|
||||
|
||||
// make sure this is an ownership transfer transaction and the ownership is correctly transferred:
|
||||
// Note: this check could also be done by examination of decrypt_resp.tx_in_json
|
||||
asset_descriptor_operation* pado = get_type_in_variant_container<asset_descriptor_operation>(to_tx.extra);
|
||||
CHECK_AND_ASSERT_NEQ(pado, 0);
|
||||
CHECK_AND_ASSERT_EQ(pado->operation_type, ASSET_DESCRIPTOR_OPERATION_UPDATE);
|
||||
CHECK_AND_ASSERT_EQ(pado->opt_asset_id.has_value(), true);
|
||||
CHECK_AND_ASSERT_EQ(pado->opt_asset_id.get(), asset_id);
|
||||
CHECK_AND_ASSERT_EQ(pado->descriptor.owner_eth_pub_key.has_value(), true);
|
||||
CHECK_AND_ASSERT_EQ(pado->descriptor.owner_eth_pub_key.get(), eth_pk_2); // the most important condition for an ownership transfer
|
||||
// other fileds of pado->descriptor may also be checked here
|
||||
|
||||
//
|
||||
// as everything is allright, sign to_tx with ETH signature.
|
||||
//
|
||||
crypto::eth_signature to_eth_sig{};
|
||||
crypto::generate_eth_signature(to_resp.tx_id, eth_sk, to_eth_sig); // note using old ETH secret key here, because this tx must be signed with the original owner
|
||||
// instant verification, just in case
|
||||
r = crypto::verify_eth_signature(to_resp.tx_id, eth_pk, to_eth_sig);
|
||||
CHECK_AND_ASSERT_MES(r, false, "verify_eth_signature failed");
|
||||
|
||||
//
|
||||
// send ETH signature along with all previous data to a wallet RPC call for final tx assembling and broadcasting
|
||||
//
|
||||
send_signed_req = tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request{};
|
||||
send_signed_req.unsigned_tx = to_resp.data_for_external_signing->unsigned_tx;
|
||||
send_signed_req.eth_sig = to_eth_sig;
|
||||
send_signed_req.expected_tx_id = decrypt_resp.verified_tx_id;
|
||||
send_signed_req.finalized_tx = to_resp.data_for_external_signing->finalized_tx;
|
||||
send_signed_req.unlock_transfers_on_fail = true;
|
||||
send_signed_resp = tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response{};
|
||||
r = miner_wlt_rpc.on_asset_send_ext_signed_tx(send_signed_req, send_signed_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: " << jerr.message);
|
||||
|
||||
// make sure tx was broadcasted
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// confirm the tx with a block
|
||||
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c), false, "");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// Alice checks her asset balance, it shouldn't change
|
||||
alice_wlt->refresh(blocks_fetched);
|
||||
CHECK_AND_ASSERT_EQ(blocks_fetched, 1);
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", total_asset_amount, asset_id, adb.decimal_point), false, "");
|
||||
|
||||
miner_wlt->refresh();
|
||||
|
||||
//
|
||||
// 4. Miner emits additional amount of the asset (signing it with ETH key #2) and transfers it to Bob
|
||||
//
|
||||
additional_emit_amount = 15000;
|
||||
uint64_t alice_amount = total_asset_amount;
|
||||
total_asset_amount += additional_emit_amount;
|
||||
|
||||
emit_req = tools::wallet_public::COMMAND_ASSETS_EMIT::request{};
|
||||
emit_req.asset_id = asset_id;
|
||||
emit_req.destinations.push_back(tools::wallet_public::transfer_destination{additional_emit_amount, m_accounts[BOB_ACC_IDX].get_public_address_str(), asset_id});
|
||||
emit_req.do_not_split_destinations = false;
|
||||
|
||||
emit_resp = tools::wallet_public::COMMAND_ASSETS_EMIT::response{};
|
||||
r = miner_wlt_rpc.on_asset_emit(emit_req, emit_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC on_asset_emit failed: " << jerr.message);
|
||||
|
||||
// make sure tx was NOT added to the pool (because it's only partially signed)
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// unserialize transaction that we got from RPC
|
||||
emit_tx = transaction{};
|
||||
CHECK_AND_ASSERT_MES(emit_resp.data_for_external_signing.has_value(), false, "data_for_external_signing has no value");
|
||||
r = t_unserializable_object_from_blob(emit_tx, emit_resp.data_for_external_signing->unsigned_tx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "t_unserializable_object_from_blob failed");
|
||||
CHECK_AND_ASSERT_EQ(get_transaction_hash(emit_tx), emit_resp.tx_id);
|
||||
|
||||
// make sure emit transaction cannot be added to the transaction pool (it's not fully signed atm)
|
||||
tvc = tx_verification_context{};
|
||||
r = c.get_tx_pool().add_tx(emit_tx, tvc, false);
|
||||
CHECK_AND_ASSERT_MES(!r, false, "emit tx was able to be added to the pool");
|
||||
|
||||
//
|
||||
// decrypt emission transaction outputs prior to ETH signing to make sure it's valid
|
||||
//
|
||||
decrypt_req = currency::COMMAND_RPC_DECRYPT_TX_DETAILS::request{};
|
||||
decrypt_req.tx_secret_key = emit_resp.data_for_external_signing->tx_secret_key;
|
||||
decrypt_req.tx_blob = epee::string_encoding::base64_encode(emit_resp.data_for_external_signing->unsigned_tx);
|
||||
// note: decrypt_req.outputs_addresses can be populated using emit_resp.data_for_external_signing->outputs_addresses but we fill it manually here
|
||||
decrypt_req.outputs_addresses.push_back(m_accounts[MINER_ACC_IDX].get_public_address_str()); // we expect that the first output is the cashback and addressed to miner
|
||||
for(size_t i = 0, size = emit_tx.vout.size() - 1; i < size; ++i)
|
||||
decrypt_req.outputs_addresses.push_back(m_accounts[BOB_ACC_IDX].get_public_address_str()); // we expect all other outputs are asset emission and addresses to Bob
|
||||
decrypt_resp = currency::COMMAND_RPC_DECRYPT_TX_DETAILS::response{};
|
||||
r = core_rpc_wrapper.on_decrypt_tx_details(decrypt_req, decrypt_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC on_decrypt_tx_details failed: " << jerr.message);
|
||||
// make sure that verified_tx_id is the one we expect
|
||||
CHECK_AND_ASSERT_EQ(decrypt_resp.verified_tx_id, emit_resp.tx_id);
|
||||
|
||||
// after a successfull tx outputs decryption, examine them
|
||||
CHECK_AND_ASSERT_EQ(decrypt_resp.decoded_outputs.size(), emit_tx.vout.size());
|
||||
decrypted_emission_sum = 0;
|
||||
for(auto& el : decrypt_resp.decoded_outputs)
|
||||
{
|
||||
if (el.asset_id == asset_id)
|
||||
decrypted_emission_sum += el.amount;
|
||||
else
|
||||
CHECK_AND_ASSERT_EQ(el.asset_id, native_coin_asset_id);
|
||||
}
|
||||
// make sure the transaction emits the expected amount of asset
|
||||
CHECK_AND_ASSERT_EQ(decrypted_emission_sum, additional_emit_amount);
|
||||
|
||||
//
|
||||
// as everything is allright, sign emit_tx with ETH signature.
|
||||
//
|
||||
eth_sig = crypto::eth_signature{};
|
||||
crypto::generate_eth_signature(emit_resp.tx_id, eth_sk_2, eth_sig); // note using ETH key #2
|
||||
// instant verification, just in case
|
||||
r = crypto::verify_eth_signature(emit_resp.tx_id, eth_pk_2, eth_sig);
|
||||
CHECK_AND_ASSERT_MES(r, false, "verify_eth_signature failed");
|
||||
|
||||
//
|
||||
// send ETH signature alogn with all previous data to a wallet RPC call for final tx assembling and broadcasting
|
||||
//
|
||||
send_signed_req = tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request{};
|
||||
send_signed_req.unsigned_tx = emit_resp.data_for_external_signing->unsigned_tx;
|
||||
send_signed_req.eth_sig = eth_sig;
|
||||
send_signed_req.expected_tx_id = decrypt_resp.verified_tx_id;
|
||||
send_signed_req.finalized_tx = emit_resp.data_for_external_signing->finalized_tx;
|
||||
send_signed_req.unlock_transfers_on_fail = true;
|
||||
send_signed_resp = tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response{};
|
||||
r = miner_wlt_rpc.on_asset_send_ext_signed_tx(send_signed_req, send_signed_resp, jerr, ctx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: " << jerr.message);
|
||||
|
||||
// make sure tx was broadcasted
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// confirm the tx with a block
|
||||
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c), false, "");
|
||||
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
|
||||
|
||||
// Alice checks her asset balance, it shouldn't change
|
||||
alice_wlt->refresh(blocks_fetched);
|
||||
CHECK_AND_ASSERT_EQ(blocks_fetched, 1);
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", alice_amount, asset_id, adb.decimal_point), false, "");
|
||||
|
||||
// Bob checks his asset balance
|
||||
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX);
|
||||
bob_wlt->refresh();
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", additional_emit_amount, asset_id, adb.decimal_point), false, "");
|
||||
|
||||
// finally, check asset's current supply
|
||||
asset_descriptor_base adb_temp{};
|
||||
r = c.get_blockchain_storage().get_asset_info(asset_id, adb_temp);
|
||||
CHECK_AND_ASSERT_MES(r, false, "get_asset_info failed");
|
||||
CHECK_AND_ASSERT_EQ(adb_temp.current_supply, total_asset_amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -81,3 +81,17 @@ 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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -58,6 +61,9 @@ struct wallet_test : virtual public test_chain_unit_enchanced
|
|||
w->set_core_proxy(m_core_proxy);
|
||||
w->set_disable_tor_relay(true);
|
||||
return w;
|
||||
|
||||
#undef LOCAL_HOST_CSTR
|
||||
#undef LOCAL_PORT_CSTR
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue