forked from lthn/blockchain
wallet: refactoring for cold-signing process + offline mode + watch-only mode (work in progress)
This commit is contained in:
parent
3543ae0520
commit
d2d1bf5d7a
16 changed files with 1073 additions and 567 deletions
|
|
@ -648,9 +648,6 @@ POP_WARNINGS
|
|||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
inline std::string cut_off_extension(const std::string& str)
|
||||
{
|
||||
std::string res;
|
||||
|
|
@ -661,7 +658,17 @@ POP_WARNINGS
|
|||
res = str.substr(0, pos);
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
inline std::wstring cut_off_extension(const std::wstring& str)
|
||||
{
|
||||
std::wstring res;
|
||||
std::wstring::size_type pos = str.rfind('.');
|
||||
if (std::wstring::npos == pos)
|
||||
return str;
|
||||
|
||||
res = str.substr(0, pos);
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
// replaces all non-ascii characters with mask_character
|
||||
inline std::string mask_non_ascii_chars(const std::string& str, const char mask_character = '?')
|
||||
|
|
|
|||
127
src/common/pod_array_file_container.h
Normal file
127
src/common/pod_array_file_container.h
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// 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 <string>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
template<typename pod_t>
|
||||
class pod_array_file_container
|
||||
{
|
||||
public:
|
||||
pod_array_file_container()
|
||||
{}
|
||||
|
||||
~pod_array_file_container()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool open(const std::wstring& filename, bool create_if_not_exist, bool* p_corrupted = nullptr, std::string* p_reason = nullptr)
|
||||
{
|
||||
if (!create_if_not_exist && !boost::filesystem::exists(filename))
|
||||
{
|
||||
if (p_reason)
|
||||
*p_reason = "file doest not exist";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_stream.open(filename, std::ios::binary | std::ios::app | std::ios::in);
|
||||
if (m_stream.rdstate() != std::ios::goodbit && m_stream.rdstate() != std::ios::eofbit)
|
||||
{
|
||||
if (p_reason)
|
||||
*p_reason = "file could not be opened";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_corrupted)
|
||||
*p_corrupted = false;
|
||||
|
||||
size_t file_size = size_bytes();
|
||||
if (file_size % sizeof(pod_t) != 0)
|
||||
{
|
||||
// currupted
|
||||
if (p_corrupted)
|
||||
*p_corrupted = true;
|
||||
|
||||
size_t corrected_size = file_size - file_size % sizeof(pod_t);
|
||||
|
||||
// truncate to nearest item boundary
|
||||
close();
|
||||
boost::filesystem::resize_file(filename, corrected_size);
|
||||
m_stream.open(filename, std::ios::binary | std::ios::app | std::ios::in);
|
||||
if ((m_stream.rdstate() != std::ios::goodbit && m_stream.rdstate() != std::ios::eofbit) ||
|
||||
size_bytes() != corrected_size)
|
||||
{
|
||||
if (p_reason)
|
||||
*p_reason = "truncation failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_reason)
|
||||
*p_reason = std::string("file was corrupted, truncated: ") + epee::string_tools::num_to_string_fast(file_size) + " -> " + epee::string_tools::num_to_string_fast(corrected_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
m_stream.close();
|
||||
}
|
||||
|
||||
bool push_back(const pod_t& item)
|
||||
{
|
||||
if (!m_stream.is_open() || (m_stream.rdstate() != std::ios::goodbit && m_stream.rdstate() != std::ios::eofbit))
|
||||
return false;
|
||||
|
||||
m_stream.seekp(0, std::ios_base::end);
|
||||
m_stream.write(reinterpret_cast<const char*>(&item), sizeof item);
|
||||
|
||||
if (m_stream.rdstate() != std::ios::goodbit && m_stream.rdstate() != std::ios::eofbit)
|
||||
return false;
|
||||
|
||||
m_stream.flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_item(size_t index, pod_t& result) const
|
||||
{
|
||||
if (!m_stream.is_open() || (m_stream.rdstate() != std::ios::goodbit && m_stream.rdstate() != std::ios::eofbit))
|
||||
return false;
|
||||
|
||||
size_t offset = index * sizeof result;
|
||||
m_stream.seekg(offset);
|
||||
if (m_stream.rdstate() != std::ios::goodbit)
|
||||
return false;
|
||||
|
||||
m_stream.read(reinterpret_cast<char*>(&result), sizeof result);
|
||||
|
||||
return m_stream.gcount() == sizeof result;
|
||||
}
|
||||
|
||||
size_t size_bytes() const
|
||||
{
|
||||
if (!m_stream.is_open() || (m_stream.rdstate() != std::ios::goodbit && m_stream.rdstate() != std::ios::eofbit))
|
||||
return 0;
|
||||
|
||||
m_stream.seekg(0, std::ios_base::end);
|
||||
return m_stream.tellg();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return size_bytes() / sizeof(pod_t);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable boost::filesystem::fstream m_stream;
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
|
@ -78,10 +78,7 @@ namespace crypto {
|
|||
buff = decrypted_buff;
|
||||
return true;
|
||||
}
|
||||
// inline bool chacha_decrypt(std::string& buff, const std::string& pass)
|
||||
// {
|
||||
// return chacha_crypt(buff, pass);
|
||||
// }
|
||||
|
||||
template<typename pod_to_encrypt, typename pod_pass>
|
||||
inline bool chacha_crypt(pod_to_encrypt& crypt, const pod_pass& pass)
|
||||
{
|
||||
|
|
@ -91,6 +88,7 @@ namespace crypto {
|
|||
memcpy(&crypt, buff.data(), sizeof(crypt));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename pod_pass>
|
||||
inline bool chacha_crypt(std::string& buff, const pod_pass& pass)
|
||||
{
|
||||
|
|
@ -101,6 +99,15 @@ namespace crypto {
|
|||
return true;
|
||||
}
|
||||
|
||||
template<typename pod_pass>
|
||||
inline std::string chacha_crypt(const std::string& input, const pod_pass& pass)
|
||||
{
|
||||
std::string result;
|
||||
result.resize(input.size());
|
||||
if (chacha_crypt(input.data(), input.size(), (void*)result.data(), &pass, sizeof pass))
|
||||
return result;
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ namespace crypto {
|
|||
}
|
||||
|
||||
|
||||
POD_MAKE_COMPARABLE(crypto, public_key)
|
||||
POD_MAKE_HASHABLE(crypto, public_key)
|
||||
POD_MAKE_COMPARABLE(crypto, secret_key)
|
||||
POD_MAKE_HASHABLE(crypto, key_image)
|
||||
POD_MAKE_COMPARABLE(crypto, signature)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ namespace currency
|
|||
const static crypto::signature null_sig = AUTO_VAL_INIT(null_sig);
|
||||
const static crypto::key_derivation null_derivation = AUTO_VAL_INIT(null_derivation);
|
||||
|
||||
|
||||
typedef std::string payment_id_t;
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
|
|
@ -563,7 +563,8 @@ namespace currency
|
|||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(keyimage)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace currency
|
||||
|
||||
POD_MAKE_HASHABLE(currency, account_public_address);
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@
|
|||
#define BC_OFFERS_CURRENCY_MARKET_FILENAME "market.bin"
|
||||
|
||||
|
||||
#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+62)
|
||||
#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+63)
|
||||
|
||||
#define CURRENT_MEMPOOL_ARCHIVE_VER (CURRENCY_FORMATION_VERSION+31)
|
||||
|
||||
|
|
|
|||
|
|
@ -2483,7 +2483,7 @@ namespace currency
|
|||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
bool is_payment_id_size_ok(const std::string& payment_id)
|
||||
bool is_payment_id_size_ok(const payment_id_t& payment_id)
|
||||
{
|
||||
return payment_id.size() <= BC_PAYMENT_ID_SERVICE_SIZE_MAX;
|
||||
}
|
||||
|
|
@ -2493,7 +2493,7 @@ namespace currency
|
|||
return tools::base58::encode_addr(CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr));
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
std::string get_account_address_and_payment_id_as_str(const account_public_address& addr, const std::string& payment_id)
|
||||
std::string get_account_address_and_payment_id_as_str(const account_public_address& addr, const payment_id_t& payment_id)
|
||||
{
|
||||
return tools::base58::encode_addr(CURRENCY_PUBLIC_INTEG_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr) + payment_id);
|
||||
}
|
||||
|
|
@ -2504,7 +2504,7 @@ namespace currency
|
|||
return get_account_address_and_payment_id_from_str(addr, integrated_payment_id, str);
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
bool get_account_address_and_payment_id_from_str(account_public_address& addr, std::string& payment_id, const std::string& str)
|
||||
bool get_account_address_and_payment_id_from_str(account_public_address& addr, payment_id_t& payment_id, const std::string& str)
|
||||
{
|
||||
static const size_t addr_blob_size = sizeof(account_public_address);
|
||||
blobdata blob;
|
||||
|
|
@ -2556,6 +2556,11 @@ namespace currency
|
|||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool parse_payment_id_from_hex_str(const std::string& payment_id_str, payment_id_t& payment_id)
|
||||
{
|
||||
return epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -85,6 +85,13 @@ namespace currency
|
|||
tx_destination_entry() : amount(0), minimum_sigs(0), amount_to_provide(0){}
|
||||
tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0){}
|
||||
tx_destination_entry(uint64_t a, const std::list<account_public_address>& addr) : amount(a), addr(addr), minimum_sigs(addr.size()), amount_to_provide(0){}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(amount)
|
||||
FIELD(addr)
|
||||
FIELD(minimum_sigs)
|
||||
FIELD(amount_to_provide)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_extra_info
|
||||
|
|
@ -391,11 +398,12 @@ namespace currency
|
|||
uint64_t get_base_block_reward(bool is_pos, uint64_t already_generated_coins, uint64_t height);
|
||||
uint64_t get_scratchpad_last_update_rebuild_height(uint64_t h);
|
||||
uint64_t get_scratchpad_size_for_height(uint64_t h);
|
||||
bool is_payment_id_size_ok(const std::string& payment_id);
|
||||
bool is_payment_id_size_ok(const payment_id_t& payment_id);
|
||||
std::string get_account_address_as_str(const account_public_address& addr);
|
||||
std::string get_account_address_and_payment_id_as_str(const account_public_address& addr, const std::string& payment_id);
|
||||
std::string get_account_address_and_payment_id_as_str(const account_public_address& addr, const payment_id_t& payment_id);
|
||||
bool get_account_address_from_str(account_public_address& addr, const std::string& str);
|
||||
bool get_account_address_and_payment_id_from_str(account_public_address& addr, std::string& payment_id, const std::string& str);
|
||||
bool get_account_address_and_payment_id_from_str(account_public_address& addr, payment_id_t& payment_id, const std::string& str);
|
||||
bool parse_payment_id_from_hex_str(const std::string& payment_id_str, payment_id_t& payment_id);
|
||||
bool is_coinbase(const transaction& tx);
|
||||
bool is_coinbase(const transaction& tx, bool& pos_coinbase);
|
||||
bool have_attachment_service_in_container(const std::vector<attachment_v>& av, const std::string& service_id, const std::string& instruction);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ namespace
|
|||
const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of default", 0};
|
||||
const command_line::arg_descriptor<uint32_t> arg_log_level = {"set-log", "", 0, true};
|
||||
const command_line::arg_descriptor<bool> arg_do_pos_mining = { "do-pos-mining", "Do PoS mining", false, false };
|
||||
|
||||
const command_line::arg_descriptor<bool> arg_offline_mode = { "offline-mode", "Don't connect to daemon, work offline (for cold-signing process)", false, true };
|
||||
|
||||
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
|
||||
|
||||
|
|
@ -172,10 +172,11 @@ bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<st
|
|||
simple_wallet::simple_wallet()
|
||||
: m_daemon_port(0),
|
||||
m_print_brain_wallet(false),
|
||||
m_do_refresh(false),
|
||||
m_do_refresh_after_load(false),
|
||||
m_do_not_set_date(false),
|
||||
m_do_pos_mining(false),
|
||||
m_refresh_progress_reporter(*this)
|
||||
m_refresh_progress_reporter(*this),
|
||||
m_offline_mode(false)
|
||||
{
|
||||
m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining <threads_count> - Start mining in daemon");
|
||||
m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon");
|
||||
|
|
@ -203,7 +204,8 @@ simple_wallet::simple_wallet()
|
|||
m_cmd_binder.set_handler("scan_transfers_for_id", boost::bind(&simple_wallet::scan_transfers_for_id, this, _1), "Rescan transfers for tx_id");
|
||||
m_cmd_binder.set_handler("scan_transfers_for_ki", boost::bind(&simple_wallet::scan_transfers_for_ki, this, _1), "Rescan transfers for key image");
|
||||
m_cmd_binder.set_handler("integrated_address", boost::bind(&simple_wallet::integrated_address, this, _1), "integrated_address [<payment_id>|<integrated_address] - encodes given payment_id along with wallet's address into an integrated address (random payment_id will be used if none is provided). Decodes given integrated_address into standard address");
|
||||
|
||||
m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), "Get transaction one-time secret key (r) for a given <txid>");
|
||||
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -278,10 +280,10 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
}
|
||||
}
|
||||
|
||||
m_do_refresh = true;
|
||||
m_do_refresh_after_load = true;
|
||||
if (command_line::has_arg(vm, arg_dont_refresh))
|
||||
{
|
||||
m_do_refresh = false;
|
||||
m_do_refresh_after_load = false;
|
||||
}
|
||||
|
||||
if (command_line::has_arg(vm, arg_print_brain_wallet))
|
||||
|
|
@ -411,7 +413,7 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
|
|||
|
||||
m_wallet->init(m_daemon_address);
|
||||
|
||||
if (!m_do_refresh)
|
||||
if (m_do_refresh_after_load && !m_offline_mode)
|
||||
refresh(std::vector<std::string>());
|
||||
|
||||
success_msg_writer() <<
|
||||
|
|
@ -1237,6 +1239,47 @@ bool simple_wallet::integrated_address(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
|
||||
{
|
||||
std::vector<std::string> local_args = args_;
|
||||
|
||||
if (local_args.size() != 1) {
|
||||
fail_msg_writer() << "usage: get_tx_key <txid>";
|
||||
return true;
|
||||
}
|
||||
|
||||
currency::blobdata txid_data;
|
||||
if (!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
|
||||
{
|
||||
fail_msg_writer() << "failed to parse txid";
|
||||
return true;
|
||||
}
|
||||
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
|
||||
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> amount_keys;
|
||||
if (m_wallet->get_tx_key(txid, tx_key))
|
||||
{
|
||||
success_msg_writer() << "tx one-time secret key: " << epee::string_tools::pod_to_hex(tx_key);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fail_msg_writer() << "no tx keys found for this txid";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::set_offline_mode(bool offline_mode)
|
||||
{
|
||||
if (offline_mode && !m_offline_mode)
|
||||
{
|
||||
message_writer(epee::log_space::console_color_yellow, true, std::string(), LOG_LEVEL_0)
|
||||
<< "WARNING: the wallet is running in OFFLINE MODE!";
|
||||
}
|
||||
m_offline_mode = offline_mode;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
|
@ -1271,13 +1314,15 @@ int main(int argc, char* argv[])
|
|||
command_line::add_arg(desc_params, arg_dont_set_date);
|
||||
command_line::add_arg(desc_params, arg_print_brain_wallet);
|
||||
command_line::add_arg(desc_params, arg_do_pos_mining);
|
||||
|
||||
command_line::add_arg(desc_params, arg_offline_mode);
|
||||
|
||||
|
||||
tools::wallet_rpc_server::init_options(desc_params);
|
||||
|
||||
po::positional_options_description positional_options;
|
||||
positional_options.add(arg_command.name, -1);
|
||||
|
||||
shared_ptr<currency::simple_wallet> sw(new currency::simple_wallet);
|
||||
po::options_description desc_all;
|
||||
desc_all.add(desc_general).add(desc_params);
|
||||
po::variables_map vm;
|
||||
|
|
@ -1287,9 +1332,8 @@ int main(int argc, char* argv[])
|
|||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
simple_wallet w;
|
||||
success_msg_writer() << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]";
|
||||
success_msg_writer() << desc_all << '\n' << w.get_commands_str();
|
||||
success_msg_writer() << desc_all << '\n' << sw->get_commands_str();
|
||||
return false;
|
||||
}
|
||||
else if (command_line::get_arg(vm, command_line::arg_version))
|
||||
|
|
@ -1320,10 +1364,13 @@ int main(int argc, char* argv[])
|
|||
log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level));
|
||||
}
|
||||
|
||||
bool offline_mode = command_line::get_arg(vm, arg_offline_mode);
|
||||
|
||||
if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port))
|
||||
{
|
||||
// runs wallet as RPC server
|
||||
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2);
|
||||
sw->set_offline_mode(offline_mode);
|
||||
LOG_PRINT_L0("Starting wallet RPC server...");
|
||||
|
||||
if (!command_line::has_arg(vm, arg_wallet_file) || command_line::get_arg(vm, arg_wallet_file).empty())
|
||||
|
|
@ -1332,7 +1379,7 @@ int main(int argc, char* argv[])
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!command_line::has_arg(vm, arg_daemon_address) )
|
||||
if (!command_line::has_arg(vm, arg_daemon_address) && !command_line::has_arg(vm, arg_offline_mode))
|
||||
{
|
||||
LOG_ERROR("Daemon address is not set.");
|
||||
return 1;
|
||||
|
|
@ -1385,7 +1432,8 @@ int main(int argc, char* argv[])
|
|||
wal.init(daemon_address);
|
||||
if (command_line::get_arg(vm, arg_generate_new_wallet).size())
|
||||
return 1;
|
||||
wal.refresh();
|
||||
if (!offline_mode)
|
||||
wal.refresh();
|
||||
LOG_PRINT_GREEN("Loaded ok", LOG_LEVEL_0);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
|
@ -1420,8 +1468,8 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}else
|
||||
{
|
||||
shared_ptr<currency::simple_wallet> sw(new currency::simple_wallet);
|
||||
//runs wallet with console interface
|
||||
sw->set_offline_mode(offline_mode);
|
||||
r = sw->init(vm);
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet");
|
||||
if (command_line::get_arg(vm, arg_generate_new_wallet).size())
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ namespace currency
|
|||
bool deinit();
|
||||
bool run();
|
||||
void stop();
|
||||
void set_offline_mode(bool offline_mode);
|
||||
|
||||
//wallet *create_wallet();
|
||||
bool process_command(const std::vector<std::string> &args);
|
||||
|
|
@ -72,6 +73,7 @@ namespace currency
|
|||
bool set_log(const std::vector<std::string> &args);
|
||||
bool enable_concole_logger(const std::vector<std::string> &args);
|
||||
bool integrated_address(const std::vector<std::string> &args);
|
||||
bool get_tx_key(const std::vector<std::string> &args_);
|
||||
|
||||
bool get_alias_from_daemon(const std::string& alias_name, currency::extra_alias_entry_base& ai);
|
||||
bool get_transfer_address(const std::string& adr_str, currency::account_public_address& addr);
|
||||
|
|
@ -145,10 +147,11 @@ namespace currency
|
|||
std::string m_daemon_address;
|
||||
std::string m_daemon_host;
|
||||
int m_daemon_port;
|
||||
bool m_do_refresh;
|
||||
bool m_do_refresh_after_load;
|
||||
bool m_do_not_set_date;
|
||||
bool m_print_brain_wallet;
|
||||
bool m_do_pos_mining;
|
||||
bool m_offline_mode;
|
||||
|
||||
epee::console_handlers_binder m_cmd_binder;
|
||||
|
||||
|
|
|
|||
|
|
@ -436,23 +436,23 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept
|
|||
|
||||
construct_tx_param construct_param = AUTO_VAL_INIT(construct_param);
|
||||
construct_param.fee = b_acceptance_fee;
|
||||
constructed_tx_data construct_res = AUTO_VAL_INIT(construct_res);
|
||||
construct_res.tx = contr_it->second.proposal.tx_template;
|
||||
construct_res.one_time_key.sec = contr_it->second.proposal.tx_onetime_secret_key;
|
||||
currency::transaction tx = contr_it->second.proposal.tx_template;
|
||||
crypto::secret_key one_time_key = contr_it->second.proposal.tx_onetime_secret_key;
|
||||
construct_param.crypt_address = m_account.get_public_address();
|
||||
construct_param.flags = TX_FLAG_SIGNATURE_MODE_SEPARATE;
|
||||
construct_param.mark_tx_as_complete = true;
|
||||
construct_param.split_strategy_id = detail::ssi_digit;
|
||||
|
||||
//little hack for now, we add multisig_entry before transaction actually get to blockchain
|
||||
//to let prepare_transaction (which is called from build_escrow_release_templates) work correct
|
||||
//this code definitely need to be rewritten later (very bad design)
|
||||
size_t n = get_multisig_out_index(construct_res.tx.vout);
|
||||
THROW_IF_FALSE_WALLET_EX(n != construct_res.tx.vout.size(), error::wallet_internal_error, "Multisig out not found in tx template in proposal");
|
||||
size_t n = get_multisig_out_index(tx.vout);
|
||||
THROW_IF_FALSE_WALLET_EX(n != tx.vout.size(), error::wallet_internal_error, "Multisig out not found in tx template in proposal");
|
||||
|
||||
transfer_details_base& tdb = m_multisig_transfers[contract_id];
|
||||
//create once instance of tx for all entries
|
||||
std::shared_ptr<transaction_wallet_info> pwallet_info(new transaction_wallet_info());
|
||||
pwallet_info->m_tx = construct_res.tx;;
|
||||
pwallet_info->m_tx = tx;;
|
||||
pwallet_info->m_block_height = 0;
|
||||
pwallet_info->m_block_timestamp = 0;
|
||||
tdb.m_ptx_wallet_info = pwallet_info;
|
||||
|
|
@ -460,10 +460,10 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept
|
|||
tdb.m_flags &= ~(WALLET_TRANSFER_DETAIL_FLAG_SPENT);
|
||||
//---------------------------------
|
||||
//figure out fee that was left for release contract
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(construct_res.tx.vout[n].amount > (contr_it->second.private_detailes.amount_to_pay +
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(tx.vout[n].amount > (contr_it->second.private_detailes.amount_to_pay +
|
||||
contr_it->second.private_detailes.amount_b_pledge +
|
||||
contr_it->second.private_detailes.amount_a_pledge), "THere is no left money for fee, contract_id: " << contract_id);
|
||||
uint64_t left_for_fee_in_multisig = construct_res.tx.vout[n].amount - (contr_it->second.private_detailes.amount_to_pay +
|
||||
uint64_t left_for_fee_in_multisig = tx.vout[n].amount - (contr_it->second.private_detailes.amount_to_pay +
|
||||
contr_it->second.private_detailes.amount_b_pledge +
|
||||
contr_it->second.private_detailes.amount_a_pledge);
|
||||
|
||||
|
|
@ -490,17 +490,14 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept
|
|||
construct_param.extra.push_back(tsa);
|
||||
|
||||
//build transaction
|
||||
prepare_transaction(construct_param, construct_res, tools::detail::digit_split_strategy);
|
||||
|
||||
send_transaction_to_network(construct_res.tx);
|
||||
|
||||
mark_transfers_as_spent(construct_res.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been accepted with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(construct_res.tx)) + ">");
|
||||
add_sent_tx_detailed_info(construct_res.tx, construct_res.prepared_destinations, construct_res.selected_transfers);
|
||||
|
||||
print_tx_sent_message(construct_res.tx, "(contract)", construct_param.fee);
|
||||
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
|
||||
prepare_transaction(construct_param, ftp, tx);
|
||||
finalize_transaction(ftp, tx, one_time_key, true);
|
||||
mark_transfers_as_spent(ftp.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been accepted with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(tx)) + ">");
|
||||
print_tx_sent_message(tx, "(contract <" + epee::string_tools::pod_to_hex(contract_id) + ">)", construct_param.fee);
|
||||
|
||||
if (p_acceptance_tx != nullptr)
|
||||
*p_acceptance_tx = construct_res.tx;
|
||||
*p_acceptance_tx = tx;
|
||||
}
|
||||
//---------------------------
|
||||
void wallet2::finish_contract(const crypto::hash& contract_id, const std::string& release_type, currency::transaction* p_release_tx /* = nullptr */)
|
||||
|
|
@ -594,7 +591,6 @@ void wallet2::request_cancel_contract(const crypto::hash& contract_id, uint64_t
|
|||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
construct_tx_param construct_param = AUTO_VAL_INIT(construct_param);
|
||||
constructed_tx_data constructed_data = AUTO_VAL_INIT(constructed_data);
|
||||
construct_param.fee = fee;
|
||||
|
||||
//-------
|
||||
|
|
@ -612,18 +608,18 @@ void wallet2::request_cancel_contract(const crypto::hash& contract_id, uint64_t
|
|||
tsa.flags |= TX_SERVICE_ATTACHMENT_ENCRYPT_BODY;
|
||||
construct_param.extra.push_back(tsa);
|
||||
construct_param.crypt_address = contr_it->second.private_detailes.b_addr;
|
||||
construct_param.split_strategy_id = detail::ssi_digit;
|
||||
|
||||
prepare_transaction(construct_param, constructed_data, tools::detail::digit_split_strategy);
|
||||
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
|
||||
prepare_transaction(construct_param, ftp);
|
||||
currency::transaction tx = AUTO_VAL_INIT(tx);
|
||||
finalize_transaction(ftp, tx, crypto::secret_key(), true);
|
||||
mark_transfers_as_spent(ftp.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been requested for cancellaton with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(tx)) + ">");
|
||||
|
||||
send_transaction_to_network(constructed_data.tx);
|
||||
|
||||
mark_transfers_as_spent(constructed_data.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been requested for cancellaton with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(constructed_data.tx)) + ">");
|
||||
add_sent_tx_detailed_info(constructed_data.tx, constructed_data.prepared_destinations, constructed_data.selected_transfers);
|
||||
|
||||
print_tx_sent_message(constructed_data.tx, "(transport for cancel proposal)", fee);
|
||||
print_tx_sent_message(tx, "(transport for cancel proposal)", fee);
|
||||
|
||||
if (p_cancellation_proposal_tx != nullptr)
|
||||
*p_cancellation_proposal_tx = constructed_data.tx;
|
||||
*p_cancellation_proposal_tx = tx;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
void wallet2::scan_tx_to_key_inputs(std::vector<uint64_t>& found_transfers, const currency::transaction& tx)
|
||||
|
|
@ -1159,7 +1155,7 @@ void wallet2::transfer(uint64_t amount, const currency::account_public_address&
|
|||
dst.back().addr.push_back(acc);
|
||||
dst.back().amount = amount;
|
||||
transaction result_tx = AUTO_VAL_INIT(result_tx);
|
||||
this->transfer(dst, 0, 0, TX_DEFAULT_FEE, extra, attachments, tools::detail::digit_split_strategy, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), result_tx);
|
||||
this->transfer(dst, 0, 0, TX_DEFAULT_FEE, extra, attachments, tools::detail::ssi_digit, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), result_tx);
|
||||
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
@ -1693,6 +1689,7 @@ bool wallet2::reset_all()
|
|||
m_unconfirmed_in_transfers.clear();
|
||||
m_unconfirmed_txs.clear();
|
||||
m_unconfirmed_multisig_transfers.clear();
|
||||
// m_tx_keys is not cleared intentionally, considered to be safe
|
||||
m_multisig_transfers.clear();
|
||||
m_payments.clear();
|
||||
m_transfer_history.clear();
|
||||
|
|
@ -1762,6 +1759,58 @@ void wallet2::init_log_prefix()
|
|||
m_log_prefix = m_account.get_public_address_str().substr(0, 6);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::load_keys2ki(bool create_if_not_exist, bool& need_to_resync)
|
||||
{
|
||||
m_pending_key_images_file_container.close(); // just in case it was opened
|
||||
bool pki_corrupted = false;
|
||||
std::string reason;
|
||||
bool ok = m_pending_key_images_file_container.open(m_pending_ki_file, create_if_not_exist, &pki_corrupted, &reason);
|
||||
THROW_IF_FALSE_WALLET_EX(ok, error::file_not_found, m_log_prefix + ": error opening file " + string_encoding::convert_to_ansii(m_pending_ki_file));
|
||||
if (pki_corrupted)
|
||||
{
|
||||
WLT_LOG_ERROR("file " << string_encoding::convert_to_ansii(m_pending_ki_file) << " is corrupted! " << reason);
|
||||
}
|
||||
|
||||
if (m_pending_key_images.size() < m_pending_key_images_file_container.size())
|
||||
{
|
||||
WLT_LOG_RED("m_pending_key_images size: " << m_pending_key_images.size() << " is LESS than m_pending_key_images_file_container size: " << m_pending_key_images_file_container.size(), LOG_LEVEL_0);
|
||||
WLT_LOG_L0("Restoring m_pending_key_images from file container...");
|
||||
m_pending_key_images.clear();
|
||||
for (size_t i = 0, size = m_pending_key_images_file_container.size(); i < size; ++i)
|
||||
{
|
||||
out_key_to_ki item = AUTO_VAL_INIT(item);
|
||||
ok = m_pending_key_images_file_container.get_item(i, item);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(ok, "m_pending_key_images_file_container.get_item() failed for index " << i << ", size: " << m_pending_key_images_file_container.size());
|
||||
ok = m_pending_key_images.insert(std::make_pair(item.out_key, item.key_image)).second;
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(ok, "m_pending_key_images.insert failed for index " << i << ", size: " << m_pending_key_images_file_container.size());
|
||||
WLT_LOG_L2("pending key image restored: (" << item.out_key << ", " << item.key_image << ")");
|
||||
}
|
||||
WLT_LOG_L0(m_pending_key_images.size() << " elements restored, requesting full wallet resync");
|
||||
WLT_LOG_L0("m_pending_key_images size: " << m_pending_key_images.size() << ", m_pending_key_images_file_container size: " << m_pending_key_images_file_container.size());
|
||||
need_to_resync = true;
|
||||
}
|
||||
else if (m_pending_key_images.size() > m_pending_key_images_file_container.size())
|
||||
{
|
||||
WLT_LOG_RED("m_pending_key_images size: " << m_pending_key_images.size() << " is GREATER than m_pending_key_images_file_container size: " << m_pending_key_images_file_container.size(), LOG_LEVEL_0);
|
||||
WLT_LOG_RED("UNRECOVERABLE ERROR, wallet stops", LOG_LEVEL_0);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(false, "m_pending_key_images > m_pending_key_images_file_container");
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::prepare_file_names(const std::wstring& file_path)
|
||||
{
|
||||
m_wallet_file = file_path;
|
||||
|
||||
m_pending_ki_file = string_tools::cut_off_extension(m_wallet_file) + L".outkey2ki";
|
||||
|
||||
// make sure file path is accessible and exists
|
||||
boost::filesystem::path pp = boost::filesystem::path(file_path).parent_path();
|
||||
if (!pp.empty())
|
||||
boost::filesystem::create_directories(pp);
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::load_keys(const std::string& buff, const std::string& password)
|
||||
{
|
||||
wallet2::keys_file_data keys_file_data;
|
||||
|
|
@ -1780,7 +1829,10 @@ void wallet2::load_keys(const std::string& buff, const std::string& password)
|
|||
const currency::account_keys& keys = m_account.get_keys();
|
||||
r = epee::serialization::load_t_from_binary(m_account, account_data);
|
||||
r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
|
||||
r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
|
||||
if (keys.m_spend_secret_key == currency::null_skey)
|
||||
m_watch_only = true;
|
||||
else
|
||||
r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
|
||||
if (!r)
|
||||
{
|
||||
WLT_LOG_L0("Wrong password for wallet " << string_encoding::convert_to_ansii(m_wallet_file));
|
||||
|
|
@ -1799,19 +1851,21 @@ void wallet2::assign_account(const currency::account_base& acc)
|
|||
void wallet2::generate(const std::wstring& path, const std::string& pass)
|
||||
{
|
||||
clear();
|
||||
m_wallet_file = path;
|
||||
prepare_file_names(path);
|
||||
m_password = pass;
|
||||
m_account.generate();
|
||||
init_log_prefix();
|
||||
boost::system::error_code ignored_ec;
|
||||
THROW_IF_TRUE_WALLET_EX(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, epee::string_encoding::convert_to_ansii(m_wallet_file));
|
||||
bool stub;
|
||||
load_keys2ki(true, stub);
|
||||
store();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& restore_key)
|
||||
{
|
||||
clear();
|
||||
m_wallet_file = path;
|
||||
prepare_file_names(path);
|
||||
m_password = pass;
|
||||
bool r = m_account.restore_keys_from_braindata(restore_key);
|
||||
init_log_prefix();
|
||||
|
|
@ -1829,7 +1883,7 @@ bool wallet2::check_connection()
|
|||
void wallet2::load(const std::wstring& wallet_, const std::string& password)
|
||||
{
|
||||
clear();
|
||||
m_wallet_file = wallet_;
|
||||
prepare_file_names(wallet_);
|
||||
m_password = password;
|
||||
|
||||
std::string keys_buff;
|
||||
|
|
@ -1857,16 +1911,21 @@ void wallet2::load(const std::wstring& wallet_, const std::string& password)
|
|||
data_file.read((char*)keys_buff.data(), wbh.m_cb_keys);
|
||||
|
||||
load_keys(keys_buff, password);
|
||||
WLT_LOG_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str());
|
||||
|
||||
bool r = tools::portable_unserialize_obj_from_stream(*this, data_file);
|
||||
bool need_to_resync = !tools::portable_unserialize_obj_from_stream(*this, data_file);
|
||||
|
||||
if (!r)
|
||||
if (m_watch_only)
|
||||
load_keys2ki(true, need_to_resync);
|
||||
|
||||
if (need_to_resync)
|
||||
{
|
||||
reset_history();
|
||||
}
|
||||
m_local_bc_height = m_blockchain.size();
|
||||
THROW_IF_TRUE_WALLET_EX(!r, error::wallet_load_notice_wallet_restored, epee::string_encoding::convert_to_ansii(m_wallet_file));
|
||||
THROW_IF_TRUE_WALLET_EX(need_to_resync, error::wallet_load_notice_wallet_restored, epee::string_encoding::convert_to_ansii(m_wallet_file));
|
||||
|
||||
WLT_LOG_L0("Loaded wallet file" << (m_watch_only ? " (WATCH ONLY) " : " ") << string_encoding::convert_to_ansii(m_wallet_file) << " with public address: " << m_account.get_public_address_str());
|
||||
WLT_LOG_L0("(pending_key_images: " << m_pending_key_images.size() << ", pki file elements: " << m_pending_key_images_file_container.size() << ", tx_keys: " << m_tx_keys.size() << ")");
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::store()
|
||||
|
|
@ -1876,6 +1935,8 @@ void wallet2::store()
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::store(const std::wstring& path_to_save)
|
||||
{
|
||||
LOG_PRINT_L0("(before storing: pending_key_images: " << m_pending_key_images.size() << ", pki file elements: " << m_pending_key_images_file_container.size() << ", tx_keys: " << m_tx_keys.size() << ")");
|
||||
|
||||
//prepare data
|
||||
std::string keys_buff;
|
||||
bool r = store_keys(keys_buff, m_password);
|
||||
|
|
@ -2020,14 +2081,59 @@ void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) con
|
|||
incoming_transfers = m_transfers;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::get_payments(const std::string& payment_id, std::list<wallet2::payment_details>& payments) const
|
||||
void wallet2::get_payments(const std::string& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height) const
|
||||
{
|
||||
auto range = m_payments.equal_range(payment_id);
|
||||
std::for_each(range.first, range.second, [&payments](const payment_container::value_type& x) {
|
||||
payments.push_back(x.second);
|
||||
std::for_each(range.first, range.second, [&payments, &min_height](const payment_container::value_type& x)
|
||||
{
|
||||
if (min_height <= x.second.m_block_height)
|
||||
{
|
||||
payments.push_back(x.second);
|
||||
}
|
||||
});
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
/*void wallet2::sign_transfer(const std::string& tx_sources_blob, std::string& signed_tx_blob, currency::transaction& tx)
|
||||
{
|
||||
// assumed to be called from normal, non-watch-only wallet
|
||||
THROW_IF_FALSE_WALLET_EX(!m_watch_only, error::wallet_common_error, "watch-only wallet is unable to sign transfers, you need to use normal wallet for that");
|
||||
|
||||
// decrypt the blob
|
||||
std::string decrypted_src_blob = crypto::chacha_crypt(tx_sources_blob, m_account.get_keys().m_view_secret_key);
|
||||
|
||||
// deserialize create_tx_arg
|
||||
create_tx_arg create_tx_param = AUTO_VAL_INIT(create_tx_param);
|
||||
bool r = t_unserializable_object_from_blob(create_tx_param, decrypted_src_blob);
|
||||
THROW_IF_FALSE_WALLET_EX(r, error::wallet_common_error, "Failed to decrypt tx sources blob");
|
||||
|
||||
// make sure unsigned tx was created with the same keys
|
||||
THROW_IF_FALSE_WALLET_EX(create_tx_param.spend_pub_key == m_account.get_keys().m_account_address.m_spend_public_key, error::wallet_common_error, "The tx sources was created in a different wallet, keys missmatch");
|
||||
|
||||
// construct the transaction
|
||||
create_tx_res create_tx_result = AUTO_VAL_INIT(create_tx_result);
|
||||
r = currency::construct_tx(m_account.get_keys(), create_tx_param, create_tx_result);
|
||||
THROW_IF_FALSE_WALLET_EX(r, error::wallet_common_error, "construct_tx failed at sign_transfer");
|
||||
tx = create_tx_result.tx;
|
||||
|
||||
// serialize and encrypt the result
|
||||
signed_tx_blob = t_serializable_object_to_blob(create_tx_result);
|
||||
crypto::do_chacha_crypt(signed_tx_blob, m_account.get_keys().m_view_secret_key);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::sign_transfer_files(const std::string& tx_sources_file, const std::string& signed_tx_file, currency::transaction& tx)
|
||||
{
|
||||
std::string sources_blob;
|
||||
bool r = epee::file_io_utils::load_file_to_string(tx_sources_file, sources_blob);
|
||||
THROW_IF_FALSE_WALLET_EX(r, error::wallet_common_error, std::string("failed to open file ") + tx_sources_file);
|
||||
|
||||
std::string signed_tx_blob;
|
||||
sign_transfer(sources_blob, signed_tx_blob, tx);
|
||||
|
||||
r = epee::file_io_utils::save_string_to_file(signed_tx_file, signed_tx_blob);
|
||||
THROW_IF_FALSE_WALLET_EX(r, error::wallet_common_error, std::string("failed to store signed tx to file ") + signed_tx_file);
|
||||
}
|
||||
*/
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::get_recent_transfers_total_count()
|
||||
{
|
||||
return m_transfer_history.size();
|
||||
|
|
@ -2235,7 +2341,7 @@ bool wallet2::reset_history()
|
|||
m_blockchain[0] = genesis_hash;
|
||||
m_account = acc_tmp;
|
||||
m_password = pass;
|
||||
m_wallet_file = file_path;
|
||||
prepare_file_names(file_path);
|
||||
return true;
|
||||
}
|
||||
//-------------------------------
|
||||
|
|
@ -2371,7 +2477,7 @@ void wallet2::push_offer(const bc_services::offer_details_ex& od, currency::tran
|
|||
bc_services::put_offer_into_attachment(static_cast<bc_services::offer_details>(od), attachments);
|
||||
|
||||
destinations.push_back(tx_dest);
|
||||
transfer(destinations, 0, 0, od.fee, extra, attachments, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx);
|
||||
transfer(destinations, 0, 0, od.fee, extra, attachments, detail::ssi_digit, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
const transaction& wallet2::get_transaction_by_id(const crypto::hash& tx_hash)
|
||||
|
|
@ -2408,7 +2514,7 @@ void wallet2::cancel_offer_by_id(const crypto::hash& tx_id, uint64_t of_ind, cur
|
|||
|
||||
destinations.push_back(tx_dest);
|
||||
uint64_t fee = 0; // use zero fee for offer cancellation transaction
|
||||
transfer(destinations, 0, 0, fee, extra, attachments, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx);
|
||||
transfer(destinations, 0, 0, fee, extra, attachments, detail::ssi_digit, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::update_offer_by_id(const crypto::hash& tx_id, uint64_t of_ind, const bc_services::offer_details_ex& od, currency::transaction& res_tx)
|
||||
|
|
@ -2434,7 +2540,7 @@ void wallet2::update_offer_by_id(const crypto::hash& tx_id, uint64_t of_ind, con
|
|||
bc_services::put_offer_into_attachment(uo, attachments);
|
||||
|
||||
destinations.push_back(tx_dest);
|
||||
transfer(destinations, 0, 0, od.fee, extra, attachments, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx);
|
||||
transfer(destinations, 0, 0, od.fee, extra, attachments, detail::ssi_digit, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::request_alias_registration(const currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward)
|
||||
|
|
@ -2455,7 +2561,7 @@ void wallet2::request_alias_registration(const currency::extra_alias_entry& ai,
|
|||
tx_dest_alias_reward.amount = reward;
|
||||
destinations.push_back(tx_dest_alias_reward);
|
||||
|
||||
transfer(destinations, 0, 0, fee, extra, attachments, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx, CURRENCY_TO_KEY_OUT_RELAXED, false);
|
||||
transfer(destinations, 0, 0, fee, extra, attachments, detail::ssi_digit, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx, CURRENCY_TO_KEY_OUT_RELAXED, false);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::request_alias_update(currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward)
|
||||
|
|
@ -2478,7 +2584,7 @@ void wallet2::request_alias_update(currency::extra_alias_entry& ai, currency::tr
|
|||
std::vector<currency::extra_v> extra;
|
||||
std::vector<currency::attachment_v> attachments;
|
||||
extra.push_back(ai);
|
||||
transfer(destinations, 0, 0, fee, extra, attachments, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx, CURRENCY_TO_KEY_OUT_RELAXED, false);
|
||||
transfer(destinations, 0, 0, fee, extra, attachments, detail::ssi_digit, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx, CURRENCY_TO_KEY_OUT_RELAXED, false);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::check_available_sources(std::list<uint64_t>& amounts)
|
||||
|
|
@ -2625,9 +2731,10 @@ void wallet2::build_escrow_release_templates(crypto::hash multisig_id,
|
|||
const bc_services::contract_private_details& ecrow_details)
|
||||
{
|
||||
construct_tx_param construct_params = AUTO_VAL_INIT(construct_params);
|
||||
constructed_tx_data constructed_data = AUTO_VAL_INIT(constructed_data);
|
||||
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
|
||||
construct_params.fee = fee;
|
||||
construct_params.multisig_id = multisig_id;
|
||||
construct_params.split_strategy_id = detail::ssi_digit;
|
||||
construct_params.dsts.resize(2);
|
||||
//0 - addr_a
|
||||
//1 - addr_b
|
||||
|
|
@ -2648,8 +2755,8 @@ void wallet2::build_escrow_release_templates(crypto::hash multisig_id,
|
|||
tsa.service_id = BC_ESCROW_SERVICE_ID;
|
||||
tsa.instruction = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_NORMAL;
|
||||
construct_params.extra.push_back(tsa);
|
||||
prepare_transaction(construct_params, constructed_data, detail::digit_split_strategy);
|
||||
tx_release_template = constructed_data.tx;
|
||||
prepare_transaction(construct_params, ftp);
|
||||
finalize_transaction(ftp, tx_release_template, crypto::secret_key(), false);
|
||||
|
||||
//generate burn escrow
|
||||
construct_params.dsts.resize(1);
|
||||
|
|
@ -2660,8 +2767,8 @@ void wallet2::build_escrow_release_templates(crypto::hash multisig_id,
|
|||
construct_params.extra.clear();
|
||||
tsa.instruction = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_BURN;
|
||||
construct_params.extra.push_back(tsa);
|
||||
prepare_transaction(construct_params, constructed_data, detail::digit_split_strategy);
|
||||
tx_burn_template = constructed_data.tx;
|
||||
prepare_transaction(construct_params, ftp);
|
||||
finalize_transaction(ftp, tx_burn_template, crypto::secret_key(), false);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::build_escrow_cancel_template(crypto::hash multisig_id,
|
||||
|
|
@ -2677,9 +2784,10 @@ void wallet2::build_escrow_cancel_template(crypto::hash multisig_id,
|
|||
"multisig id out amount no more than escrow total amount");
|
||||
|
||||
construct_tx_param construct_params = AUTO_VAL_INIT(construct_params);
|
||||
constructed_tx_data constructed_data = AUTO_VAL_INIT(constructed_data);
|
||||
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
|
||||
construct_params.fee = it->second.amount() - (ecrow_details.amount_a_pledge + ecrow_details.amount_to_pay + ecrow_details.amount_b_pledge);
|
||||
construct_params.multisig_id = multisig_id;
|
||||
construct_params.split_strategy_id = detail::ssi_digit;
|
||||
construct_params.dsts.resize(2);
|
||||
//0 - addr_a
|
||||
//1 - addr_b
|
||||
|
|
@ -2698,8 +2806,8 @@ void wallet2::build_escrow_cancel_template(crypto::hash multisig_id,
|
|||
expir.v = m_core_runtime_config.get_core_time() + expiration_period;
|
||||
construct_params.extra.push_back(expir);
|
||||
|
||||
prepare_transaction(construct_params, constructed_data, detail::digit_split_strategy);
|
||||
tx_cancel_template = constructed_data.tx;
|
||||
prepare_transaction(construct_params, ftp);
|
||||
finalize_transaction(ftp, tx_cancel_template, crypto::secret_key(), false);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
@ -2711,56 +2819,52 @@ void wallet2::build_escrow_template(const bc_services::contract_private_details&
|
|||
const std::string& payment_id,
|
||||
currency::transaction& tx,
|
||||
std::vector<uint64_t>& selected_transfers,
|
||||
currency::keypair& one_time_key)
|
||||
crypto::secret_key& one_time_key)
|
||||
{
|
||||
construct_tx_param ctp = AUTO_VAL_INIT(ctp);
|
||||
ctp.crypt_address = ecrow_details.b_addr;
|
||||
ctp.dust_policy = tx_dust_policy(DEFAULT_DUST_THRESHOLD);
|
||||
ctp.fake_outputs_count = fake_outputs_count;
|
||||
ctp.fee = 0;
|
||||
ctp.flags = TX_FLAG_SIGNATURE_MODE_SEPARATE;
|
||||
ctp.mark_tx_as_complete = false;
|
||||
ctp.multisig_id = currency::null_hash;
|
||||
ctp.shuffle = true;
|
||||
ctp.split_strategy_id = detail::ssi_digit;
|
||||
ctp.tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED;
|
||||
ctp.unlock_time = unlock_time;
|
||||
|
||||
std::vector<currency::tx_destination_entry> destinations;
|
||||
std::vector<currency::extra_v> extra;
|
||||
std::vector<currency::attachment_v> attachments;
|
||||
|
||||
etc_tx_details_expiration_time t = AUTO_VAL_INIT(t);
|
||||
t.v = expiration_time;
|
||||
extra.push_back(t);
|
||||
t.v = expiration_time; // TODO: move it to construct_tx_param
|
||||
ctp.extra.push_back(t);
|
||||
currency::tx_service_attachment att = AUTO_VAL_INIT(att);
|
||||
bc_services::pack_attachment_as_gzipped_json(ecrow_details, att);
|
||||
att.flags |= TX_SERVICE_ATTACHMENT_ENCRYPT_BODY;
|
||||
att.service_id = BC_ESCROW_SERVICE_ID;
|
||||
att.instruction = BC_ESCROW_SERVICE_INSTRUCTION_PRIVATE_DETAILS;
|
||||
extra.push_back(att);
|
||||
ctp.extra.push_back(att);
|
||||
|
||||
destinations.resize(1);
|
||||
destinations.back().amount = ecrow_details.amount_a_pledge + ecrow_details.amount_b_pledge + ecrow_details.amount_to_pay + b_release_fee;
|
||||
destinations.back().amount_to_provide = ecrow_details.amount_a_pledge + ecrow_details.amount_to_pay;
|
||||
destinations.back().addr.push_back(ecrow_details.a_addr);
|
||||
destinations.back().addr.push_back(ecrow_details.b_addr);
|
||||
destinations.back().minimum_sigs = 2;
|
||||
std::vector<currency::tx_destination_entry> prepared_destinations;
|
||||
ctp.dsts.resize(1);
|
||||
ctp.dsts.back().amount = ecrow_details.amount_a_pledge + ecrow_details.amount_b_pledge + ecrow_details.amount_to_pay + b_release_fee;
|
||||
ctp.dsts.back().amount_to_provide = ecrow_details.amount_a_pledge + ecrow_details.amount_to_pay;
|
||||
ctp.dsts.back().addr.push_back(ecrow_details.a_addr);
|
||||
ctp.dsts.back().addr.push_back(ecrow_details.b_addr);
|
||||
ctp.dsts.back().minimum_sigs = 2;
|
||||
if (payment_id.size())
|
||||
{
|
||||
currency::tx_service_attachment att = AUTO_VAL_INIT(att);
|
||||
att.body = payment_id;
|
||||
att.service_id = BC_PAYMENT_ID_SERVICE_ID;
|
||||
att.flags = TX_SERVICE_ATTACHMENT_DEFLATE_BODY;
|
||||
attachments.push_back(att);
|
||||
ctp.attachments.push_back(att);
|
||||
}
|
||||
|
||||
prepare_transaction(destinations,
|
||||
fake_outputs_count,
|
||||
unlock_time,
|
||||
0,
|
||||
extra,
|
||||
attachments,
|
||||
detail::digit_split_strategy,
|
||||
tx_dust_policy(DEFAULT_DUST_THRESHOLD),
|
||||
ecrow_details.b_addr,
|
||||
tx,
|
||||
CURRENCY_TO_KEY_OUT_RELAXED,
|
||||
true,
|
||||
false,
|
||||
TX_FLAG_SIGNATURE_MODE_SEPARATE,
|
||||
selected_transfers,
|
||||
one_time_key,
|
||||
prepared_destinations);
|
||||
|
||||
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
|
||||
prepare_transaction(ctp, ftp, tx);
|
||||
|
||||
selected_transfers = ftp.selected_transfers;
|
||||
|
||||
finalize_transaction(ftp, tx, one_time_key, false);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::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)
|
||||
|
|
@ -2834,7 +2938,7 @@ void wallet2::remove_transfer_from_expiration_list(uint64_t transfer_index)
|
|||
// (don't change m_spent flag, because transfer status is unclear - the caller should take care of it)
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::send_escrow_proposal(const bc_services::contract_private_details& ecrow_detaild,
|
||||
void wallet2::send_escrow_proposal(const bc_services::contract_private_details& ecrow_details,
|
||||
size_t fake_outputs_count,
|
||||
uint64_t unlock_time,
|
||||
uint64_t expiration_period,
|
||||
|
|
@ -2842,8 +2946,7 @@ void wallet2::send_escrow_proposal(const bc_services::contract_private_details&
|
|||
uint64_t b_release_fee,
|
||||
const std::string& payment_id,
|
||||
currency::transaction &tx,
|
||||
currency::transaction &template_tx/*,
|
||||
crypto::hash& contract_id*/)
|
||||
currency::transaction &template_tx)
|
||||
{
|
||||
if (!is_connected_to_net())
|
||||
{
|
||||
|
|
@ -2851,54 +2954,41 @@ void wallet2::send_escrow_proposal(const bc_services::contract_private_details&
|
|||
"Transfer attempt while daemon offline");
|
||||
}
|
||||
|
||||
using namespace currency;
|
||||
std::vector<uint64_t> selected_transfers;
|
||||
std::vector<currency::tx_destination_entry> destinations;
|
||||
std::vector<currency::tx_destination_entry> prepared_destinations;
|
||||
std::vector<uint64_t> selected_transfers_for_template;
|
||||
|
||||
currency::keypair one_time_key = AUTO_VAL_INIT(one_time_key);
|
||||
crypto::secret_key one_time_key = AUTO_VAL_INIT(one_time_key);
|
||||
uint64_t expiration_time = m_core_runtime_config.get_core_time() + expiration_period;
|
||||
|
||||
build_escrow_template(ecrow_detaild, fake_outputs_count, unlock_time, expiration_time, b_release_fee, payment_id, template_tx, selected_transfers_for_template, one_time_key);
|
||||
std::vector<uint64_t> selected_transfers_for_template;
|
||||
build_escrow_template(ecrow_details, fake_outputs_count, unlock_time, expiration_time, b_release_fee, payment_id, template_tx, selected_transfers_for_template, one_time_key);
|
||||
crypto::hash ms_id = get_multisig_out_id(template_tx, 0);
|
||||
|
||||
const uint32_t mask_to_mark_escrow_template_locked_transfers = WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION;
|
||||
mark_transfers_with_flag(selected_transfers_for_template, mask_to_mark_escrow_template_locked_transfers, "preparing escrow template tx");
|
||||
mark_transfers_with_flag(selected_transfers_for_template, mask_to_mark_escrow_template_locked_transfers, "preparing escrow template tx, contract: " + epee::string_tools::pod_to_hex(ms_id));
|
||||
|
||||
currency::tx_service_attachment att = AUTO_VAL_INIT(att);
|
||||
construct_tx_param ctp = AUTO_VAL_INIT(ctp);
|
||||
|
||||
bc_services::proposal_body pb = AUTO_VAL_INIT(pb);
|
||||
pb.tx_onetime_secret_key = one_time_key.sec;
|
||||
pb.tx_onetime_secret_key = one_time_key;
|
||||
pb.tx_template = template_tx;
|
||||
currency::tx_service_attachment att = AUTO_VAL_INIT(att);
|
||||
att.body = t_serializable_object_to_blob(pb);
|
||||
att.service_id = BC_ESCROW_SERVICE_ID;
|
||||
att.instruction = BC_ESCROW_SERVICE_INSTRUCTION_PROPOSAL;
|
||||
att.flags = TX_SERVICE_ATTACHMENT_ENCRYPT_BODY | TX_SERVICE_ATTACHMENT_DEFLATE_BODY;
|
||||
ctp.attachments.push_back(att);
|
||||
|
||||
std::vector<currency::extra_v> extra;
|
||||
std::vector<currency::attachment_v> attachments;
|
||||
attachments.push_back(att);
|
||||
currency::keypair one_time_key_transport_tx = AUTO_VAL_INIT(one_time_key_transport_tx);
|
||||
ctp.crypt_address = ecrow_details.b_addr;
|
||||
ctp.dust_policy = tx_dust_policy(DEFAULT_DUST_THRESHOLD);
|
||||
ctp.fake_outputs_count = fake_outputs_count;
|
||||
ctp.fee = fee;
|
||||
ctp.shuffle = true;
|
||||
ctp.split_strategy_id = detail::ssi_digit;
|
||||
ctp.tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED;
|
||||
ctp.unlock_time = unlock_time;
|
||||
|
||||
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
|
||||
try
|
||||
{
|
||||
prepare_transaction(destinations,
|
||||
fake_outputs_count,
|
||||
unlock_time,
|
||||
fee,
|
||||
extra,
|
||||
attachments,
|
||||
detail::digit_split_strategy,
|
||||
tx_dust_policy(DEFAULT_DUST_THRESHOLD),
|
||||
ecrow_detaild.b_addr,
|
||||
tx,
|
||||
CURRENCY_TO_KEY_OUT_RELAXED,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
selected_transfers,
|
||||
one_time_key_transport_tx,
|
||||
prepared_destinations);
|
||||
prepare_transaction(ctp, ftp);
|
||||
finalize_transaction(ftp, tx, crypto::secret_key(), false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
|
@ -2909,15 +2999,14 @@ void wallet2::send_escrow_proposal(const bc_services::contract_private_details&
|
|||
|
||||
send_transaction_to_network(tx);
|
||||
|
||||
mark_transfers_as_spent(selected_transfers, std::string("escrow proposal sent, tx <") + epee::string_tools::pod_to_hex(get_transaction_hash(tx)) + ">");
|
||||
add_sent_tx_detailed_info(tx, prepared_destinations, selected_transfers);
|
||||
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.prepared_destinations, ftp.selected_transfers);
|
||||
|
||||
print_tx_sent_message(tx, "(from multisig)", fee);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::prepare_tx_sources(uint64_t needed_money, size_t fake_outputs_count, uint64_t dust_threshold, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money)
|
||||
{
|
||||
|
||||
found_money = select_transfers(needed_money, fake_outputs_count, dust_threshold, selected_indicies);
|
||||
THROW_IF_FALSE_WALLET_EX_MES(found_money >= needed_money, error::not_enough_money, "wallet_dump: " << ENDL << dump_trunsfers(false), found_money, needed_money, 0);
|
||||
|
||||
|
|
@ -2933,29 +3022,28 @@ bool wallet2::prepare_tx_sources(uint64_t needed_money, size_t fake_outputs_coun
|
|||
for (uint64_t i: selected_indicies)
|
||||
{
|
||||
auto it = m_transfers.begin() + i;
|
||||
THROW_IF_TRUE_WALLET_EX(it->m_ptx_wallet_info->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
|
||||
"m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
|
||||
" is greater or equal to outputs count = " + std::to_string(it->m_ptx_wallet_info->m_tx.vout.size()));
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->m_ptx_wallet_info->m_tx.vout.size() > it->m_internal_output_index,
|
||||
"m_internal_output_index = " << it->m_internal_output_index <<
|
||||
" is greater or equal to outputs count = " << it->m_ptx_wallet_info->m_tx.vout.size());
|
||||
req.amounts.push_back(it->amount());
|
||||
}
|
||||
|
||||
bool r = m_core_proxy->call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(req, daemon_resp);
|
||||
THROW_IF_TRUE_WALLET_EX(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
|
||||
THROW_IF_TRUE_WALLET_EX(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
|
||||
THROW_IF_TRUE_WALLET_EX(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
|
||||
THROW_IF_TRUE_WALLET_EX(daemon_resp.outs.size() != selected_indicies.size(), error::wallet_internal_error,
|
||||
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " +
|
||||
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_indicies.size()));
|
||||
THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs.bin");
|
||||
THROW_IF_FALSE_WALLET_EX(daemon_resp.status != CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
|
||||
THROW_IF_FALSE_WALLET_EX(daemon_resp.status == CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(daemon_resp.outs.size() == selected_indicies.size(),
|
||||
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " << daemon_resp.outs.size() << ", expected: " << selected_indicies.size());
|
||||
|
||||
std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount> scanty_outs;
|
||||
BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs)
|
||||
for(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs : daemon_resp.outs)
|
||||
{
|
||||
if (amount_outs.outs.size() < req.outs_count)
|
||||
{
|
||||
scanty_outs.push_back(amount_outs);
|
||||
}
|
||||
}
|
||||
THROW_IF_TRUE_WALLET_EX(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
|
||||
THROW_IF_FALSE_WALLET_EX(scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
|
||||
}
|
||||
|
||||
//prepare inputs
|
||||
|
|
@ -2972,7 +3060,7 @@ bool wallet2::prepare_tx_sources(uint64_t needed_money, size_t fake_outputs_coun
|
|||
if (daemon_resp.outs.size())
|
||||
{
|
||||
daemon_resp.outs[i].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index; });
|
||||
BOOST_FOREACH(out_entry& daemon_oe, daemon_resp.outs[i].outs)
|
||||
for(out_entry& daemon_oe : daemon_resp.outs[i].outs)
|
||||
{
|
||||
if (td.m_global_output_index == daemon_oe.global_amount_index)
|
||||
continue;
|
||||
|
|
@ -3012,14 +3100,13 @@ bool wallet2::prepare_tx_sources(crypto::hash multisig_id, std::vector<currency:
|
|||
THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_multisig_transfers.end(), "can't find multisig_id: " + epee::string_tools::pod_to_hex(multisig_id));
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(!it->second.is_spent(), "output with multisig_id: " + epee::string_tools::pod_to_hex(multisig_id) + " has already been spent by other party at height " + epee::string_tools::num_to_string_fast(it->second.m_spent_height));
|
||||
|
||||
sources.push_back(AUTO_VAL_INIT(currency::tx_source_entry()));
|
||||
currency::tx_source_entry& src = sources.back();
|
||||
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(it->second.m_internal_output_index < it->second.m_ptx_wallet_info->m_tx.vout.size(), "it->second.m_internal_output_index < it->second.m_tx.vout.size()");
|
||||
const tx_out& out = it->second.m_ptx_wallet_info->m_tx.vout[it->second.m_internal_output_index];
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(out.target.type() == typeid(txout_multisig), "ms out target type is " << out.target.type().name() << ", expected: txout_multisig");
|
||||
const txout_multisig& ms_out = boost::get<txout_multisig>(it->second.m_ptx_wallet_info->m_tx.vout[it->second.m_internal_output_index].target);
|
||||
const txout_multisig& ms_out = boost::get<txout_multisig>(out.target);
|
||||
|
||||
sources.push_back(AUTO_VAL_INIT(currency::tx_source_entry()));
|
||||
currency::tx_source_entry& src = sources.back();
|
||||
src.amount = found_money = out.amount;
|
||||
src.real_output_in_tx_index = it->second.m_internal_output_index;
|
||||
src.real_out_tx_key = get_tx_pub_key_from_extra(it->second.m_ptx_wallet_info->m_tx);
|
||||
|
|
@ -3394,15 +3481,15 @@ std::string wallet2::get_alias_for_address(const std::string& addr)
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::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,
|
||||
const std::vector<currency::attachment_v>& attachments,
|
||||
currency::transaction& tx)
|
||||
{
|
||||
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, attachments, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx);
|
||||
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, attachments, detail::ssi_digit, tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::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)
|
||||
const std::vector<currency::attachment_v>& attachments)
|
||||
{
|
||||
currency::transaction tx;
|
||||
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, attachments, tx);
|
||||
|
|
@ -3483,5 +3570,273 @@ void wallet2::print_source_entry(const currency::tx_source_entry& src) const
|
|||
WLT_LOG_L0("amount=" << currency::print_money(src.amount) << ", real_output=" << src.real_output << ", real_output_in_tx_index=" << src.real_output_in_tx_index << ", indexes: " << indexes.str());
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const
|
||||
{
|
||||
const std::unordered_map<crypto::hash, crypto::secret_key>::const_iterator i = m_tx_keys.find(txid);
|
||||
if (i == m_tx_keys.end())
|
||||
return false;
|
||||
tx_key = i->second;
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::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)
|
||||
{
|
||||
currency::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
|
||||
if (needed_money < found_money)
|
||||
{
|
||||
change_dts.addr.push_back(m_account.get_keys().m_account_address);
|
||||
change_dts.amount = found_money - needed_money;
|
||||
}
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(found_money >= needed_money, "needed_money==" << needed_money << " < found_money==" << found_money);
|
||||
|
||||
uint64_t dust = 0;
|
||||
bool r = detail::apply_split_strategy_by_id(destination_split_strategy_id, dsts, change_dts, dust_policy.dust_threshold, final_detinations, dust, WALLET_MAX_ALLOWED_OUTPUT_AMOUNT);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "invalid split strategy id: " << destination_split_strategy_id);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(dust_policy.dust_threshold >= dust, "invalid dust value: dust = " << dust << ", dust_threshold = " << dust_policy.dust_threshold);
|
||||
|
||||
//@#@
|
||||
#ifdef _DEBUG
|
||||
if (final_detinations.size() > 10)
|
||||
{
|
||||
WLT_LOG_L0("final_detinations.size()=" << final_detinations.size());
|
||||
}
|
||||
#endif
|
||||
//@#@
|
||||
|
||||
if (0 != dust && !dust_policy.add_to_fee)
|
||||
{
|
||||
final_detinations.push_back(currency::tx_destination_entry(dust, dust_policy.addr_for_dust));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
/*void wallet2::prepare_transaction(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,
|
||||
tools::detail::split_strategy_id_t destination_split_strategy_id,
|
||||
const tx_dust_policy& dust_policy,
|
||||
const currency::account_public_address& crypt_address,
|
||||
currency::transaction &tx,
|
||||
uint8_t tx_outs_attr,
|
||||
bool shuffle,
|
||||
bool mark_tx_as_complete,
|
||||
uint8_t flags,
|
||||
std::vector<uint64_t>& selected_transfers,
|
||||
currency::keypair& one_time_key,
|
||||
std::vector<currency::tx_destination_entry>& prepared_destinations,
|
||||
crypto::hash multisig_id)
|
||||
{
|
||||
// TODO
|
||||
}*/
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::prepare_transaction(const construct_tx_param& ctp, finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate /* = currency::transaction() */)
|
||||
{
|
||||
TIME_MEASURE_START_MS(get_needed_money_time);
|
||||
uint64_t needed_money = get_needed_money(ctp.fee, ctp.dsts);
|
||||
if (ctp.flags & TX_FLAG_SIGNATURE_MODE_SEPARATE && tx_for_mode_separate.vout.size())
|
||||
{
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(get_tx_flags(tx_for_mode_separate) & TX_FLAG_SIGNATURE_MODE_SEPARATE, "tx_param.flags differs from tx.flags");
|
||||
needed_money += (currency::get_outs_money_amount(tx_for_mode_separate) - get_inputs_money_amount(tx_for_mode_separate));
|
||||
}
|
||||
TIME_MEASURE_FINISH_MS(get_needed_money_time);
|
||||
|
||||
uint64_t found_money = 0;
|
||||
|
||||
TIME_MEASURE_START_MS(prepare_tx_sources_time);
|
||||
if (ctp.multisig_id == currency::null_hash)
|
||||
prepare_tx_sources(needed_money, ctp.fake_outputs_count, ctp.dust_policy.dust_threshold, ftp.sources, ftp.selected_transfers, found_money);
|
||||
else
|
||||
prepare_tx_sources(ctp.multisig_id, ftp.sources, found_money);
|
||||
TIME_MEASURE_FINISH_MS(prepare_tx_sources_time);
|
||||
|
||||
TIME_MEASURE_START_MS(prepare_tx_destinations_time);
|
||||
prepare_tx_destinations(needed_money, found_money, static_cast<detail::split_strategy_id_t>(ctp.split_strategy_id), ctp.dust_policy, ctp.dsts, ftp.prepared_destinations);
|
||||
TIME_MEASURE_FINISH_MS(prepare_tx_destinations_time);
|
||||
|
||||
if (ctp.mark_tx_as_complete && !ftp.sources.empty())
|
||||
ftp.sources.back().separately_signed_tx_complete = true;
|
||||
|
||||
|
||||
ftp.unlock_time = ctp.unlock_time;
|
||||
ftp.extra = ctp.extra; // TODO consider move semantic
|
||||
ftp.attachments = ctp.attachments; // TODO consider move semantic
|
||||
ftp.crypt_address = ctp.crypt_address;
|
||||
ftp.tx_outs_attr = ctp.tx_outs_attr;
|
||||
ftp.shuffle = ctp.shuffle;
|
||||
ftp.flags = ctp.flags;
|
||||
ftp.multisig_id = ctp.multisig_id;
|
||||
|
||||
/* TODO
|
||||
WLT_LOG_GREEN("[prepare_transaction]: get_needed_money_time: " << get_needed_money_time << " ms"
|
||||
<< ", prepare_tx_sources_time: " << prepare_tx_sources_time << " ms"
|
||||
<< ", prepare_tx_destinations_time: " << prepare_tx_destinations_time << " ms"
|
||||
<< ", construct_tx_time: " << construct_tx_time << " ms"
|
||||
<< ", sign_ms_input_time: " << sign_ms_input_time << " ms",
|
||||
LOG_LEVEL_0);*/
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx)
|
||||
{
|
||||
TIME_MEASURE_START_MS(construct_tx_time);
|
||||
bool r = currency::construct_tx(m_account.get_keys(),
|
||||
ftp.sources,
|
||||
ftp.prepared_destinations,
|
||||
ftp.extra,
|
||||
ftp.attachments,
|
||||
tx,
|
||||
tx_key,
|
||||
ftp.unlock_time,
|
||||
ftp.crypt_address,
|
||||
0, // expiration time
|
||||
ftp.tx_outs_attr,
|
||||
ftp.shuffle,
|
||||
ftp.flags);
|
||||
TIME_MEASURE_FINISH_MS(construct_tx_time);
|
||||
THROW_IF_FALSE_WALLET_EX(r, error::tx_not_constructed, ftp.sources, ftp.prepared_destinations, ftp.unlock_time);
|
||||
|
||||
TIME_MEASURE_START_MS(sign_ms_input_time);
|
||||
if (ftp.multisig_id != currency::null_hash)
|
||||
{
|
||||
// In case there's multisig input is used -- sign it partially with this wallet's keys (we don't have any others here).
|
||||
// NOTE: this tx will not be ready to send until all other necessary signs for ms input would made.
|
||||
auto it = m_multisig_transfers.find(ftp.multisig_id);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_multisig_transfers.end(), "can't find multisig_id: " << ftp.multisig_id);
|
||||
const currency::transaction& ms_source_tx = it->second.m_ptx_wallet_info->m_tx;
|
||||
bool is_tx_input_fully_signed = false;
|
||||
r = sign_multisig_input_in_tx(tx, 0, m_account.get_keys(), ms_source_tx, &is_tx_input_fully_signed); // it's assumed that ms input is the first one (index 0)
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(r && !is_tx_input_fully_signed, "sign_multisig_input_in_tx failed: r = " << r << ", is_tx_input_fully_signed = " << is_tx_input_fully_signed);
|
||||
}
|
||||
TIME_MEASURE_FINISH_MS(sign_ms_input_time);
|
||||
|
||||
m_tx_keys.insert(std::make_pair(get_transaction_hash(tx), tx_key));
|
||||
|
||||
THROW_IF_FALSE_WALLET_EX(get_object_blobsize(tx) < CURRENCY_MAX_TRANSACTION_BLOB_SIZE, error::tx_too_big, tx, m_upper_transaction_size_limit);
|
||||
|
||||
TIME_MEASURE_START(send_transaction_to_network_time);
|
||||
if (broadcast_tx)
|
||||
send_transaction_to_network(tx);
|
||||
TIME_MEASURE_FINISH(send_transaction_to_network_time);
|
||||
|
||||
TIME_MEASURE_START(add_sent_tx_detailed_info_time);
|
||||
if (broadcast_tx)
|
||||
add_sent_tx_detailed_info(tx, ftp.prepared_destinations, ftp.selected_transfers);
|
||||
TIME_MEASURE_FINISH(add_sent_tx_detailed_info_time);
|
||||
|
||||
/* TODO
|
||||
WLT_LOG_GREEN("[prepare_transaction]: get_needed_money_time: " << get_needed_money_time << " ms"
|
||||
<< ", prepare_tx_sources_time: " << prepare_tx_sources_time << " ms"
|
||||
<< ", prepare_tx_destinations_time: " << prepare_tx_destinations_time << " ms"
|
||||
<< ", construct_tx_time: " << construct_tx_time << " ms"
|
||||
<< ", sign_ms_input_time: " << sign_ms_input_time << " ms",
|
||||
LOG_LEVEL_0);*/
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::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;
|
||||
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, attachments, destination_split_strategy_id, dust_policy, tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
tx workflow:
|
||||
|
||||
prepare_transaction(construct_param, construct_res, tools::detail::digit_split_strategy);
|
||||
mark_transfers_as_spent(construct_res.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been accepted with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(construct_res.tx)) + ">");
|
||||
--
|
||||
store/load
|
||||
--
|
||||
finalize_transaction()
|
||||
construct_tx
|
||||
sign ms input
|
||||
send_transaction_to_network(construct_res.tx);
|
||||
add_sent_tx_detailed_info(construct_res.tx, construct_res.prepared_destinations, construct_res.selected_transfers);
|
||||
print_tx_sent_message(construct_res.tx, "(contract)", construct_param.fee);
|
||||
*/
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::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,
|
||||
bool shuffle,
|
||||
uint8_t flags,
|
||||
bool send_to_network)
|
||||
{
|
||||
TIME_MEASURE_START(precalculation_time);
|
||||
//using namespace currency;
|
||||
currency::keypair onetime_keys = AUTO_VAL_INIT(onetime_keys);
|
||||
|
||||
construct_tx_param tx_param = AUTO_VAL_INIT(tx_param);
|
||||
tx_param.attachments = attachments;
|
||||
tx_param.crypt_address = currency::get_crypt_address_from_destinations(m_account.get_keys(), dsts);
|
||||
tx_param.dsts = dsts;
|
||||
tx_param.dust_policy = dust_policy;
|
||||
tx_param.extra = extra;
|
||||
tx_param.fake_outputs_count = fake_outputs_count;
|
||||
tx_param.fee = fee;
|
||||
tx_param.flags = flags;
|
||||
// tx_param.mark_tx_as_complete
|
||||
// tx_param.multisig_id
|
||||
tx_param.shuffle = shuffle;
|
||||
tx_param.split_strategy_id = destination_split_strategy_id;
|
||||
tx_param.tx_outs_attr = tx_outs_attr;
|
||||
tx_param.unlock_time = unlock_time;
|
||||
TIME_MEASURE_FINISH(precalculation_time);
|
||||
|
||||
TIME_MEASURE_START(prepare_transaction_time);
|
||||
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
|
||||
prepare_transaction(tx_param, ftp);
|
||||
TIME_MEASURE_FINISH(prepare_transaction_time);
|
||||
|
||||
|
||||
if (m_watch_only)
|
||||
{
|
||||
blobdata bl = t_serializable_object_to_blob(tx_param);
|
||||
crypto::chacha_crypt(bl, m_account.get_keys().m_view_secret_key);
|
||||
epee::file_io_utils::save_string_to_file("unsigned_zano_tx", bl);
|
||||
LOG_PRINT_L0("Transaction stored to unsigned_zano_tx. You need to sign this tx using a full-access wallet.");
|
||||
//relay_blob = bl; // TODO: save encrypted tx param blob to relay_blob
|
||||
|
||||
// unlock transfers at the very end
|
||||
TIME_MEASURE_START(mark_transfers_as_spent_time);
|
||||
mark_transfers_as_spent(ftp.selected_transfers, std::string("money transfer"));
|
||||
TIME_MEASURE_FINISH(mark_transfers_as_spent_time);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
TIME_MEASURE_START(finalize_transaction_time);
|
||||
finalize_transaction(ftp, tx, crypto::secret_key(), send_to_network);
|
||||
TIME_MEASURE_FINISH(finalize_transaction_time);
|
||||
|
||||
// unlock transfers at the very end
|
||||
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(tx)));
|
||||
TIME_MEASURE_FINISH(mark_transfers_as_spent_time);
|
||||
|
||||
/* TODO
|
||||
WLT_LOG_GREEN("[wallet::transfer] prepare_transaction_time: " << print_fixed_decimal_point(prepare_transaction_time, 3)
|
||||
<< ", precalculation_time: " << print_fixed_decimal_point(precalculation_time, 3)
|
||||
<< ", send_transaction_to_network_time: " << print_fixed_decimal_point(send_transaction_to_network_time, 3)
|
||||
<< ", mark_transfers_as_spent_time: " << print_fixed_decimal_point(mark_transfers_as_spent_time, 3)
|
||||
<< ", add_sent_tx_detailed_info_time: " << print_fixed_decimal_point(add_sent_tx_detailed_info_time, 3),
|
||||
LOG_LEVEL_0);*/
|
||||
|
||||
//print_tx_sent_message(tx, std::string() + "(transaction)", fee);
|
||||
}
|
||||
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "currency_core/offers_services_helpers.h"
|
||||
#include "currency_core/bc_offers_serialization.h"
|
||||
#include "currency_core/bc_escrow_service.h"
|
||||
#include "common/pod_array_file_container.h"
|
||||
|
||||
|
||||
#define WALLET_DEFAULT_TX_SPENDABLE_AGE 10
|
||||
|
|
@ -61,6 +62,7 @@ ENABLE_CHANNEL_BY_DEFAULT("wallet");
|
|||
#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)
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
|
@ -105,22 +107,233 @@ namespace tools
|
|||
, 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
|
||||
{
|
||||
out_key_to_ki() {}
|
||||
out_key_to_ki(const crypto::public_key& out_key, const crypto::key_image& key_image) : out_key(out_key), key_image(key_image) {}
|
||||
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
|
||||
{
|
||||
currency::decompose_amount_into_digits(de.amount, dust_threshold,
|
||||
[&](uint64_t chunk) { splitted_dsts.push_back(currency::tx_destination_entry(chunk, de.addr)); },
|
||||
[&](uint64_t a_dust) { splitted_dsts.push_back(currency::tx_destination_entry(a_dust, de.addr)); }, 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 = dsts;
|
||||
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;
|
||||
uint64_t fee;
|
||||
tx_dust_policy dust_policy;
|
||||
crypto::hash multisig_id;
|
||||
uint8_t flags;
|
||||
uint8_t split_strategy_id;
|
||||
bool mark_tx_as_complete;
|
||||
|
||||
// constructing tx
|
||||
uint64_t unlock_time;
|
||||
std::vector<currency::extra_v> extra;
|
||||
std::vector<currency::attachment_v> attachments;
|
||||
currency::account_public_address crypt_address;
|
||||
uint8_t tx_outs_attr;
|
||||
bool shuffle;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(dsts)
|
||||
FIELD(fake_outputs_count)
|
||||
FIELD(fee)
|
||||
FIELD(dust_policy)
|
||||
FIELD(multisig_id)
|
||||
FIELD(flags)
|
||||
FIELD(split_strategy_id)
|
||||
FIELD(mark_tx_as_complete)
|
||||
FIELD(unlock_time)
|
||||
FIELD(extra)
|
||||
FIELD(attachments)
|
||||
FIELD(crypt_address)
|
||||
FIELD(tx_outs_attr)
|
||||
FIELD(shuffle)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct finalize_tx_param
|
||||
{
|
||||
//std::vector<currency::tx_destination_entry> dsts;
|
||||
//size_t fake_outputs_count;
|
||||
//uint64_t fee;
|
||||
//tx_dust_policy dust_policy;
|
||||
//bool mark_tx_as_complete;
|
||||
//detail::split_strategy_id_t split_strategy_id;
|
||||
|
||||
uint64_t unlock_time;
|
||||
std::vector<currency::extra_v> extra;
|
||||
std::vector<currency::attachment_v> attachments;
|
||||
currency::account_public_address crypt_address;
|
||||
uint8_t tx_outs_attr;
|
||||
bool shuffle;
|
||||
uint8_t flags;
|
||||
crypto::hash multisig_id;
|
||||
std::vector<currency::tx_source_entry> sources;
|
||||
std::vector<uint64_t> selected_transfers;
|
||||
std::vector<currency::tx_destination_entry> prepared_destinations;
|
||||
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(unlock_time)
|
||||
FIELD(extra)
|
||||
FIELD(attachments)
|
||||
FIELD(crypt_address)
|
||||
FIELD(tx_outs_attr)
|
||||
FIELD(shuffle)
|
||||
FIELD(flags)
|
||||
FIELD(multisig_id)
|
||||
FIELD(sources)
|
||||
FIELD(selected_transfers)
|
||||
FIELD(prepared_destinations)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
/*uct constructed_tx_data
|
||||
{
|
||||
//std::vector<currency::tx_destination_entry> prepared_destinations;
|
||||
currency::transaction tx;
|
||||
//std::vector<uint64_t> selected_transfers;
|
||||
crypto::secret_key one_time_key;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
//FIELD(prepared_destinations)
|
||||
FIELD(tx)
|
||||
//FIELD(selected_transfers)
|
||||
FIELD(one_time_key)
|
||||
END_SERIALIZE()
|
||||
};*/
|
||||
|
||||
|
||||
class wallet2
|
||||
{
|
||||
wallet2(const wallet2&) : m_stop(false),
|
||||
m_wcallback(new i_wallet2_callback()),
|
||||
m_height_of_start_sync(0),
|
||||
m_last_sync_percent(0),
|
||||
m_do_rise_transfer(false)
|
||||
m_do_rise_transfer(false),
|
||||
m_watch_only(false)
|
||||
{};
|
||||
public:
|
||||
wallet2() : m_stop(false),
|
||||
|
|
@ -131,7 +344,8 @@ namespace tools
|
|||
m_last_sync_percent(0),
|
||||
m_fake_outputs_count(0),
|
||||
m_do_rise_transfer(false),
|
||||
m_log_prefix("???")
|
||||
m_log_prefix("???"),
|
||||
m_watch_only(false)
|
||||
{
|
||||
m_core_runtime_config = currency::get_default_core_runtime_config();
|
||||
};
|
||||
|
|
@ -298,24 +512,22 @@ namespace tools
|
|||
|
||||
void transfer(uint64_t amount, const currency::account_public_address& acc);
|
||||
|
||||
template<typename T>
|
||||
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,
|
||||
T destination_split_strategy,
|
||||
const std::vector<currency::attachment_v>& attachments,
|
||||
detail::split_strategy_id_t destination_split_strategy_id,
|
||||
const tx_dust_policy& dust_policy);
|
||||
|
||||
template<typename destination_split_strategy_t>
|
||||
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,
|
||||
destination_split_strategy_t destination_split_strategy,
|
||||
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,
|
||||
|
|
@ -328,14 +540,14 @@ namespace tools
|
|||
uint64_t unlock_time,
|
||||
uint64_t fee,
|
||||
const std::vector<currency::extra_v>& extra,
|
||||
const std::vector<currency::attachment_v> attachments);
|
||||
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,
|
||||
const std::vector<currency::attachment_v>& attachments,
|
||||
currency::transaction& tx);
|
||||
|
||||
template<typename destination_split_strategy_t>
|
||||
|
|
@ -347,7 +559,7 @@ namespace tools
|
|||
uint64_t unlock_time,
|
||||
uint64_t fee,
|
||||
const std::vector<currency::extra_v>& extra,
|
||||
const std::vector<currency::attachment_v> attachments,
|
||||
const std::vector<currency::attachment_v>& attachments,
|
||||
destination_split_strategy_t destination_split_strategy,
|
||||
const tx_dust_policy& dust_policy,
|
||||
currency::transaction &tx,
|
||||
|
|
@ -378,7 +590,7 @@ namespace tools
|
|||
const std::string& payment_id,
|
||||
currency::transaction& tx,
|
||||
std::vector<uint64_t>& selected_transfers,
|
||||
currency::keypair& one_time_key);
|
||||
crypto::secret_key& one_time_key);
|
||||
|
||||
void send_escrow_proposal(const bc_services::contract_private_details& ecrow_detaild,
|
||||
size_t fake_outputs_count,
|
||||
|
|
@ -388,8 +600,7 @@ namespace tools
|
|||
uint64_t b_release_fee,
|
||||
const std::string& payment_id,
|
||||
currency::transaction &proposal_tx,
|
||||
currency::transaction &escrow_template_tx /*,
|
||||
crypto::hash& contract_id*/ );
|
||||
currency::transaction &escrow_template_tx);
|
||||
|
||||
bool check_connection();
|
||||
template<typename idle_condition_cb_t> //do refresh as external callback
|
||||
|
|
@ -398,7 +609,11 @@ namespace tools
|
|||
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
|
||||
|
||||
// Returns all payments by given id in unspecified order
|
||||
void get_payments(const std::string& payment_id, std::list<payment_details>& payments) const;
|
||||
void get_payments(const std::string& payment_id, std::list<payment_details>& payments, uint64_t min_height = 0) const;
|
||||
|
||||
bool is_watch_only() const { return m_watch_only; }
|
||||
void sign_transfer(const std::string& tx_sources_blob, std::string& signed_tx_blob, currency::transaction& tx);
|
||||
|
||||
|
||||
bool get_transfer_address(const std::string& adr_str, currency::account_public_address& addr, std::string& payment_id);
|
||||
uint64_t get_blockchain_current_height() const { return m_blockchain.size(); }
|
||||
|
|
@ -413,6 +628,7 @@ namespace tools
|
|||
a & m_key_images;
|
||||
a & m_unconfirmed_txs;
|
||||
a & m_unconfirmed_multisig_transfers;
|
||||
a & m_tx_keys;
|
||||
a & m_payments;
|
||||
a & m_transfer_history;
|
||||
a & m_unconfirmed_in_transfers;
|
||||
|
|
@ -477,26 +693,29 @@ namespace tools
|
|||
|
||||
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;
|
||||
|
||||
template<typename destination_split_strategy_t>
|
||||
void prepare_transaction(const std::vector<currency::tx_destination_entry>& dsts,
|
||||
/*void prepare_transaction(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 std::vector<currency::attachment_v>& attachments,
|
||||
detail::split_strategy_id_t destination_split_strategy_id,
|
||||
const tx_dust_policy& dust_policy,
|
||||
const currency::account_public_address& crypt_address,
|
||||
currency::transaction &tx,
|
||||
OUT currency::transaction &tx,
|
||||
uint8_t tx_outs_attr,
|
||||
bool shuffle,
|
||||
bool mark_tx_as_complete,
|
||||
uint8_t flags,
|
||||
std::vector<uint64_t>& selected_transfers,
|
||||
currency::keypair& one_time_key,
|
||||
std::vector<currency::tx_destination_entry>& prepared_destinations,
|
||||
crypto::hash multisig_id = currency::null_hash);
|
||||
OUT std::vector<uint64_t>& selected_transfers,
|
||||
OUT currency::keypair& one_time_key,
|
||||
OUT std::vector<currency::tx_destination_entry>& prepared_destinations,
|
||||
crypto::hash multisig_id = currency::null_hash);*/
|
||||
|
||||
void prepare_transaction(const construct_tx_param& ctp, finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate = currency::transaction());
|
||||
void finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx);
|
||||
|
||||
std::string get_log_prefix() const { return m_log_prefix; }
|
||||
|
||||
|
|
@ -520,7 +739,7 @@ private:
|
|||
uint64_t select_transfers(uint64_t needed_money, 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);
|
||||
bool prepare_file_names(const std::string& file_path);
|
||||
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,
|
||||
|
|
@ -557,10 +776,9 @@ private:
|
|||
bool prepare_tx_sources(uint64_t needed_money, size_t fake_outputs_count, uint64_t dust_threshold, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
|
||||
bool prepare_tx_sources(crypto::hash multisig_id, std::vector<currency::tx_source_entry>& sources, uint64_t& found_money);
|
||||
uint64_t get_needed_money(uint64_t fee, const std::vector<currency::tx_destination_entry>& dsts);
|
||||
template<class destination_split_strategy_t>
|
||||
void prepare_tx_destinations(uint64_t needed_money,
|
||||
uint64_t found_money,
|
||||
destination_split_strategy_t destination_split_strategy,
|
||||
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);
|
||||
|
|
@ -600,37 +818,7 @@ private:
|
|||
void print_source_entry(const currency::tx_source_entry& src) const;
|
||||
|
||||
void init_log_prefix();
|
||||
|
||||
struct construct_tx_param
|
||||
{
|
||||
std::vector<currency::tx_destination_entry> dsts;
|
||||
size_t fake_outputs_count;
|
||||
uint64_t unlock_time;
|
||||
uint64_t fee;
|
||||
std::vector<currency::extra_v> extra;
|
||||
std::vector<currency::attachment_v> attachments;
|
||||
tx_dust_policy dust_policy;
|
||||
currency::account_public_address crypt_address;
|
||||
uint8_t tx_outs_attr;
|
||||
bool shuffle;
|
||||
bool mark_tx_as_complete;
|
||||
uint8_t flags;
|
||||
crypto::hash multisig_id;
|
||||
};
|
||||
|
||||
struct constructed_tx_data
|
||||
{
|
||||
std::vector<currency::tx_destination_entry> prepared_destinations;
|
||||
currency::transaction tx;
|
||||
std::vector<uint64_t> selected_transfers;
|
||||
currency::keypair one_time_key;
|
||||
};
|
||||
|
||||
|
||||
template<typename destination_split_strategy_t>
|
||||
void prepare_transaction(const construct_tx_param& construct_tx_data,
|
||||
constructed_tx_data& res, destination_split_strategy_t destination_split_strategy);
|
||||
|
||||
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,
|
||||
|
|
@ -646,8 +834,10 @@ private:
|
|||
|
||||
|
||||
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;
|
||||
std::vector<crypto::hash> m_blockchain;
|
||||
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
|
||||
|
|
@ -658,6 +848,8 @@ private:
|
|||
multisig_transfer_container m_multisig_transfers;
|
||||
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;
|
||||
|
|
@ -665,6 +857,7 @@ private:
|
|||
std::unordered_map<crypto::hash, currency::transaction> m_unconfirmed_in_transfers;
|
||||
std::unordered_map<crypto::hash, tools::wallet_rpc::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::shared_ptr<i_core_proxy> m_core_proxy;
|
||||
std::shared_ptr<i_wallet2_callback> m_wcallback;
|
||||
|
|
@ -680,10 +873,9 @@ private:
|
|||
std::string m_miner_text_info;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}; // class wallet2
|
||||
|
||||
} // namespace tools
|
||||
|
||||
BOOST_CLASS_VERSION(tools::wallet2, WALLET_FILE_SERIALIZATION_VERSION)
|
||||
BOOST_CLASS_VERSION(tools::wallet_rpc::wallet_transfer_info, 9)
|
||||
|
|
@ -814,322 +1006,6 @@ namespace boost
|
|||
|
||||
namespace tools
|
||||
{
|
||||
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;
|
||||
|
||||
BOOST_FOREACH(auto& de, dsts)
|
||||
{
|
||||
if (de.addr.size() > 1)
|
||||
{
|
||||
//for multisig we don't 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)); },
|
||||
[&](uint64_t a_dust) { splitted_dsts.push_back(currency::tx_destination_entry(a_dust, de.addr)); }, 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 = dsts;
|
||||
if (change_dst.amount > 0)
|
||||
splitted_dsts.push_back(change_dst);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
void wallet2::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, T destination_split_strategy, const tx_dust_policy& dust_policy)
|
||||
{
|
||||
currency::transaction tx;
|
||||
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, attachments, destination_split_strategy, dust_policy, tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
template<typename destination_split_strategy_t>
|
||||
void wallet2::prepare_tx_destinations(uint64_t needed_money,
|
||||
uint64_t found_money,
|
||||
destination_split_strategy_t destination_split_strategy,
|
||||
const tx_dust_policy& dust_policy,
|
||||
const std::vector<currency::tx_destination_entry>& dsts,
|
||||
std::vector<currency::tx_destination_entry>& final_detinations)
|
||||
{
|
||||
currency::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
|
||||
if (needed_money < found_money)
|
||||
{
|
||||
change_dts.addr.push_back(m_account.get_keys().m_account_address);
|
||||
change_dts.amount = found_money - needed_money;
|
||||
}
|
||||
THROW_IF_FALSE_WALLET_EX(found_money >= needed_money, error::wallet_internal_error, "needed_money(" + std::to_string(needed_money)
|
||||
+ ") < found_money(" + std::to_string(found_money) + ") ");
|
||||
|
||||
uint64_t dust = 0;
|
||||
destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, final_detinations, dust, WALLET_MAX_ALLOWED_OUTPUT_AMOUNT);
|
||||
THROW_IF_FALSE_WALLET_EX(dust_policy.dust_threshold >= dust, error::wallet_internal_error, "invalid dust value: dust = " +
|
||||
std::to_string(dust) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
|
||||
|
||||
//@#@
|
||||
#ifdef _DEBUG
|
||||
if (final_detinations.size() > 10)
|
||||
{
|
||||
WLT_LOG_L0("final_detinations.size()=" << final_detinations.size());
|
||||
}
|
||||
#endif
|
||||
//@#@
|
||||
|
||||
if (0 != dust && !dust_policy.add_to_fee)
|
||||
{
|
||||
final_detinations.push_back(currency::tx_destination_entry(dust, dust_policy.addr_for_dust));
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
template<typename destination_split_strategy_t>
|
||||
void wallet2::prepare_transaction(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,
|
||||
const currency::account_public_address& crypt_address,
|
||||
currency::transaction &tx,
|
||||
uint8_t tx_outs_attr,
|
||||
bool shuffle,
|
||||
bool mark_tx_as_complete,
|
||||
uint8_t flags,
|
||||
std::vector<uint64_t>& selected_transfers,
|
||||
currency::keypair& one_time_key,
|
||||
std::vector<currency::tx_destination_entry>& prepared_destinations,
|
||||
crypto::hash multisig_id)
|
||||
{
|
||||
TIME_MEASURE_START_MS(get_needed_money_time);
|
||||
uint64_t needed_money = get_needed_money(fee, dsts);
|
||||
if (flags&TX_FLAG_SIGNATURE_MODE_SEPARATE && tx.vout.size())
|
||||
{
|
||||
needed_money += (currency::get_outs_money_amount(tx) - get_inputs_money_amount(tx));
|
||||
}
|
||||
TIME_MEASURE_FINISH_MS(get_needed_money_time);
|
||||
|
||||
std::vector<currency::tx_source_entry> sources;
|
||||
uint64_t found_money = 0;
|
||||
|
||||
TIME_MEASURE_START_MS(prepare_tx_sources_time);
|
||||
if (multisig_id == currency::null_hash)
|
||||
{
|
||||
prepare_tx_sources(needed_money, fake_outputs_count, dust_policy.dust_threshold, sources, selected_transfers, found_money);
|
||||
}
|
||||
else
|
||||
{
|
||||
prepare_tx_sources(multisig_id, sources, found_money);
|
||||
}
|
||||
TIME_MEASURE_FINISH_MS(prepare_tx_sources_time);
|
||||
|
||||
TIME_MEASURE_START_MS(prepare_tx_destinations_time);
|
||||
prepare_tx_destinations(needed_money, found_money, destination_split_strategy, dust_policy, dsts, prepared_destinations);
|
||||
TIME_MEASURE_FINISH_MS(prepare_tx_destinations_time);
|
||||
|
||||
if (mark_tx_as_complete && !sources.empty())
|
||||
sources.back().separately_signed_tx_complete = true;
|
||||
|
||||
TIME_MEASURE_START_MS(construct_tx_time);
|
||||
bool r = currency::construct_tx(m_account.get_keys(),
|
||||
sources,
|
||||
prepared_destinations,
|
||||
extra,
|
||||
attachments,
|
||||
tx,
|
||||
one_time_key.sec,
|
||||
unlock_time,
|
||||
crypt_address,
|
||||
0,
|
||||
tx_outs_attr,
|
||||
shuffle,
|
||||
flags);
|
||||
TIME_MEASURE_FINISH_MS(construct_tx_time);
|
||||
THROW_IF_TRUE_WALLET_EX(!r, error::tx_not_constructed, sources, prepared_destinations, unlock_time);
|
||||
|
||||
TIME_MEASURE_START_MS(sign_ms_input_time);
|
||||
if (multisig_id != currency::null_hash)
|
||||
{
|
||||
// In case there's multisig input is used -- sign it partially with this wallet's keys (we don't have any others here).
|
||||
// NOTE: this tx will not be ready to send until all other necessary signs for ms input would made.
|
||||
auto it = m_multisig_transfers.find(multisig_id);
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_multisig_transfers.end(), "can't find multisig_id: " << multisig_id);
|
||||
const currency::transaction& ms_source_tx = it->second.m_ptx_wallet_info->m_tx;
|
||||
bool is_tx_input_fully_signed = false;
|
||||
r = sign_multisig_input_in_tx(tx, 0, m_account.get_keys(), ms_source_tx, &is_tx_input_fully_signed);
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(r && !is_tx_input_fully_signed, "sign_multisig_input_in_tx failed: r = " << r << ", is_tx_input_fully_signed = " << is_tx_input_fully_signed);
|
||||
}
|
||||
TIME_MEASURE_FINISH_MS(sign_ms_input_time);
|
||||
|
||||
THROW_IF_TRUE_WALLET_EX(CURRENCY_MAX_TRANSACTION_BLOB_SIZE <= get_object_blobsize(tx), error::tx_too_big, tx, m_upper_transaction_size_limit);
|
||||
WLT_LOG_GREEN("[prepare_transaction]: get_needed_money_time: " << get_needed_money_time << " ms"
|
||||
<< ", prepare_tx_sources_time: " << prepare_tx_sources_time << " ms"
|
||||
<< ", prepare_tx_destinations_time: " << prepare_tx_destinations_time << " ms"
|
||||
<< ", construct_tx_time: " << construct_tx_time << " ms"
|
||||
<< ", sign_ms_input_time: " << sign_ms_input_time << " ms",
|
||||
LOG_LEVEL_0);
|
||||
}
|
||||
|
||||
template<typename destination_split_strategy_t>
|
||||
void wallet2::prepare_transaction(const construct_tx_param& construct_tx_data, constructed_tx_data& res,
|
||||
destination_split_strategy_t destination_split_strategy)
|
||||
{
|
||||
return prepare_transaction(construct_tx_data.dsts,
|
||||
construct_tx_data.fake_outputs_count,
|
||||
construct_tx_data.unlock_time,
|
||||
construct_tx_data.fee,
|
||||
construct_tx_data.extra,
|
||||
construct_tx_data.attachments,
|
||||
destination_split_strategy,
|
||||
construct_tx_data.dust_policy,
|
||||
construct_tx_data.crypt_address,
|
||||
res.tx,
|
||||
construct_tx_data.tx_outs_attr,
|
||||
construct_tx_data.shuffle,
|
||||
construct_tx_data.mark_tx_as_complete,
|
||||
construct_tx_data.flags,
|
||||
res.selected_transfers,
|
||||
res.one_time_key,
|
||||
res.prepared_destinations,
|
||||
construct_tx_data.multisig_id);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
template<typename destination_split_strategy_t>
|
||||
void wallet2::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,
|
||||
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)
|
||||
{
|
||||
TIME_MEASURE_START(precalculation_time);
|
||||
using namespace currency;
|
||||
std::vector<uint64_t> selected_transfers;
|
||||
std::vector<currency::tx_destination_entry> prepared_destinations;
|
||||
account_public_address crypt_address = currency::get_crypt_address_from_destinations(m_account.get_keys(), dsts);
|
||||
currency::keypair onetime_keys = AUTO_VAL_INIT(onetime_keys);
|
||||
TIME_MEASURE_FINISH(precalculation_time);
|
||||
|
||||
TIME_MEASURE_START(prepare_transaction_time);
|
||||
prepare_transaction(dsts,
|
||||
fake_outputs_count,
|
||||
unlock_time,
|
||||
fee,
|
||||
extra,
|
||||
attachments,
|
||||
destination_split_strategy,
|
||||
dust_policy,
|
||||
crypt_address,
|
||||
tx,
|
||||
tx_outs_attr,
|
||||
shuffle,
|
||||
false,
|
||||
flags,
|
||||
selected_transfers,
|
||||
onetime_keys,
|
||||
prepared_destinations);
|
||||
TIME_MEASURE_FINISH(prepare_transaction_time);
|
||||
|
||||
TIME_MEASURE_START(send_transaction_to_network_time);
|
||||
if (send_to_network)
|
||||
send_transaction_to_network(tx);
|
||||
TIME_MEASURE_FINISH(send_transaction_to_network_time);
|
||||
|
||||
TIME_MEASURE_START(mark_transfers_as_spent_time);
|
||||
mark_transfers_as_spent(selected_transfers, std::string("money transfer"));
|
||||
TIME_MEASURE_FINISH(mark_transfers_as_spent_time);
|
||||
|
||||
TIME_MEASURE_START(add_sent_tx_detailed_info_time);
|
||||
add_sent_tx_detailed_info(tx, prepared_destinations, selected_transfers);
|
||||
TIME_MEASURE_FINISH(add_sent_tx_detailed_info_time);
|
||||
|
||||
WLT_LOG_GREEN("[wallet::transfer] prepare_transaction_time: " << print_fixed_decimal_point(prepare_transaction_time, 3)
|
||||
<< ", precalculation_time: " << print_fixed_decimal_point(precalculation_time, 3)
|
||||
<< ", send_transaction_to_network_time: " << print_fixed_decimal_point(send_transaction_to_network_time, 3)
|
||||
<< ", mark_transfers_as_spent_time: " << print_fixed_decimal_point(mark_transfers_as_spent_time, 3)
|
||||
<< ", add_sent_tx_detailed_info_time: " << print_fixed_decimal_point(add_sent_tx_detailed_info_time, 3),
|
||||
LOG_LEVEL_0);
|
||||
|
||||
//print_tx_sent_message(tx, std::string() + "(transaction)", fee);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
template<typename idle_condition_cb_t> //do refresh as external callback
|
||||
bool wallet2::scan_pos(mining_context& cxt,
|
||||
std::atomic<bool>& stop,
|
||||
|
|
@ -1231,8 +1107,7 @@ namespace tools
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
||||
#if !defined(KEEP_WALLET_LOG_MACROS)
|
||||
#undef WLT_LOG_L0
|
||||
|
|
@ -1249,6 +1124,7 @@ namespace tools
|
|||
#undef WLT_LOG_YELLOW
|
||||
#undef WLT_CHECK_AND_ASSERT_MES
|
||||
#undef WLT_CHECK_AND_ASSERT_MES_NO_RET
|
||||
// TODO update this list
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,14 @@ namespace tools
|
|||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct wallet_common_error : public wallet_runtime_error
|
||||
{
|
||||
explicit wallet_common_error(std::string&& loc, const std::string& message)
|
||||
: wallet_runtime_error(std::move(loc), message)
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct unexpected_txin_type : public wallet_internal_error
|
||||
{
|
||||
explicit unexpected_txin_type(std::string&& loc, const currency::transaction& tx)
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ namespace tools
|
|||
bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx)
|
||||
{
|
||||
std::string payment_id;
|
||||
if (!epee::string_tools::parse_hexstr_to_binbuff(req.payment_id, payment_id))
|
||||
if (!currency::parse_payment_id_from_hex_str(req.payment_id, payment_id))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
|
||||
er.message = std::string("invalid payment id given: \'") + req.payment_id + "\', hex-encoded string was expected";
|
||||
|
|
@ -214,6 +214,7 @@ namespace tools
|
|||
for (auto payment : payment_list)
|
||||
{
|
||||
wallet_rpc::payment_details rpc_payment;
|
||||
rpc_payment.payment_id = req.payment_id;
|
||||
rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.m_tx_hash);
|
||||
rpc_payment.amount = payment.m_amount;
|
||||
rpc_payment.block_height = payment.m_block_height;
|
||||
|
|
@ -224,6 +225,38 @@ namespace tools
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx)
|
||||
{
|
||||
res.payments.clear();
|
||||
|
||||
for (auto & payment_id_str : req.payment_ids)
|
||||
{
|
||||
currency::payment_id_t payment_id;
|
||||
if (!currency::parse_payment_id_from_hex_str(payment_id_str, payment_id))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
|
||||
er.message = std::string("invalid payment id given: \'") + payment_id_str + "\', hex-encoded string was expected";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<wallet2::payment_details> payment_list;
|
||||
m_wallet.get_payments(payment_id, payment_list, req.min_block_height);
|
||||
|
||||
for (auto & payment : payment_list)
|
||||
{
|
||||
wallet_rpc::payment_details rpc_payment;
|
||||
rpc_payment.payment_id = payment_id_str;
|
||||
rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.m_tx_hash);
|
||||
rpc_payment.amount = payment.m_amount;
|
||||
rpc_payment.block_height = payment.m_block_height;
|
||||
rpc_payment.unlock_time = payment.m_unlock_time;
|
||||
res.payments.push_back(std::move(rpc_payment));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx)
|
||||
{
|
||||
std::string payment_id;
|
||||
|
|
|
|||
|
|
@ -38,13 +38,15 @@ namespace tools
|
|||
|
||||
BEGIN_URI_MAP2()
|
||||
BEGIN_JSON_RPC_MAP("/json_rpc")
|
||||
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
|
||||
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
|
||||
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
|
||||
MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE)
|
||||
MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS)
|
||||
MAP_JON_RPC_WE("make_integrated_address", on_make_integrated_address, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS)
|
||||
MAP_JON_RPC_WE("split_integrated_address", on_split_integrated_address, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS)
|
||||
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
|
||||
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
|
||||
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
|
||||
MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE)
|
||||
MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS)
|
||||
MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS)
|
||||
MAP_JON_RPC_WE("make_integrated_address", on_make_integrated_address, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS)
|
||||
MAP_JON_RPC_WE("split_integrated_address", on_split_integrated_address, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS)
|
||||
|
||||
// supernet api
|
||||
MAP_JON_RPC_WE("maketelepod", on_maketelepod, wallet_rpc::COMMAND_RPC_MAKETELEPOD)
|
||||
MAP_JON_RPC_WE("clonetelepod", on_clonetelepod, wallet_rpc::COMMAND_RPC_CLONETELEPOD)
|
||||
|
|
@ -59,6 +61,7 @@ namespace tools
|
|||
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||
bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||
bool on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||
bool on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||
bool on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||
bool on_maketelepod(const wallet_rpc::COMMAND_RPC_MAKETELEPOD::request& req, wallet_rpc::COMMAND_RPC_MAKETELEPOD::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||
|
|
|
|||
|
|
@ -255,12 +255,14 @@ namespace wallet_rpc
|
|||
|
||||
struct payment_details
|
||||
{
|
||||
std::string payment_id;
|
||||
std::string tx_hash;
|
||||
uint64_t amount;
|
||||
uint64_t block_height;
|
||||
uint64_t unlock_time;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(payment_id)
|
||||
KV_SERIALIZE(tx_hash)
|
||||
KV_SERIALIZE(amount)
|
||||
KV_SERIALIZE(block_height)
|
||||
|
|
@ -289,6 +291,29 @@ namespace wallet_rpc
|
|||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_GET_BULK_PAYMENTS
|
||||
{
|
||||
struct request
|
||||
{
|
||||
std::vector<std::string> payment_ids;
|
||||
uint64_t min_block_height;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(payment_ids)
|
||||
KV_SERIALIZE(min_block_height)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::list<payment_details> payments;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(payments)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_MAKE_INTEGRATED_ADDRESS
|
||||
{
|
||||
struct request
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue