1
0
Fork 0
forked from lthn/blockchain
blockchain/src/wallet/wallet2.h
2023-03-29 20:26:46 +02:00

1490 lines
67 KiB
C++

// Copyright (c) 2014-2022 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <memory>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#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>
#include "include_base_utils.h"
#include "profile_tools.h"
#include "sync_locked_object.h"
#include "currency_core/currency_boost_serialization.h"
#include "currency_core/account_boost_serialization.h"
#include "currency_core/currency_format_utils.h"
#include "common/make_hashable.h"
#include "wallet_public_structs_defs.h"
#include "currency_core/currency_format_utils.h"
#include "common/unordered_containers_boost_serialization.h"
#include "common/atomics_boost_serialization.h"
#include "storages/portable_storage_template_helper.h"
#include "crypto/chacha8.h"
#include "crypto/hash.h"
#include "core_rpc_proxy.h"
#include "core_default_rpc_proxy.h"
#include "wallet_errors.h"
#include "eos/portable_archive.hpp"
#include "currency_core/core_runtime_config.h"
#include "currency_core/bc_offers_serialization.h"
#include "currency_core/bc_escrow_service.h"
#include "common/pod_array_file_container.h"
#include "wallet_chain_shortener.h"
#include "tor-connect/torlib/tor_lib_iface.h"
#include "currency_core/pos_mining.h"
#include "view_iface.h"
#define WALLET_DEFAULT_TX_SPENDABLE_AGE 10
#define WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL 1
#define WALLET_DEFAULT_POS_MINT_PACKING_SIZE 100
#define WALLET_TRANSFER_DETAIL_FLAG_SPENT uint32_t(1 << 0)
#define WALLET_TRANSFER_DETAIL_FLAG_BLOCKED uint32_t(1 << 1)
#define WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION uint32_t(1 << 2)
#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
const uint64_t WALLET_MINIMUM_HEIGHT_UNSET_CONST = std::numeric_limits<uint64_t>::max();
#undef LOG_DEFAULT_CHANNEL
#define LOG_DEFAULT_CHANNEL "wallet"
// wallet-specific logging functions
#define WLT_LOG(msg, level) LOG_PRINT("[W:" << m_log_prefix << "] " << msg, level)
#define WLT_LOG_L0(msg) LOG_PRINT_L0("[W:" << m_log_prefix << "] " << msg)
#define WLT_LOG_L1(msg) LOG_PRINT_L1("[W:" << m_log_prefix << "] " << msg)
#define WLT_LOG_L2(msg) LOG_PRINT_L2("[W:" << m_log_prefix << "] " << msg)
#define WLT_LOG_L3(msg) LOG_PRINT_L3("[W:" << m_log_prefix << "] " << msg)
#define WLT_LOG_L4(msg) LOG_PRINT_L4("[W:" << m_log_prefix << "] " << msg)
#define WLT_LOG_ERROR(msg) LOG_ERROR("[W:" << m_log_prefix << "] " << msg)
#define WLT_LOG_BLUE(msg, log_level) LOG_PRINT_BLUE("[W:" << m_log_prefix << "] " << msg, log_level)
#define WLT_LOG_CYAN(msg, log_level) LOG_PRINT_CYAN("[W:" << m_log_prefix << "] " << msg, log_level)
#define WLT_LOG_GREEN(msg, log_level) LOG_PRINT_GREEN("[W:" << m_log_prefix << "] " << msg, log_level)
#define WLT_LOG_MAGENTA(msg, log_level) LOG_PRINT_MAGENTA("[W:" << m_log_prefix << "] " << msg, log_level)
#define WLT_LOG_RED(msg, log_level) LOG_PRINT_RED("[W:" << m_log_prefix << "] " << msg, log_level)
#define WLT_LOG_YELLOW(msg, log_level) LOG_PRINT_YELLOW("[W:" << m_log_prefix << "] " << msg, log_level)
#define WLT_CHECK_AND_ASSERT_MES(expr, ret, msg) CHECK_AND_ASSERT_MES(expr, ret, "[W:" << m_log_prefix << "] " << msg)
#define WLT_CHECK_AND_ASSERT_MES_NO_RET(expr, msg) CHECK_AND_ASSERT_MES_NO_RET(expr, "[W:" << m_log_prefix << "] " << msg)
#define WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(cond, msg) THROW_IF_FALSE_WALLET_INT_ERR_EX(cond, "[W:" << m_log_prefix << "] " << msg)
#define WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(cond, msg) THROW_IF_FALSE_WALLET_CMN_ERR_EX(cond, "[W:" << m_log_prefix << "] " << msg)
#define WLT_THROW_IF_FALSE_WALLET_EX_MES(cond, exception_t, msg, ...) THROW_IF_FALSE_WALLET_EX_MES(cond, exception_t, "[W:" << m_log_prefix << "] " << msg, ## __VA_ARGS__)
class test_generator;
namespace tools
{
#pragma pack(push, 1)
struct wallet_file_binary_header
{
uint64_t m_signature;
uint16_t m_cb_keys;
//uint64_t m_cb_body; <-- this field never used, soo replace it with two other variables "m_ver" + and "m_reserved"
uint32_t m_ver;
uint32_t m_reserved; //for future use
};
#pragma pack (pop)
struct money_transfer2_details
{
std::vector<size_t> receive_indices;
std::vector<size_t> spent_indices;
};
class i_wallet2_callback
{
public:
enum message_severity { ms_red, ms_yellow, ms_normal };
virtual ~i_wallet2_callback() = default;
virtual void on_new_block(uint64_t /*height*/, const currency::block& /*block*/) {}
virtual void on_transfer2(const wallet_public::wallet_transfer_info& wti, const std::list<wallet_public::asset_balance_entry>& balances, uint64_t total_mined) {}
virtual void on_pos_block_found(const currency::block& /*block*/) {}
virtual void on_sync_progress(const uint64_t& /*percents*/) {}
virtual void on_transfer_canceled(const wallet_public::wallet_transfer_info& wti) {}
virtual void on_message(message_severity /*severity*/, const std::string& /*m*/) {}
virtual void on_tor_status_change(const std::string& state) {}
//mw api
virtual void on_mw_get_wallets(std::vector<wallet_public::wallet_entry_info>& wallets) {}
virtual bool on_mw_select_wallet(uint64_t wallet_id) {}
};
struct tx_dust_policy
{
uint64_t dust_threshold = 0;
bool add_to_fee = false;
currency::account_public_address addr_for_dust;
tx_dust_policy(uint64_t a_dust_threshold = DEFAULT_DUST_THRESHOLD, bool an_add_to_fee = true, currency::account_public_address an_addr_for_dust = currency::account_public_address())
: dust_threshold(a_dust_threshold)
, add_to_fee(an_add_to_fee)
, addr_for_dust(an_addr_for_dust)
{
}
BEGIN_SERIALIZE_OBJECT()
FIELD(dust_threshold)
FIELD(add_to_fee)
FIELD(addr_for_dust)
END_SERIALIZE()
};
class test_generator;
#pragma pack(push, 1)
struct out_key_to_ki
{
crypto::public_key out_key;
crypto::key_image key_image;
};
#pragma pack(pop)
typedef tools::pod_array_file_container<out_key_to_ki> pending_ki_file_container_t;
namespace detail
{
//----------------------------------------------------------------------------------------------------
inline void digit_split_strategy(const std::vector<currency::tx_destination_entry>& dsts,
const currency::tx_destination_entry& change_dst, uint64_t dust_threshold,
std::vector<currency::tx_destination_entry>& splitted_dsts, uint64_t& dust, uint64_t max_output_allowed)
{
splitted_dsts.clear();
dust = 0;
for(auto& de : dsts)
{
if (de.addr.size() > 1)
{
//for multisig we don't split
splitted_dsts.push_back(de);
}
else if (de.htlc_options.expiration != 0)
{
//for htlc we don't do split
splitted_dsts.push_back(de);
}
else
{
currency::decompose_amount_into_digits(de.amount, dust_threshold,
[&](uint64_t chunk) { splitted_dsts.push_back(currency::tx_destination_entry(chunk, de.addr, de.asset_id)); },
[&](uint64_t a_dust) { splitted_dsts.push_back(currency::tx_destination_entry(a_dust, de.addr, de.asset_id)); }, max_output_allowed);
}
}
if (change_dst.amount > 0)
{
if (change_dst.addr.size() > 1)
{
//for multisig we don't split
splitted_dsts.push_back(change_dst);
}
else
{
currency::decompose_amount_into_digits(change_dst.amount, dust_threshold,
[&](uint64_t chunk) { splitted_dsts.push_back(currency::tx_destination_entry(chunk, change_dst.addr)); },
[&](uint64_t a_dust) { dust = a_dust; }, max_output_allowed);
}
}
}
//----------------------------------------------------------------------------------------------------
inline void null_split_strategy(const std::vector<currency::tx_destination_entry>& dsts,
const currency::tx_destination_entry& change_dst, uint64_t dust_threshold,
std::vector<currency::tx_destination_entry>& splitted_dsts, uint64_t& dust, uint64_t max_output_allowed)
{
splitted_dsts = dsts;
dust = 0;
uint64_t change = change_dst.amount;
if (0 < dust_threshold)
{
for (uint64_t order = 10; order <= 10 * dust_threshold; order *= 10)
{
uint64_t dust_candidate = change_dst.amount % order;
uint64_t change_candidate = (change_dst.amount / order) * order;
if (dust_candidate <= dust_threshold)
{
dust = dust_candidate;
change = change_candidate;
}
else
{
break;
}
}
}
if (0 != change)
{
splitted_dsts.push_back(currency::tx_destination_entry(change, change_dst.addr));
}
}
//----------------------------------------------------------------------------------------------------
inline void void_split_strategy(const std::vector<currency::tx_destination_entry>& dsts,
const currency::tx_destination_entry& change_dst, uint64_t dust_threshold,
std::vector<currency::tx_destination_entry>& splitted_dsts, uint64_t& dust, uint64_t max_output_allowed)
{
splitted_dsts.insert(splitted_dsts.end(), dsts.begin(), dsts.end());
if (change_dst.amount > 0)
splitted_dsts.push_back(change_dst);
}
//----------------------------------------------------------------------------------------------------
enum split_strategy_id_t { ssi_none = 0, ssi_digit = 1, ssi_null = 2, ssi_void = 3 };
//----------------------------------------------------------------------------------------------------
inline bool apply_split_strategy_by_id(split_strategy_id_t id, const std::vector<currency::tx_destination_entry>& dsts,
const currency::tx_destination_entry& change_dst, uint64_t dust_threshold,
std::vector<currency::tx_destination_entry>& splitted_dsts, uint64_t& dust, uint64_t max_output_allowed)
{
switch (id)
{
case ssi_digit:
digit_split_strategy(dsts, change_dst, dust_threshold, splitted_dsts, dust, max_output_allowed);
return true;
case ssi_null:
null_split_strategy(dsts, change_dst, dust_threshold, splitted_dsts, dust, max_output_allowed);
return true;
case ssi_void:
void_split_strategy(dsts, change_dst, dust_threshold, splitted_dsts, dust, max_output_allowed);
return true;
default:
return false;
}
}
} // namespace detail
struct construct_tx_param
{
// preparing data for tx
std::vector<currency::tx_destination_entry> dsts;
size_t fake_outputs_count = 0;
uint64_t fee = 0;
tx_dust_policy dust_policy;
crypto::hash multisig_id = currency::null_hash;
uint8_t flags = 0;
uint8_t split_strategy_id = 0;
bool mark_tx_as_complete = false;
crypto::hash htlc_tx_id;
std::string htlc_origin;
// constructing tx
uint64_t unlock_time = 0;
std::vector<currency::extra_v> extra;
std::vector<currency::attachment_v> attachments;
currency::account_public_address crypt_address;
uint8_t tx_outs_attr = 0;
bool shuffle = false;
bool perform_packing = false;
};
struct mode_separate_context
{
currency::transaction tx_for_mode_separate;
view::ionic_swap_proposal_info proposal;
};
struct selection_for_amount
{
uint64_t needed_amount = 0;
uint64_t found_amount = 0;
//std::vector<uint64_t> selected_indicies;
};
typedef std::unordered_map<crypto::hash, selection_for_amount> assets_selection_context;
class wallet2: public tools::tor::t_transport_state_notifier
{
wallet2(const wallet2&) = delete;
public:
wallet2();
static std::string transfer_flags_to_str(uint32_t flags);
static std::string transform_tx_to_str(const currency::transaction& tx);
static currency::transaction transform_str_to_tx(const std::string& tx_str);
struct transaction_wallet_info
{
uint64_t m_block_height = 0;
uint64_t m_block_timestamp = 0;
currency::transaction m_tx;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_block_height)
KV_SERIALIZE(m_block_timestamp)
KV_SERIALIZE_CUSTOM(m_tx, std::string, tools::wallet2::transform_tx_to_str, tools::wallet2::transform_str_to_tx)
END_KV_SERIALIZE_MAP()
};
static const transaction_wallet_info& transform_ptr_to_value(const std::shared_ptr<transaction_wallet_info>& a);
static std::shared_ptr<transaction_wallet_info> transform_value_to_ptr(const transaction_wallet_info& d);
struct transfer_details_base;
static uint64_t transfer_details_base_to_amount(const transfer_details_base& tdb);
static std::string transfer_details_base_to_tx_hash(const transfer_details_base& tdb);
struct transfer_details_base
{
std::shared_ptr<transaction_wallet_info> m_ptx_wallet_info;
uint64_t m_internal_output_index = 0;
uint64_t m_spent_height = 0;
uint32_t m_flags = 0;
uint64_t m_amount = 0;
boost::shared_ptr<crypto::scalar_t> m_opt_blinding_mask;
boost::shared_ptr<crypto::hash> m_asset_id;
// @#@ will throw if type is not tx_out_bare, TODO: change according to new model,
// need to replace all get_tx_out_bare_from_out_v() to proper code
uint64_t amount() const { return m_amount; }
const currency::tx_out_bare& output() const { return currency::get_tx_out_bare_from_out_v(m_ptx_wallet_info->m_tx.vout[m_internal_output_index]); }
uint8_t mix_attr() const { return output().target.type() == typeid(currency::txout_to_key) ? boost::get<const currency::txout_to_key&>(output().target).mix_attr : UINT8_MAX; }
crypto::hash tx_hash() const { return get_transaction_hash(m_ptx_wallet_info->m_tx); }
bool is_spent() const { return m_flags & WALLET_TRANSFER_DETAIL_FLAG_SPENT; }
bool is_spendable() const { return (m_flags & (WALLET_TRANSFER_DETAIL_FLAG_SPENT | WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION | WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION)) == 0; }
bool is_reserved_for_escrow() const { return ( (m_flags & WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION) != 0 ); }
bool is_zc() const { return m_opt_blinding_mask.get(); }
const crypto::hash& get_asset_id() const { if (m_asset_id.get()) { return *m_asset_id; } else { return currency::null_hash; } }
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CUSTOM(m_ptx_wallet_info, const transaction_wallet_info&, tools::wallet2::transform_ptr_to_value, tools::wallet2::transform_value_to_ptr)
KV_SERIALIZE(m_internal_output_index)
KV_SERIALIZE(m_spent_height)
KV_SERIALIZE(m_flags)
KV_SERIALIZE(m_amount)
KV_SERIALIZE_N(m_opt_blinding_mask, "blinding_mask")
KV_SERIALIZE(m_asset_id)
KV_SERIALIZE_EPHEMERAL_N(uint64_t, tools::wallet2::transfer_details_base_to_amount, "amount")
KV_SERIALIZE_EPHEMERAL_N(std::string, tools::wallet2::transfer_details_base_to_tx_hash, "tx_id")
END_KV_SERIALIZE_MAP()
};
struct transfer_details_extra_option_htlc_info
{
std::string origin; //this field filled only if htlc had been redeemed
crypto::hash redeem_tx_id = currency::null_hash;
};
typedef boost::variant<transfer_details_extra_option_htlc_info, currency::tx_payer> transfer_details_extra_options_v;
struct transfer_details : public transfer_details_base
{
uint64_t m_global_output_index = 0;
crypto::key_image m_key_image; //TODO: key_image stored twice :(
std::vector<transfer_details_extra_options_v> varian_options;
//v2
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_global_output_index)
KV_SERIALIZE_POD_AS_HEX_STRING(m_key_image)
KV_CHAIN_BASE(transfer_details_base)
END_KV_SERIALIZE_MAP()
};
//used in wallet
struct htlc_expiration_trigger
{
bool is_wallet_owns_redeem = false; //specify if this HTLC belong to this wallet by pkey_redeem or by pkey_refund
uint64_t transfer_index = 0;
};
struct payment_details
{
crypto::hash m_tx_hash = currency::null_hash;
uint64_t m_amount = 0;
uint64_t m_block_height = 0;
uint64_t m_unlock_time = 0;
};
struct mining_context : public currency::pos_mining_context
{
std::string status;
bool is_pos_allowed = false;
uint64_t index = 0; // index in m_transfers
uint64_t stake_unlock_time = 0;
uint64_t height = 0;
uint64_t starter_timestamp = 0;
crypto::hash last_block_hash = currency::null_hash;
uint64_t iterations_processed = 0;
uint64_t total_items_checked = 0;
uint64_t total_amount_checked = 0;
};
struct expiration_entry_info
{
std::vector<uint64_t> selected_transfers;
uint64_t change_amount = 0;
uint64_t expiration_time = 0;
crypto::hash related_tx_id = currency::null_hash; // tx id which caused money lock, if any (ex: escrow proposal transport tx)
};
typedef std::unordered_multimap<std::string, payment_details> payment_container;
typedef std::deque<transfer_details> transfer_container;
typedef std::unordered_map<crypto::hash, transfer_details_base> multisig_transfer_container;
typedef std::unordered_map<crypto::hash, tools::wallet_public::escrow_contract_details_basic> escrow_contracts_container;
typedef std::map<uint64_t, std::set<size_t> > free_amounts_cache_type;
typedef std::unordered_map<crypto::hash, free_amounts_cache_type> free_assets_amounts_cache_type;
typedef std::unordered_map<std::pair<uint64_t, uint64_t>, uint64_t> amount_gindex_to_transfer_id_container; // maps [amount; gindex] -> tid
struct keys_file_data_old
{
crypto::chacha8_iv iv;
std::string account_data;
BEGIN_SERIALIZE_OBJECT()
FIELD(iv)
FIELD(account_data)
END_SERIALIZE()
};
struct keys_file_data
{
uint8_t version;
crypto::chacha8_iv iv;
std::string account_data;
static keys_file_data from_old(const keys_file_data_old& v)
{
keys_file_data result = AUTO_VAL_INIT(result);
result.iv = v.iv;
result.account_data = v.account_data;
return result;
}
DEFINE_SERIALIZATION_VERSION(1)
BEGIN_SERIALIZE_OBJECT()
VERSION_ENTRY(version)
FIELD(iv)
FIELD(account_data)
END_SERIALIZE()
};
struct process_transaction_context
{
uint64_t tx_money_spent_in_ins = 0;
// check all outputs for spending (compare key images)
money_transfer2_details mtd;
bool is_pos_coinbase = false;
bool coin_base_tx = false;
//PoW block don't have change, so all outs supposed to be marked as "mined"
bool is_derived_from_coinbase = false;
size_t i = 0;
size_t sub_i = 0;
uint64_t height = 0;
};
struct wallet_own_asset_context
{
currency::asset_descriptor_base asset_descriptor;
crypto::secret_key control_key;
uint64_t height = 0;
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(asset_descriptor)
BOOST_SERIALIZE(control_key)
BOOST_SERIALIZE(height)
END_BOOST_SERIALIZATION()
};
void assign_account(const currency::account_base& acc);
void generate(const std::wstring& path, const std::string& password, bool auditable_wallet);
void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password);
void load(const std::wstring& path, const std::string& password);
void store();
void store(const std::wstring& path);
void store(const std::wstring& path, const std::string& password);
void store_watch_only(const std::wstring& path, const std::string& password) const;
bool store_keys(std::string& buff, const std::string& password, wallet2::keys_file_data& keys_file_data, bool store_as_watch_only = false);
std::wstring get_wallet_path()const { return m_wallet_file; }
std::string get_wallet_password()const { return m_password; }
currency::account_base& get_account() { return m_account; }
const currency::account_base& get_account() const { return m_account; }
void get_recent_transfers_history(std::vector<wallet_public::wallet_transfer_info>& trs, size_t offset, size_t count, uint64_t& total, uint64_t& last_item_index, bool exclude_mining_txs = false, bool start_from_end = true);
bool is_consolidating_transaction(const wallet_public::wallet_transfer_info& wti);
uint64_t get_recent_transfers_total_count();
uint64_t get_transfer_entries_count();
void get_unconfirmed_transfers(std::vector<wallet_public::wallet_transfer_info>& trs, bool exclude_mining_txs = false);
void init(const std::string& daemon_address = "http://localhost:8080");
bool deinit();
void stop() { m_stop.store(true, std::memory_order_relaxed); }
void reset_creation_time(uint64_t timestamp);
//i_wallet2_callback* callback() const { return m_wcallback; }
//void callback(i_wallet2_callback* callback) { m_callback = callback; }
void callback(std::shared_ptr<i_wallet2_callback> callback) { m_wcallback = callback; m_do_rise_transfer = (callback != nullptr); }
i_wallet2_callback* get_callback() { return m_wcallback; }
void set_do_rise_transfer(bool do_rise) { m_do_rise_transfer = do_rise; }
bool has_related_alias_entry_unconfirmed(const currency::transaction& tx);
void scan_tx_pool(bool& has_related_alias_in_unconfirmed);
void refresh();
void refresh(size_t & blocks_fetched);
void refresh(size_t & blocks_fetched, bool& received_money, std::atomic<bool>& stop);
bool refresh(size_t & blocks_fetched, bool& received_money, bool& ok, std::atomic<bool>& stop);
void refresh(std::atomic<bool>& stop);
void resend_unconfirmed();
void push_offer(const bc_services::offer_details_ex& od, currency::transaction& res_tx);
void cancel_offer_by_id(const crypto::hash& tx_id, uint64_t of_ind, uint64_t fee, currency::transaction& tx);
void update_offer_by_id(const crypto::hash& tx_id, uint64_t of_ind, const bc_services::offer_details_ex& od, currency::transaction& res_tx);
void request_alias_registration(currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward, const crypto::secret_key& authority_key = currency::null_skey);
void request_alias_update(currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward);
bool check_available_sources(std::list<uint64_t>& amounts);
void publish_new_asset(const currency::asset_descriptor_base& asset_info, const std::vector<currency::tx_destination_entry>& destinations, currency::transaction& result_tx, crypto::hash& asset_id);
bool set_core_proxy(const std::shared_ptr<i_core_proxy>& proxy);
void set_pos_mint_packing_size(uint64_t new_size);
void set_minimum_height(uint64_t h);
std::shared_ptr<i_core_proxy> get_core_proxy();
uint64_t balance() const;
uint64_t balance(uint64_t& unloked, uint64_t& awaiting_in, uint64_t& awaiting_out, uint64_t& mined) const;
bool balance(std::unordered_map<crypto::hash, wallet_public::asset_balance_entry_base>& balances, uint64_t& mined) const;
bool balance(std::list<wallet_public::asset_balance_entry>& balances, uint64_t& mined) const;
uint64_t balance(uint64_t& unloked) const;
uint64_t unlocked_balance() const;
void transfer(uint64_t amount, const currency::account_public_address& acc, const crypto::hash& asset_id = currency::null_hash);
void transfer(uint64_t amount, size_t fake_outs_count, const currency::account_public_address& acc, uint64_t fee = TX_DEFAULT_FEE, const crypto::hash& asset_id = currency::null_hash);
void transfer(uint64_t amount, const currency::account_public_address& acc, currency::transaction& result_tx, const crypto::hash& asset_id = currency::null_hash);
void transfer(const std::vector<currency::tx_destination_entry>& dsts,
size_t fake_outputs_count,
uint64_t unlock_time,
uint64_t fee,
const std::vector<currency::extra_v>& extra,
const std::vector<currency::attachment_v>& attachments,
detail::split_strategy_id_t destination_split_strategy_id,
const tx_dust_policy& dust_policy);
void transfer(const std::vector<currency::tx_destination_entry>& dsts,
size_t fake_outputs_count,
uint64_t unlock_time,
uint64_t fee,
const std::vector<currency::extra_v>& extra,
const std::vector<currency::attachment_v>& attachments,
detail::split_strategy_id_t destination_split_strategy_id,
const tx_dust_policy& dust_policy,
currency::transaction &tx,
uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED,
bool shuffle = true,
uint8_t flags = 0,
bool send_to_network = true,
std::string* p_unsigned_filename_or_tx_blob_str = nullptr);
void transfer(const std::vector<currency::tx_destination_entry>& dsts,
size_t fake_outputs_count,
uint64_t unlock_time,
uint64_t fee,
const std::vector<currency::extra_v>& extra,
const std::vector<currency::attachment_v>& attachments);
void transfer(const std::vector<currency::tx_destination_entry>& dsts,
size_t fake_outputs_count,
uint64_t unlock_time,
uint64_t fee,
const std::vector<currency::extra_v>& extra,
const std::vector<currency::attachment_v>& attachments,
currency::transaction& tx);
void transfer(construct_tx_param& ctp,
currency::transaction &tx,
bool send_to_network,
std::string* p_unsigned_filename_or_tx_blob_str = nullptr);
void transfer(construct_tx_param& ctp,
currency::finalized_tx& result,
bool send_to_network,
std::string* p_unsigned_filename_or_tx_blob_str = nullptr);
template<typename destination_split_strategy_t>
void transfer_from_contract(
const std::list<currency::account_keys>& owner_keys,
crypto::hash multisig_id,
const std::vector<currency::tx_destination_entry>& dsts,
size_t fake_outputs_count,
uint64_t unlock_time,
uint64_t fee,
const std::vector<currency::extra_v>& extra,
const std::vector<currency::attachment_v>& attachments,
destination_split_strategy_t destination_split_strategy,
const tx_dust_policy& dust_policy,
currency::transaction &tx,
uint8_t tx_outs_attr,
bool shuffle,
uint8_t flags,
bool send_to_network);
void build_escrow_release_templates(crypto::hash multisig_id,
uint64_t fee,
currency::transaction& tx_release_template,
currency::transaction& tx_burn_template,
const bc_services::contract_private_details& ecrow_details);
void build_escrow_cancel_template(crypto::hash multisig_id,
uint64_t expiration_period,
currency::transaction& tx_cancel_template,
const bc_services::contract_private_details& ecrow_details);
void build_escrow_template(const bc_services::contract_private_details& ecrow_detaild,
size_t fake_outputs_count,
uint64_t unlock_time,
uint64_t expiration_time,
uint64_t b_release_fee,
const std::string& payment_id,
currency::transaction& tx,
std::vector<uint64_t>& selected_transfers,
crypto::secret_key& one_time_key);
void send_escrow_proposal(const wallet_public::create_proposal_param& wp,
currency::transaction &proposal_tx,
currency::transaction &escrow_template_tx);
void send_escrow_proposal(const bc_services::contract_private_details& ecrow_detaild,
size_t fake_outputs_count,
uint64_t unlock_time,
uint64_t expiration_period,
uint64_t fee,
uint64_t b_release_fee,
const std::string& payment_id,
currency::transaction &proposal_tx,
currency::transaction &escrow_template_tx);
bool check_connection();
// PoS mining
void do_pos_mining_prepare_entry(mining_context& cxt, size_t transfer_index);
bool do_pos_mining_iteration(mining_context& cxt, size_t transfer_index, uint64_t ts);
template<typename idle_condition_cb_t> //do refresh as external callback
bool scan_pos(mining_context& cxt, std::atomic<bool>& stop, idle_condition_cb_t idle_condition_cb, const currency::core_runtime_config &runtime_config);
bool fill_mining_context(mining_context& ctx);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
std::string get_transfers_str(bool include_spent = true, bool include_unspent = true) const;
// Returns all payments by given id in unspecified order
void get_payments(const std::string& payment_id, std::list<payment_details>& payments, uint64_t min_height = 0) const;
// callback: (const wallet_public::wallet_transfer_info& wti) -> bool, true -- continue, false -- stop
template<typename callback_t>
void enumerate_transfers_history(callback_t cb, bool enumerate_forward) const;
// callback: (const wallet_public::wallet_transfer_info& wti) -> bool, true -- continue, false -- stop
template<typename callback_t>
void enumerate_unconfirmed_transfers(callback_t cb) const;
bool is_watch_only() const { return m_watch_only; }
bool is_auditable() const { return m_account.get_public_address().is_auditable(); }
void sign_transfer(const std::string& tx_sources_blob, std::string& signed_tx_blob, currency::transaction& tx);
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 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);
bool get_transfer_address(const std::string& adr_str, currency::account_public_address& addr, std::string& payment_id);
inline uint64_t get_blockchain_current_size() const {
return m_chain.get_blockchain_current_size();
}
uint64_t get_top_block_height() const { return m_chain.get_top_block_height(); }
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver)
{
if (t_archive::is_saving::value)
{
WLT_LOG_MAGENTA("Serializing file with ver: " << ver, LOG_LEVEL_0);
}
// do not load wallet if data version is greather than the code version
if (ver > WALLET_FILE_SERIALIZATION_VERSION)
{
WLT_LOG_MAGENTA("Wallet file truncated due to WALLET_FILE_SERIALIZATION_VERSION is more then curren build", LOG_LEVEL_0);
return;
}
if (ver < 149)
{
WLT_LOG_MAGENTA("Wallet file truncated due to old version. ver: " << ver << ", WALLET_FILE_SERIALIZATION_VERSION=" << WALLET_FILE_SERIALIZATION_VERSION, LOG_LEVEL_0);
return;
}
if (t_archive::is_saving::value)
{
uint64_t formation_ver = CURRENCY_FORMATION_VERSION;
a & formation_ver;
}
else
{
uint64_t formation_ver = 0;
a & formation_ver;
if (formation_ver != CURRENCY_FORMATION_VERSION)
{
WLT_LOG_MAGENTA("Wallet file truncated due to mismatch CURRENCY_FORMATION_VERSION", LOG_LEVEL_0);
return;
}
}
//convert from old version
if (ver < 150)
{
WLT_LOG_MAGENTA("Converting blockchain into a short form...", LOG_LEVEL_0);
std::vector<crypto::hash> old_blockchain;
a & old_blockchain;
uint64_t count = 0;
for (auto& h : old_blockchain)
{
m_chain.push_new_block_id(h, count);
count++;
}
WLT_LOG_MAGENTA("Converting done", LOG_LEVEL_0);
}
else
{
a & m_chain;
a & m_minimum_height;
}
// v151: m_amount_gindex_to_transfer_id added
if (ver >= 151)
a & m_amount_gindex_to_transfer_id;
a & m_transfers;
a & m_multisig_transfers;
a & m_key_images;
a & m_unconfirmed_txs;
a & m_unconfirmed_multisig_transfers;
a & m_payments;
a & m_transfer_history;
a & m_unconfirmed_in_transfers;
a & m_contracts;
a & m_money_expirations;
a & m_pending_key_images;
a & m_tx_keys;
a & m_last_pow_block_h;
//after processing
if (ver < 152)
{
wipeout_extra_if_needed(m_transfer_history);
}
if (ver < 153)
return;
a & m_htlcs;
a & m_active_htlcs;
a & m_active_htlcs_txid;
if (ver < 154)
return;
a & m_own_asset_descriptors;
a & m_custom_assets;
}
void wipeout_extra_if_needed(std::vector<wallet_public::wallet_transfer_info>& transfer_history);
bool is_transfer_ready_to_go(const transfer_details& td, uint64_t fake_outputs_count);
bool is_transfer_able_to_go(const transfer_details& td, uint64_t fake_outputs_count);
uint64_t select_indices_for_transfer(std::vector<uint64_t>& ind, free_amounts_cache_type& found_free_amounts, uint64_t needed_money, uint64_t fake_outputs_count);
bool select_indices_for_transfer(assets_selection_context& needed_money_map, uint64_t fake_outputs_count, std::vector<uint64_t>& selected_indexes);
//PoS
//synchronous version of function
bool try_mint_pos();
bool try_mint_pos(const currency::account_public_address& miner_address); // block reward will be sent to miner_address, stake will be returned back to the wallet
//for unit tests
friend class ::test_generator;
//next functions in public area only becausce of test_generator
//TODO: Need refactoring - remove it back to private zone
void set_genesis(const crypto::hash& genesis_hash);
bool prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const currency::pos_entry& pe, const crypto::scalar_t& blinding_masks_sum) const;
void process_new_blockchain_entry(const currency::block& b,
const currency::block_direct_data_entry& bche,
const crypto::hash& bl_id,
uint64_t height);
void process_htlc_triggers_on_block_added(uint64_t height);
void unprocess_htlc_triggers_on_block_removed(uint64_t height);
bool get_pos_entries(std::vector<currency::pos_entry>& entries); // TODO: make it const
size_t get_pos_entries_count();
bool build_minted_block(const mining_context& cxt);
bool build_minted_block(const mining_context& cxt, const currency::account_public_address& miner_address);
bool reset_history();
bool is_transfer_unlocked(const transfer_details& td) const;
bool is_transfer_unlocked(const transfer_details& td, bool for_pos_mining, uint64_t& stake_lock_time) const;
void get_mining_history(wallet_public::mining_history& hist, uint64_t timestamp_from = 0);
void set_core_runtime_config(const currency::core_runtime_config& pc);
currency::core_runtime_config& get_core_runtime_config();
bool backup_keys(const std::string& path);
bool reset_password(const std::string& pass);
bool is_password_valid(const std::string& pass);
bool get_actual_offers(std::list<bc_services::offer_details_ex>& offers);
bool process_contract_info(wallet_public::wallet_transfer_info& wti, const std::vector<currency::payload_items_v>& decrypted_attach);
bool handle_proposal(wallet_public::wallet_transfer_info& wti, const bc_services::proposal_body& prop);
void accept_proposal(const crypto::hash& contract_id, uint64_t b_acceptance_fee, currency::transaction* p_acceptance_tx = nullptr);
void finish_contract(const crypto::hash& contract_id, const std::string& release_type, currency::transaction* p_release_tx = nullptr);
void request_cancel_contract(const crypto::hash& contract_id, uint64_t fee, uint64_t expiration_period, currency::transaction* p_cancellation_proposal_tx = nullptr);
void accept_cancel_contract(const crypto::hash& contract_id, currency::transaction* p_cancellation_acceptance_tx = nullptr);
void scan_tx_to_key_inputs(std::vector<uint64_t>& found_transfers, const currency::transaction& tx);
void dump_trunsfers(std::stringstream& ss, bool verbose = true) const;
std::string dump_trunsfers(bool verbose = false) const;
void dump_key_images(std::stringstream& ss);
void get_multisig_transfers(multisig_transfer_container& ms_transfers);
const multisig_transfer_container& get_multisig_transfers() const { return m_multisig_transfers; }
void set_miner_text_info(const std::string& mti) { m_miner_text_info = mti; }
bool get_transfer_info_by_key_image(const crypto::key_image& ki, transfer_details& td, size_t& i);
bool get_transfer_info_by_index(size_t i, transfer_details& td);
size_t scan_for_collisions(std::unordered_map<crypto::key_image, std::list<size_t> >& key_images);
size_t fix_collisions();
size_t scan_for_transaction_entries(const crypto::hash& tx_id, const crypto::key_image& ki, std::list<transfer_details>& details);
bool get_contracts(escrow_contracts_container& contracts);
const std::list<expiration_entry_info>& get_expiration_entries() const { return m_money_expirations; };
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const;
void prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const mode_separate_context& emode_separate = mode_separate_context());
void finalize_transaction(const currency::finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key = true);
void finalize_transaction(const currency::finalize_tx_param& ftp, currency::finalized_tx& result, bool broadcast_tx, bool store_tx_secret_key = true );
std::string get_log_prefix() const { return m_log_prefix; }
static uint64_t get_max_unlock_time_from_receive_indices(const currency::transaction& tx, const money_transfer2_details& td);
bool get_utxo_distribution(std::map<uint64_t, uint64_t>& distribution);
uint64_t get_sync_progress();
uint64_t get_wallet_file_size()const;
void set_use_deffered_global_outputs(bool use);
construct_tx_param get_default_construct_tx_param_inital();
void set_disable_tor_relay(bool disable);
uint64_t get_default_fee() {return TX_DEFAULT_FEE;}
void export_transaction_history(std::ostream& ss, const std::string& format, bool include_pos_transactions = true);
bool add_custom_asset_id(const crypto::hash& asset_id, currency::asset_descriptor_base& asset_descriptor);
bool delete_custom_asset_id(const crypto::hash& asset_id);
bool load_whitelisted_tokens_if_not_loaded() const;
bool load_whitelisted_tokens()const;
/*
create_htlc_proposal: if htlc_hash == null_hash, then this wallet is originator of the atomic process, and
we use deterministic origin, if given some particular htlc_hash, then we use this hash, and this means that
opener-hash will be given by other side
*/
void 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);
void get_list_of_active_htlc(std::list<wallet_public::htlc_entry_info>& htlcs, bool only_redeem_txs);
void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin, currency::transaction& result_tx);
void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin);
bool check_htlc_redeemed(const crypto::hash& htlc_tx_id, std::string& origin, crypto::hash& redeem_tx_id);
// ionic swaps:
bool create_ionic_swap_proposal(const view::ionic_swap_proposal_info& proposal, const currency::account_public_address& destination_addr, transaction& tx_template);
bool build_ionic_swap_template(const view::ionic_swap_proposal_info& proposal_detais, const currency::account_public_address& destination_addr,
currency::transaction& template_tx,
std::vector<uint64_t>& selected_transfers_for_template,
crypto::secret_key& one_time_key);
bool get_ionic_swap_proposal_info(const std::string&raw_tx_template, view::ionic_swap_proposal_info& proposal);
bool get_ionic_swap_proposal_info(const currency::transaction tx, view::ionic_swap_proposal_info& proposal);
bool accept_ionic_swap_proposal(const std::string&raw_tx_template, currency::transaction& result_tx);
bool accept_ionic_swap_proposal(const currency::transaction& tx_template, currency::transaction& result_tx);
private:
// -------- t_transport_state_notifier ------------------------------------------------
virtual void notify_state_change(const std::string& state_code, const std::string& details = std::string());
// ------------------------------------------------------------------------------------
void add_transfers_to_expiration_list(const std::vector<uint64_t>& selected_transfers, uint64_t expiration, uint64_t change_amount, const crypto::hash& related_tx_id);
void remove_transfer_from_expiration_list(uint64_t transfer_index);
void load_keys(const std::string& keys_file_name, const std::string& password, uint64_t file_signature, keys_file_data& kf_data);
void process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector<uint64_t>* pglobal_indexes);
void fetch_tx_global_indixes(const currency::transaction& tx, std::vector<uint64_t>& goutputs_indexes);
void fetch_tx_global_indixes(const std::list<std::reference_wrapper<const currency::transaction>>& txs, std::vector<std::vector<uint64_t>>& goutputs_indexes);
void detach_blockchain(uint64_t including_height);
bool extract_offers_from_transfer_entry(size_t i, std::unordered_map<crypto::hash, bc_services::offer_details_ex>& offers_local);
bool select_my_offers(std::list<bc_services::offer_details_ex>& offers);
bool clear();
bool reset_all();
bool on_idle();
void unserialize_block_complete_entry(const currency::COMMAND_RPC_GET_BLOCKS_FAST::response& serialized,
currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& unserialized);
void pull_blocks(size_t& blocks_added, std::atomic<bool>& stop);
bool prepare_free_transfers_cache(uint64_t fake_outputs_count);
bool select_transfers(assets_selection_context& needed_money_map, size_t fake_outputs_count, uint64_t dust, std::vector<uint64_t>& selected_indicies);
void add_transfers_to_transfers_cache(const std::vector<uint64_t>& indexs);
void add_transfer_to_transfers_cache(uint64_t amount, uint64_t index, const crypto::hash& asset_id = currency::null_hash);
bool prepare_file_names(const std::wstring& file_path);
void process_unconfirmed(const currency::transaction& tx, std::vector<std::string>& recipients, std::vector<std::string>& recipients_aliases);
void add_sent_unconfirmed_tx(const currency::transaction& tx,
const std::vector<std::string>& recipients,
const std::vector<uint64_t>& selected_indicies,
const std::vector<currency::tx_destination_entry>& splitted_dsts);
bool read_money_transfer2_details_from_tx(const currency::transaction& tx,
const std::vector<currency::tx_destination_entry>& splitted_dsts,
wallet_public::wallet_transfer_info_details& wtd);
void update_current_tx_limit();
void prepare_wti(wallet_public::wallet_transfer_info& wti, uint64_t height, uint64_t timestamp, const currency::transaction& tx, uint64_t amount, const money_transfer2_details& td);
void prepare_wti_decrypted_attachments(wallet_public::wallet_transfer_info& wti, const std::vector<currency::payload_items_v>& decrypted_att);
void handle_money_received2(const currency::block& b,
const currency::transaction& tx,
uint64_t amount,
const money_transfer2_details& td);
void handle_money_spent2(const currency::block& b,
const currency::transaction& in_tx,
uint64_t amount,
const money_transfer2_details& td,
const std::vector<std::string>& recipients,
const std::vector<std::string>& recipients_aliases);
void handle_pulled_blocks(size_t& blocks_added, std::atomic<bool>& stop,
currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& blocks);
std::string get_alias_for_address(const std::string& addr);
std::vector<std::string> get_aliases_for_address(const std::string& addr);
bool is_connected_to_net();
bool is_transfer_okay_for_pos(const transfer_details& tr, bool is_zarcanum_hf, uint64_t& stake_unlock_time) const;
bool scan_unconfirmed_outdate_tx();
const currency::transaction& get_transaction_by_id(const crypto::hash& tx_hash);
void rise_on_transfer2(const wallet_public::wallet_transfer_info& wti);
void process_genesis_if_needed(const currency::block& genesis);
bool build_escrow_proposal(bc_services::contract_private_details& ecrow_details, uint64_t fee, uint64_t unlock_time, currency::tx_service_attachment& att, std::vector<uint64_t>& selected_indicies);
bool prepare_tx_sources(assets_selection_context& needed_money_map, size_t fake_outputs_count, uint64_t dust_threshold, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies);
bool prepare_tx_sources(size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, const std::vector<uint64_t>& selected_indicies);
bool prepare_tx_sources(crypto::hash multisig_id, std::vector<currency::tx_source_entry>& sources, uint64_t& found_money);
bool prepare_tx_sources_htlc(crypto::hash htlc_tx_id, const std::string& origin, std::vector<currency::tx_source_entry>& sources, uint64_t& found_money);
bool prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
void prefetch_global_indicies_if_needed(const std::vector<uint64_t>& selected_indicies);
assets_selection_context get_needed_money(uint64_t fee, const std::vector<currency::tx_destination_entry>& dsts);
void prepare_tx_destinations(const assets_selection_context& needed_money_map,
detail::split_strategy_id_t destination_split_strategy_id,
const tx_dust_policy& dust_policy,
const std::vector<currency::tx_destination_entry>& dsts,
std::vector<currency::tx_destination_entry>& final_destinations);
void prepare_tx_destinations(uint64_t needed_money,
uint64_t found_money,
detail::split_strategy_id_t destination_split_strategy_id,
const tx_dust_policy& dust_policy,
const std::vector<currency::tx_destination_entry>& dsts,
std::vector<currency::tx_destination_entry>& final_detinations, const crypto::hash& assed_id);
bool handle_contract(wallet_public::wallet_transfer_info& wti, const bc_services::contract_private_details& cntr, const std::vector<currency::payload_items_v>& decrypted_attach);
bool handle_release_contract(wallet_public::wallet_transfer_info& wti, const std::string& release_instruction);
bool handle_cancel_proposal(wallet_public::wallet_transfer_info& wti, const bc_services::escrow_cancel_templates_body& ectb, const std::vector<currency::payload_items_v>& decrypted_attach);
bool handle_expiration_list(uint64_t tx_expiration_ts_median);
void handle_contract_expirations(uint64_t tx_expiration_ts_median);
uint64_t get_current_tx_version();
void change_contract_state(wallet_public::escrow_contract_details_basic& contract, uint32_t new_state, const crypto::hash& contract_id, const wallet_public::wallet_transfer_info& wti) const;
void change_contract_state(wallet_public::escrow_contract_details_basic& contract, uint32_t new_state, const crypto::hash& contract_id, const std::string& reason = "internal intention") const;
bool is_need_to_split_outputs();
template<typename input_t>
bool process_input_t(const input_t& in_t, wallet2::process_transaction_context& ptc, const currency::transaction& tx);
construct_tx_param get_default_construct_tx_param();
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);
// 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,
std::vector<currency::payload_items_v>& decrypted_items, crypto::hash& ms_id, bc_services::contract_private_details& cpd);
bool validate_escrow_release(const currency::transaction& tx, bool release_type_normal, const bc_services::contract_private_details& cpd,
const currency::txout_multisig& source_ms_out, const crypto::hash& ms_id, size_t source_ms_out_index, const currency::transaction& source_tx, const currency::account_keys& a_keys) const;
bool validate_escrow_contract(const wallet_public::wallet_transfer_info& wti, const bc_services::contract_private_details& cpd, bool is_a,
const std::vector<currency::payload_items_v>& decrypted_items, crypto::hash& ms_id, bc_services::escrow_relese_templates_body& rtb);
bool validate_escrow_cancel_release(const currency::transaction& tx, const wallet_public::wallet_transfer_info& wti, const bc_services::escrow_cancel_templates_body& ectb,
const std::vector<currency::payload_items_v>& decrypted_items, crypto::hash& ms_id, bc_services::contract_private_details& cpd, const currency::transaction& source_tx,
size_t source_ms_out_index, const currency::account_keys& b_keys, uint64_t minimum_release_fee) const;
bool validate_escrow_cancel_proposal(const wallet_public::wallet_transfer_info& wti, const bc_services::escrow_cancel_templates_body& ectb,
const std::vector<currency::payload_items_v>& decrypted_items, crypto::hash& ms_id, bc_services::contract_private_details& cpd,
const currency::transaction& proposal_template_tx);
void fill_transfer_details(const currency::transaction& tx, const tools::money_transfer2_details& td, tools::wallet_public::wallet_transfer_info_details& res_td) const;
void print_source_entry(std::stringstream& output, const currency::tx_source_entry& src) const;
void init_log_prefix();
void load_keys2ki(bool create_if_not_exist, bool& need_to_resync);
void send_transaction_to_network(const currency::transaction& tx);
void add_sent_tx_detailed_info(const currency::transaction& tx,
const std::vector<currency::tx_destination_entry>& destinations,
const std::vector<uint64_t>& selected_indicies);
void mark_transfers_as_spent(const std::vector<uint64_t>& selected_transfers, const std::string& reason = std::string());
void mark_transfers_with_flag(const std::vector<uint64_t>& selected_transfers, uint32_t flag, const std::string& reason = std::string(), bool throw_if_flag_already_set = false);
void clear_transfers_from_flag(const std::vector<uint64_t>& selected_transfers, uint32_t flag, const std::string& reason = std::string()) noexcept;
void exception_handler();
void exception_handler() const;
uint64_t get_minimum_allowed_fee_for_contract(const crypto::hash& ms_id);
bool generate_packing_transaction_if_needed(currency::transaction& tx, uint64_t fake_outputs_number);
bool store_unsigned_tx_to_file_and_reserve_transfers(const currency::finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str = nullptr);
void check_and_throw_if_self_directed_tx_with_payment_id_requested(const construct_tx_param& ctp);
void push_new_block_id(const crypto::hash& id, uint64_t height);
bool lookup_item_around(uint64_t i, std::pair<uint64_t, crypto::hash>& result);
//void get_short_chain_history(std::list<crypto::hash>& ids);
//void check_if_block_matched(uint64_t i, const crypto::hash& id, bool& block_found, bool& block_matched, bool& full_reset_needed);
uint64_t detach_from_block_ids(uint64_t height);
uint64_t get_wallet_minimum_height();
uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(uint64_t amount, const std::vector<currency::txout_ref_v> & key_offsets);
uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_to_key& intk);
uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_zc_input& inzc);
bool is_in_hardfork_zone(uint64_t hardfork_index) const;
uint8_t out_get_mixin_attr(const currency::tx_out_v& out_t);
const crypto::public_key& out_get_pub_key(const currency::tx_out_v& out_t, std::list<currency::htlc_info>& htlc_info_list);
void push_alias_info_to_extra_according_to_hf_status(const currency::extra_alias_entry& ai, std::vector<currency::extra_v>& extra);
void remove_transfer_from_amount_gindex_map(uint64_t tid);
uint64_t get_alias_cost(const std::string& alias);
detail::split_strategy_id_t get_current_split_strategy();
static void wti_to_csv_entry(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index);
static void wti_to_txt_line(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index);
static void wti_to_json_line(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index);
currency::account_base m_account;
bool m_watch_only;
std::string m_log_prefix; // part of pub address, prefix for logging functions
std::wstring m_wallet_file;
std::wstring m_pending_ki_file;
std::string m_password;
uint64_t m_minimum_height;
std::atomic<uint64_t> m_last_bc_timestamp;
bool m_do_rise_transfer;
uint64_t m_pos_mint_packing_size;
transfer_container m_transfers;
multisig_transfer_container m_multisig_transfers;
amount_gindex_to_transfer_id_container m_amount_gindex_to_transfer_id;
payment_container m_payments;
std::unordered_map<crypto::key_image, size_t> m_key_images;
std::unordered_map<crypto::public_key, crypto::key_image> m_pending_key_images; // (out_pk -> ki) pairs of change outputs to be added in watch-only wallet without spend sec key
pending_ki_file_container_t m_pending_key_images_file_container;
uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
std::atomic<bool> m_stop;
std::vector<wallet_public::wallet_transfer_info> m_transfer_history;
std::unordered_map<crypto::hash, currency::transaction> m_unconfirmed_in_transfers;
std::unordered_map<crypto::hash, tools::wallet_public::wallet_transfer_info> m_unconfirmed_txs;
std::unordered_set<crypto::hash> m_unconfirmed_multisig_transfers;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
std::unordered_map<crypto::hash, wallet_own_asset_context> m_own_asset_descriptors;
std::unordered_map<crypto::hash, currency::asset_descriptor_base> m_custom_assets; //assets that manually added by user
mutable std::unordered_map<crypto::hash, currency::asset_descriptor_base> m_whitelisted_assets; //assets that whitelisted
std::multimap<uint64_t, htlc_expiration_trigger> m_htlcs; //map [expired_if_more_then] -> height of expiration
amount_gindex_to_transfer_id_container m_active_htlcs; // map [amount; gindex] -> transfer index
std::unordered_map<crypto::hash, uint64_t> m_active_htlcs_txid; // map [txid] -> transfer index, limitation: 1 transactiom -> 1 htlc
std::shared_ptr<i_core_proxy> m_core_proxy;
std::shared_ptr<i_wallet2_callback> m_wcallback;
uint64_t m_height_of_start_sync;
std::atomic<uint64_t> m_last_sync_percent;
uint64_t m_last_pow_block_h;
currency::core_runtime_config m_core_runtime_config;
escrow_contracts_container m_contracts;
wallet_chain_shortener m_chain;
std::list<expiration_entry_info> m_money_expirations;
//optimization for big wallets and batch tx
free_assets_amounts_cache_type m_found_free_amounts;
uint64_t m_fake_outputs_count;
std::string m_miner_text_info;
mutable uint64_t m_current_wallet_file_size;
bool m_use_deffered_global_outputs;
bool m_disable_tor_relay;
//this needed to access wallets state in coretests, for creating abnormal blocks and tranmsactions
friend class test_generator;
}; // class wallet2
} // namespace tools
BOOST_CLASS_VERSION(tools::wallet2, WALLET_FILE_SERIALIZATION_VERSION)
BOOST_CLASS_VERSION(tools::wallet_public::wallet_transfer_info, 11)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 3)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details_base, 2)
namespace boost
{
namespace serialization
{
template <class Archive>
inline void serialize(Archive &a, tools::wallet2::transaction_wallet_info &x, const boost::serialization::version_type ver)
{
a & x.m_block_height;
a & x.m_block_timestamp;
a & x.m_tx;
}
template <class Archive>
inline void serialize(Archive &a, tools::wallet2::transfer_details_base &x, const boost::serialization::version_type ver)
{
a & x.m_ptx_wallet_info;
a & x.m_internal_output_index;
a & x.m_flags;
a & x.m_spent_height;
if (ver < 2)
{
x.m_amount = currency::get_tx_out_bare_from_out_v(x.m_ptx_wallet_info->m_tx.vout[x.m_internal_output_index]).amount;
return;
}
a & x.m_amount;
a & x.m_opt_blinding_mask;
a & x.m_asset_id;
}
template <class Archive>
inline void serialize(Archive &a, tools::wallet2::transfer_details_extra_option_htlc_info &x, const boost::serialization::version_type ver)
{
a & x.origin;
}
template <class Archive>
inline void serialize(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
{
a & x.m_global_output_index;
a & x.m_key_image;
a & static_cast<tools::wallet2::transfer_details_base&>(x);
if (ver < 3)
return;
a & x.varian_options;
}
template <class Archive>
inline void serialize(Archive &a, tools::wallet2::htlc_expiration_trigger &x, const boost::serialization::version_type ver)
{
a & x.is_wallet_owns_redeem;
a & x.transfer_index;
}
template <class Archive>
inline void serialize(Archive& a, tools::wallet2::payment_details& x, const boost::serialization::version_type ver)
{
a & x.m_tx_hash;
a & x.m_amount;
a & x.m_block_height;
a & x.m_unlock_time;
}
template <class Archive>
inline void serialize(Archive& a, tools::wallet_public::wallet_transfer_info_details& x, const boost::serialization::version_type ver)
{
a & x.rcv;
a & x.spn;
}
template <class Archive>
inline void serialize(Archive& a, tools::wallet_public::wallet_transfer_info& x, const boost::serialization::version_type ver)
{
a & x.amount;
a & x.timestamp;
a & x.tx_hash;
a & x.height;
a & x.tx_blob_size;
a & x.payment_id;
a & x.remote_addresses;
a & x.is_income;
a & x.td;
a & x.tx;
a & x.remote_aliases;
a & x.comment;
a & x.contract;
a & x.selected_indicies;
a & x.marketplace_entries;
a & x.unlock_time;
if (ver < 10)
return;
a & x.service_entries;
if (ver < 11)
return;
a & x.asset_id;
}
template <class Archive>
inline void serialize(Archive& a, tools::wallet_public::escrow_contract_details_basic& x, const boost::serialization::version_type ver)
{
a & x.state;
a & x.is_a;
a & x.private_detailes;
a & x.expiration_time;
a & x.cancel_expiration_time;
a & x.timestamp;
a & x.height;
a & x.payment_id;
//is not kv_serialization map
a & x.proposal;
a & x.release_body;
a & x.cancel_body;
}
template <class Archive>
inline void serialize(Archive& a, tools::wallet_public::escrow_contract_details& x, const boost::serialization::version_type ver)
{
a & static_cast<tools::wallet_public::escrow_contract_details_basic&>(x);
a & x.contract_id;
}
template <class Archive>
inline void serialize(Archive& a, tools::wallet2::expiration_entry_info& x, const boost::serialization::version_type ver)
{
a & x.expiration_time;
a & x.change_amount;
a & x.selected_transfers;
a & x.related_tx_id;
}
}
}
namespace tools
{
template<typename input_t>
bool wallet2::process_input_t(const input_t& in_t, wallet2::process_transaction_context& ptc, const currency::transaction& tx)
{
// check if this input spends our output
uint64_t tr_index = UINT64_MAX;
if (this->is_auditable() && this->is_watch_only())
{
// tracking wallet, assuming all outputs are spent directly because of mix_attr = 1
tr_index = this->get_directly_spent_transfer_index_by_input_in_tracking_wallet(in_t);
}
else
{
// wallet with spend secret key -- we can calculate own key images and then search by them
auto it = m_key_images.find(in_t.k_image);
if (it != m_key_images.end())
{
tr_index = it->second;
}
}
if (tr_index != UINT64_MAX)
{
transfer_details& td = m_transfers[tr_index];
ptc.tx_money_spent_in_ins += td.amount();
uint32_t flags_before = td.m_flags;
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT;
td.m_spent_height = ptc.height;
if (ptc.coin_base_tx && td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER)
ptc.is_derived_from_coinbase = true;
else
ptc.is_derived_from_coinbase = false;
WLT_LOG_L0("Spent key out, transfer #" << tr_index << ", amount: " << currency::print_money_brief(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << ptc.height <<
"; flags: " << flags_before << " -> " << td.m_flags);
ptc.mtd.spent_indices.push_back(ptc.i);
remove_transfer_from_expiration_list(tr_index);
}
return true;
}
template<typename idle_condition_cb_t> //do refresh as external callback
bool wallet2::scan_pos(mining_context& cxt,
std::atomic<bool>& stop,
idle_condition_cb_t idle_condition_cb,
const currency::core_runtime_config &runtime_config)
{
cxt.status = API_RETURN_CODE_NOT_FOUND;
uint64_t timstamp_last_idle_call = runtime_config.get_core_time();
cxt.iterations_processed = 0;
uint64_t ts_from = cxt.starter_timestamp; // median ts of last BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW blocks
ts_from = ts_from - (ts_from % POS_SCAN_STEP) + POS_SCAN_STEP;
uint64_t ts_to = runtime_config.get_core_time() + CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT - 5;
ts_to = ts_to - (ts_to % POS_SCAN_STEP);
CHECK_AND_ASSERT_MES(ts_to > ts_from, false, "scan_pos: ts_to <= ts_from: " << ts_to << ", " << ts_from);
uint64_t ts_middle = (ts_to + ts_from) / 2;
ts_middle -= ts_middle % POS_SCAN_STEP;
uint64_t ts_window = std::min(ts_middle - ts_from, ts_to - ts_middle);
size_t pos_entry_index = 0;
for (size_t transfer_index = 0; transfer_index != m_transfers.size(); transfer_index++)
{
auto& tr = m_transfers[transfer_index];
uint64_t stake_unlock_time = 0;
if (!is_transfer_okay_for_pos(tr, cxt.zarcanum, stake_unlock_time))
continue;
bool go_past = true;
uint64_t step = 0;
auto next_turn = [&](){
if (!step)
{
step += POS_SCAN_STEP;
}
else if (go_past)
{
go_past = false;
}
else
{
go_past = true;
step += POS_SCAN_STEP;
}
};
do_pos_mining_prepare_entry(cxt, transfer_index);
cxt.total_items_checked++;
cxt.total_amount_checked += tr.amount();
while(step <= ts_window)
{
//check every WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL seconds wheither top block changed, if so - break the loop
if (runtime_config.get_core_time() - timstamp_last_idle_call > WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL)
{
if (!idle_condition_cb())
{
LOG_PRINT_L0("Detected new block, minting interrupted");
cxt.status = API_RETURN_CODE_NOT_FOUND;
return false;
}
timstamp_last_idle_call = runtime_config.get_core_time();
}
uint64_t ts = go_past ? ts_middle - step : ts_middle + step;
if (ts < ts_from || ts > ts_to)
{
next_turn();
continue;
}
PROFILE_FUNC("general_mining_iteration");
if (stop)
return false;
cxt.iterations_processed++;
if (do_pos_mining_iteration(cxt, transfer_index, ts))
{
cxt.index = transfer_index;
cxt.stake_unlock_time = stake_unlock_time;
cxt.status = API_RETURN_CODE_OK;
return true;
}
next_turn();
}
++pos_entry_index;
}
cxt.status = API_RETURN_CODE_NOT_FOUND;
return false;
}
template<typename callback_t>
void wallet2::enumerate_transfers_history(callback_t cb, bool enumerate_forward) const
{
if (enumerate_forward)
{
for(auto it = m_transfer_history.begin(); it != m_transfer_history.end(); ++it)
if (!cb(*it))
break;
}
else
{
for(auto it = m_transfer_history.rbegin(); it != m_transfer_history.rend(); ++it)
if (!cb(*it))
break;
}
}
template<typename callback_t>
void wallet2::enumerate_unconfirmed_transfers(callback_t cb) const
{
for (auto& el : m_unconfirmed_txs)
if (!cb(el.second))
break;
}
} // namespace tools
#if !defined(KEEP_WALLET_LOG_MACROS)
#undef WLT_LOG
#undef WLT_LOG_L0
#undef WLT_LOG_L1
#undef WLT_LOG_L2
#undef WLT_LOG_L3
#undef WLT_LOG_L4
#undef WLT_LOG_ERROR
#undef WLT_LOG_BLUE
#undef WLT_LOG_CYAN
#undef WLT_LOG_GREEN
#undef WLT_LOG_MAGENTA
#undef WLT_LOG_RED
#undef WLT_LOG_YELLOW
#undef WLT_CHECK_AND_ASSERT_MES
#undef WLT_CHECK_AND_ASSERT_MES_NO_RET
#undef WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX
#undef WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX
#undef WLT_THROW_IF_FALSE_WALLET_EX_MES
#endif
#undef LOG_DEFAULT_CHANNEL
#define LOG_DEFAULT_CHANNEL "wallet"
ENABLE_CHANNEL_BY_DEFAULT("wallet");