wallet: auto sync outputs and key images in cold signing files
When passing around unsigned and signed transactions, outputs and key images are passed along (outputs are passed along unsigned transactions from the hot wallet to the cold wallet, key images are passed along with signed transations from the cold wallet to the hot wallet), to allow more user friendly syncing between hot and cold wallets.
This commit is contained in:
parent
f8066116dd
commit
a0131c8be3
|
@ -1777,7 +1777,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
|
||||||
}
|
}
|
||||||
std::string verbose_string;
|
std::string verbose_string;
|
||||||
if (verbose)
|
if (verbose)
|
||||||
verbose_string = (boost::format("%68s%68s") % td.get_public_key() % td.m_key_image).str();
|
verbose_string = (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : std::string('?', 64))).str();
|
||||||
message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) <<
|
message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) <<
|
||||||
boost::format("%21s%8s%12s%8s%16u%68s%s") %
|
boost::format("%21s%8s%12s%8s%16u%68s%s") %
|
||||||
print_money(td.amount()) %
|
print_money(td.amount()) %
|
||||||
|
@ -2774,7 +2774,7 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx)
|
bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message)
|
||||||
{
|
{
|
||||||
// gather info to ask the user
|
// gather info to ask the user
|
||||||
uint64_t amount = 0, amount_to_dests = 0, change = 0;
|
uint64_t amount = 0, amount_to_dests = 0, change = 0;
|
||||||
|
@ -2847,19 +2847,25 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
|
||||||
change_string += tr("no change");
|
change_string += tr("no change");
|
||||||
|
|
||||||
uint64_t fee = amount - amount_to_dests;
|
uint64_t fee = amount - amount_to_dests;
|
||||||
std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. Is this okay? (Y/Yes/N/No)")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_mixin).str();
|
std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %sIs this okay? (Y/Yes/N/No)")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_mixin % extra_message).str();
|
||||||
std::string accepted = command_line::input_line(prompt_str);
|
std::string accepted = command_line::input_line(prompt_str);
|
||||||
return is_it_true(accepted);
|
return is_it_true(accepted);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
|
bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
|
||||||
{
|
{
|
||||||
return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];});
|
std::string extra_message;
|
||||||
|
if (!txs.transfers.empty())
|
||||||
|
extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.size()).str();
|
||||||
|
return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::accept_loaded_tx(const tools::wallet2::signed_tx_set &txs)
|
bool simple_wallet::accept_loaded_tx(const tools::wallet2::signed_tx_set &txs)
|
||||||
{
|
{
|
||||||
return accept_loaded_tx([&txs](){return txs.ptx.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.ptx[n].construction_data;});
|
std::string extra_message;
|
||||||
|
if (!txs.key_images.empty())
|
||||||
|
extra_message = (boost::format("%u key images to import. ") % (unsigned)txs.key_images.size()).str();
|
||||||
|
return accept_loaded_tx([&txs](){return txs.ptx.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.ptx[n].construction_data;}, extra_message);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
|
bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
|
||||||
|
@ -2870,9 +2876,10 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<tools::wallet2::pending_tx> ptx;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool r = m_wallet->sign_tx("unsigned_monero_tx", "signed_monero_tx", [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); });
|
bool r = m_wallet->sign_tx("unsigned_monero_tx", "signed_monero_tx", ptx, [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); });
|
||||||
if (!r)
|
if (!r)
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("Failed to sign transaction");
|
fail_msg_writer() << tr("Failed to sign transaction");
|
||||||
|
@ -2885,7 +2892,14 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
success_msg_writer(true) << tr("Transaction successfully signed to file: ") << "signed_monero_tx";
|
std::string txids_as_text;
|
||||||
|
for (const auto &t: ptx)
|
||||||
|
{
|
||||||
|
if (!txids_as_text.empty())
|
||||||
|
txids_as_text += (", ");
|
||||||
|
txids_as_text += epee::string_tools::pod_to_hex(get_transaction_hash(t.tx));
|
||||||
|
}
|
||||||
|
success_msg_writer(true) << tr("Transaction successfully signed to file ") << "signed_monero_tx" << ", txid " << txids_as_text;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -155,7 +155,7 @@ namespace cryptonote
|
||||||
uint64_t get_daemon_blockchain_height(std::string& err);
|
uint64_t get_daemon_blockchain_height(std::string& err);
|
||||||
bool try_connect_to_daemon(bool silent = false);
|
bool try_connect_to_daemon(bool silent = false);
|
||||||
bool ask_wallet_create_if_needed();
|
bool ask_wallet_create_if_needed();
|
||||||
bool accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx);
|
bool accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message = std::string());
|
||||||
bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs);
|
bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs);
|
||||||
bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs);
|
bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs);
|
||||||
bool get_address_from_str(const std::string &str, cryptonote::account_public_address &address, bool &has_payment_id, crypto::hash8 &payment_id);
|
bool get_address_from_str(const std::string &str, cryptonote::account_public_address &address, bool &has_payment_id, crypto::hash8 &payment_id);
|
||||||
|
|
|
@ -2953,6 +2953,7 @@ bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::stri
|
||||||
unsigned_tx_set txs;
|
unsigned_tx_set txs;
|
||||||
for (auto &tx: ptx_vector)
|
for (auto &tx: ptx_vector)
|
||||||
txs.txes.push_back(tx.construction_data);
|
txs.txes.push_back(tx.construction_data);
|
||||||
|
txs.transfers = m_transfers;
|
||||||
std::string s = obj_to_json_str(txs);
|
std::string s = obj_to_json_str(txs);
|
||||||
if (s.empty())
|
if (s.empty())
|
||||||
return false;
|
return false;
|
||||||
|
@ -2963,7 +2964,7 @@ bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::stri
|
||||||
return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + s);
|
return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + s);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::function<bool(const unsigned_tx_set&)> accept_func)
|
bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, std::function<bool(const unsigned_tx_set&)> accept_func)
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
boost::system::error_code errcode;
|
boost::system::error_code errcode;
|
||||||
|
@ -2998,6 +2999,8 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import_outputs(exported_txs.transfers);
|
||||||
|
|
||||||
// sign the transactions
|
// sign the transactions
|
||||||
signed_tx_set signed_txes;
|
signed_tx_set signed_txes;
|
||||||
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
|
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
|
||||||
|
@ -3043,6 +3046,17 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
|
||||||
ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet
|
ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet
|
||||||
ptx.dests = sd.splitted_dsts;
|
ptx.dests = sd.splitted_dsts;
|
||||||
ptx.construction_data = sd;
|
ptx.construction_data = sd;
|
||||||
|
|
||||||
|
txs.push_back(ptx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add key images
|
||||||
|
signed_txes.key_images.resize(m_transfers.size());
|
||||||
|
for (size_t i = 0; i < m_transfers.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!m_transfers[i].m_key_image_known)
|
||||||
|
LOG_PRINT_L0("WARNING: key image not known in signing wallet at index " << i);
|
||||||
|
signed_txes.key_images[i] = m_transfers[i].m_key_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = obj_to_json_str(signed_txes);
|
s = obj_to_json_str(signed_txes);
|
||||||
|
@ -3092,6 +3106,23 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// import key images
|
||||||
|
if (signed_txs.key_images.size() > m_transfers.size())
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("More key images returned that we know outputs for");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < signed_txs.key_images.size(); ++i)
|
||||||
|
{
|
||||||
|
transfer_details &td = m_transfers[i];
|
||||||
|
if (td.m_key_image_known && td.m_key_image != signed_txs.key_images[i])
|
||||||
|
LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one");
|
||||||
|
td.m_key_image = signed_txs.key_images[i];
|
||||||
|
m_key_images[m_transfers[i].m_key_image] = i;
|
||||||
|
td.m_key_image_known = true;
|
||||||
|
m_pub_keys[m_transfers[i].get_public_key()] = i;
|
||||||
|
}
|
||||||
|
|
||||||
ptx = signed_txs.ptx;
|
ptx = signed_txs.ptx;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -133,6 +133,21 @@ namespace tools
|
||||||
bool is_rct() const { return m_rct; }
|
bool is_rct() const { return m_rct; }
|
||||||
uint64_t amount() const { return m_amount; }
|
uint64_t amount() const { return m_amount; }
|
||||||
const crypto::public_key &get_public_key() const { return boost::get<const cryptonote::txout_to_key>(m_tx.vout[m_internal_output_index].target).key; }
|
const crypto::public_key &get_public_key() const { return boost::get<const cryptonote::txout_to_key>(m_tx.vout[m_internal_output_index].target).key; }
|
||||||
|
|
||||||
|
BEGIN_SERIALIZE_OBJECT()
|
||||||
|
FIELD(m_block_height)
|
||||||
|
FIELD(m_tx)
|
||||||
|
FIELD(m_txid)
|
||||||
|
FIELD(m_internal_output_index)
|
||||||
|
FIELD(m_global_output_index)
|
||||||
|
FIELD(m_spent)
|
||||||
|
FIELD(m_spent_height)
|
||||||
|
FIELD(m_key_image)
|
||||||
|
FIELD(m_mask)
|
||||||
|
FIELD(m_amount)
|
||||||
|
FIELD(m_rct)
|
||||||
|
FIELD(m_key_image_known)
|
||||||
|
END_SERIALIZE()
|
||||||
};
|
};
|
||||||
|
|
||||||
struct payment_details
|
struct payment_details
|
||||||
|
@ -226,16 +241,20 @@ namespace tools
|
||||||
struct unsigned_tx_set
|
struct unsigned_tx_set
|
||||||
{
|
{
|
||||||
std::vector<tx_construction_data> txes;
|
std::vector<tx_construction_data> txes;
|
||||||
|
wallet2::transfer_container transfers;
|
||||||
BEGIN_SERIALIZE_OBJECT()
|
BEGIN_SERIALIZE_OBJECT()
|
||||||
FIELD(txes)
|
FIELD(txes)
|
||||||
|
FIELD(transfers)
|
||||||
END_SERIALIZE()
|
END_SERIALIZE()
|
||||||
};
|
};
|
||||||
|
|
||||||
struct signed_tx_set
|
struct signed_tx_set
|
||||||
{
|
{
|
||||||
std::vector<pending_tx> ptx;
|
std::vector<pending_tx> ptx;
|
||||||
|
std::vector<crypto::key_image> key_images;
|
||||||
BEGIN_SERIALIZE_OBJECT()
|
BEGIN_SERIALIZE_OBJECT()
|
||||||
FIELD(ptx)
|
FIELD(ptx)
|
||||||
|
FIELD(key_images)
|
||||||
END_SERIALIZE()
|
END_SERIALIZE()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -377,7 +396,7 @@ namespace tools
|
||||||
void commit_tx(pending_tx& ptx_vector);
|
void commit_tx(pending_tx& ptx_vector);
|
||||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
||||||
bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename);
|
bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename);
|
||||||
bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::function<bool(const unsigned_tx_set&)> accept_func = NULL);
|
bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector<wallet2::pending_tx> &ptx, std::function<bool(const unsigned_tx_set&)> accept_func = NULL);
|
||||||
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
||||||
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
|
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
|
||||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
|
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
|
||||||
|
|
Loading…
Reference in New Issue