1
0
Fork 0
forked from lthn/blockchain

wallet: refactoring for cold-signing process + offline mode + watch-only mode (work in progress)

This commit is contained in:
sowle 2019-04-03 12:48:09 +03:00
parent 3543ae0520
commit d2d1bf5d7a
16 changed files with 1073 additions and 567 deletions

View file

@ -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 = '?')

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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