1
0
Fork 0
forked from lthn/blockchain

minor improvements for wallet/simplewallet (logs, comments, error handling)

This commit is contained in:
sowle 2025-07-03 16:13:44 +02:00
parent d5890e3c7f
commit dfbda0a77f
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
5 changed files with 76 additions and 66 deletions

View file

@ -294,6 +294,7 @@ namespace crypto {
}
void crypto_ops::generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) {
// image = sec * 8 * ge_fromfe_frombytes_vartime(cn_fast_hash(pub)) = sec * Hp( pub )
ge_p3 point;
ge_p2 point2;
crypto_assert(sc_check(&sec) == 0);

View file

@ -736,14 +736,15 @@ namespace currency
//------------------------------------------------------------------
bool derive_ephemeral_key_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral)
{
// TODO: re-implement this to avoid double Hs calculation -- sowle
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
bool r = crypto::generate_key_derivation(tx_public_key, ack.view_secret_key, recv_derivation);
bool r = crypto::generate_key_derivation(tx_public_key, ack.view_secret_key, recv_derivation); // recv_derivation = 8 * ack.view_secret_key * tx_public_key
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.view_secret_key << ")");
r = crypto::derive_public_key(recv_derivation, real_output_index, ack.account_address.spend_public_key, in_ephemeral.pub);
r = crypto::derive_public_key(recv_derivation, real_output_index, ack.account_address.spend_public_key, in_ephemeral.pub); // = Hs(recv_derivation, real_output_index) * G + spend_public_key
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.account_address.spend_public_key << ")");
crypto::derive_secret_key(recv_derivation, real_output_index, ack.spend_secret_key, in_ephemeral.sec);
crypto::derive_secret_key(recv_derivation, real_output_index, ack.spend_secret_key, in_ephemeral.sec); // = Hs(recv_derivation, real_output_index) + spend_secret_key
return true;
}
//---------------------------------------------------------------
@ -764,6 +765,8 @@ namespace currency
//---------------------------------------------------------------
bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki)
{
// h = Hs(8 * ack.view_secret_key * tx_public_key, real_output_index)
// ki = sec * Hp( pub ) = (h + spend_secret_key) * Hp( h * G + spend_public_key )
bool r = derive_ephemeral_key_helper(ack, tx_public_key, real_output_index, in_ephemeral);
CHECK_AND_ASSERT_MES(r, false, "Failed to call derive_ephemeral_key_helper(...)");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2024 Zano Project
// Copyright (c) 2014-2025 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -96,22 +96,18 @@ namespace ph = boost::placeholders;
} \
catch (const tools::error::transfer_error& e) \
{ \
LOG_ERROR("unknown transfer error: " << e.to_string()); \
fail_msg_writer() << "unknown transfer error: " << e.what(); \
fail_msg_writer() << "(transfer) " << e.what(); \
} \
catch (const tools::error::wallet_internal_error& e) \
{ \
LOG_ERROR("internal error: " << e.to_string()); \
fail_msg_writer() << "internal error: " << e.what(); \
fail_msg_writer() << "(internal) " << e.what(); \
} \
catch (const std::exception& e) \
{ \
LOG_ERROR("unexpected error: " << e.what()); \
fail_msg_writer() << "unexpected error: " << e.what(); \
fail_msg_writer() << "(unexpected) " << e.what(); \
} \
catch (...) \
{ \
LOG_ERROR("Unknown error"); \
fail_msg_writer() << "unknown error"; \
} \
@ -521,7 +517,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
else
{
bool r = open_wallet(m_wallet_file, pwd_container.password());
CHECK_AND_ASSERT_MES(r, false, "could not open account");
CHECK_AND_ASSERT_MES(r, false, "wallet could not be opened");
was_open = true;
}
process_wallet_command_line_params(vm, *m_wallet, false);
@ -714,6 +710,10 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
if (!m_voting_config_file.empty())
m_wallet->set_votes_config_path(m_voting_config_file);
auto print_wallet_opened_msg = [&](){
message_writer(epee::log_space::console_color_white, true) << "Opened" << (m_wallet->is_auditable() ? " auditable" : "") << (m_wallet->is_watch_only() ? " watch-only" : "") << " wallet: " << m_wallet->get_account().get_public_address_str();
};
while (true)
{
@ -728,7 +728,7 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
}
catch (const tools::error::wallet_load_notice_wallet_restored& /*e*/)
{
message_writer(epee::log_space::console_color_white, true) << "Opened wallet: " << m_wallet->get_account().get_public_address_str();
print_wallet_opened_msg();
message_writer(epee::log_space::console_color_red, true) << "NOTICE: Wallet file was damaged and restored.";
break;
}
@ -1797,6 +1797,12 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
//----------------------------------------------------------------------------------------------------
bool simple_wallet::show_seed(const std::vector<std::string> &args)
{
if (m_wallet->is_watch_only())
{
fail_msg_writer() << "watch-only wallet doesn't have the full set of keys, hence no seed phrase can be generated";
return false;
}
CONFIRM_WITH_PASSWORD();
success_msg_writer() << "Please enter a password to secure this seed. Securing your seed is HIGHLY recommended. Leave password blank to stay unsecured.";
success_msg_writer(true) << "Remember, restoring a wallet from Secured Seed can only be done if you know its password.";
@ -1984,12 +1990,10 @@ bool simple_wallet::save_watch_only(const std::vector<std::string> &args)
}
catch (const std::exception& e)
{
LOG_ERROR("unexpected error: " << e.what());
fail_msg_writer() << "unexpected error: " << e.what();
fail_msg_writer() << e.what();
}
catch (...)
{
LOG_ERROR("Unknown error");
fail_msg_writer() << "unknown error";
}
return true;
@ -2053,12 +2057,10 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args)
}
catch (const std::exception& e)
{
LOG_ERROR("unexpected error: " << e.what());
fail_msg_writer() << "unexpected error: " << e.what();
fail_msg_writer() << e.what();
}
catch (...)
{
LOG_ERROR("Unknown error");
fail_msg_writer() << "unknown error";
}
return true;
@ -2079,12 +2081,10 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args)
}
catch (const std::exception& e)
{
LOG_ERROR("unexpected error: " << e.what());
fail_msg_writer() << "unexpected error: " << e.what();
fail_msg_writer() << e.what();
}
catch (...)
{
LOG_ERROR("Unknown error");
fail_msg_writer() << "unknown error";
}
return true;
@ -3270,7 +3270,7 @@ int main(int argc, char* argv[])
}
else if (command_line::get_arg(vm, command_line::arg_version))
{
success_msg_writer() << CURRENCY_NAME << " wallet v" << PROJECT_VERSION_LONG;
success_msg_writer() << CURRENCY_NAME << " simplewallet v" << PROJECT_VERSION_LONG;
exit_requested = true;
return true;
}
@ -3296,6 +3296,7 @@ int main(int argc, char* argv[])
std::string log_dir;
log_dir = log_file_path.has_parent_path() ? log_file_path.parent_path().string() : log_space::log_singletone::get_default_log_folder();
log_space::log_singletone::add_logger(LOGGER_FILE, log_file_path.filename().string().c_str(), log_dir.c_str(), LOG_LEVEL_4);
LOG_PRINT_L0(ENDL << ENDL);
message_writer(epee::log_space::console_color_white, true, std::string(), LOG_LEVEL_0) << CURRENCY_NAME << " simplewallet v" << PROJECT_VERSION_LONG;
if (command_line::has_arg(vm, command_line::arg_log_level))

View file

@ -757,7 +757,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
{
// normal wallet, calculate and store key images for own outs
currency::keypair in_ephemeral = AUTO_VAL_INIT(in_ephemeral);
currency::generate_key_image_helper(m_account.get_keys(), ptc.tx_pub_key, o, in_ephemeral, ki);
currency::generate_key_image_helper(m_account.get_keys(), ptc.tx_pub_key, /* output index */ o, in_ephemeral, ki);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(in_ephemeral.pub == out_key, "key_image generated ephemeral public key that does not match with output_key");
}
@ -4322,29 +4322,25 @@ void wallet2::sign_transfer(const std::string& tx_sources_blob, std::string& sig
// 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().view_secret_key);
// decrypt the blob
std::string decrypted_src_blob = crypto::chacha_crypt(tx_sources_blob, m_account.get_keys().view_secret_key);
// deserialize args
currency::finalized_tx ft = AUTO_VAL_INIT(ft);
currency::finalized_tx ft{};
bool r = t_unserializable_object_from_blob(ft.ftp, 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(ft.ftp.spend_pub_key == m_account.get_keys().account_address.spend_public_key, error::wallet_common_error, "The was created in a different wallet, keys missmatch");
finalize_transaction(ft.ftp, ft.tx, ft.one_time_key, false);
finalize_transaction(ft.ftp, ft, false, true);
WLT_LOG_L0("sign_transfer: tx " << ft.tx_id << " has been successfully signed");
// calculate key images for each change output
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(
crypto::generate_key_derivation(
m_account.get_keys().account_address.view_public_key,
ft.one_time_key,
derivation),
"internal error: sign_transfer: failed to generate key derivation("
<< m_account.get_keys().account_address.view_public_key
<< ", view secret key: " << ft.one_time_key << ")");
crypto::key_derivation derivation{};
r = crypto::generate_key_derivation(m_account.get_keys().account_address.view_public_key, ft.one_time_key, derivation);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "sign_transfer: generate_key_derivation failed, tx: " << ft.tx_id);
for (size_t i = 0; i < ft.tx.vout.size(); ++i)
{
@ -4354,7 +4350,7 @@ void wallet2::sign_transfer(const std::string& tx_sources_blob, std::string& sig
crypto::public_key ephemeral_pub{};
if (!crypto::derive_public_key(derivation, i, m_account.get_keys().account_address.spend_public_key, ephemeral_pub))
{
WLT_LOG_ERROR("derive_public_key failed for tx " << get_transaction_hash(ft.tx) << ", out # " << i);
WLT_LOG_ERROR("derive_public_key failed for tx " << ft.tx_id << ", out # " << i);
}
if (out_pk == ephemeral_pub)
@ -4367,6 +4363,7 @@ void wallet2::sign_transfer(const std::string& tx_sources_blob, std::string& sig
crypto::generate_key_image(ephemeral_pub, ephemeral_sec, ki);
ft.outs_key_images.push_back(make_serializable_pair(static_cast<uint64_t>(i), ki));
WLT_LOG_L1("sign_transfer: tx " << ft.tx_id << ", out index: " << i << ", ki: " << ki);
}
}
@ -4495,6 +4492,9 @@ bool wallet2::attach_asset_descriptor(const wallet_public::COMMAND_ATTACH_ASSET_
//----------------------------------------------------------------------------------------------------
void wallet2::submit_transfer(const std::string& signed_tx_blob, currency::transaction& tx)
{
// assumed to be called from watch-only wallet
THROW_IF_FALSE_WALLET_EX(m_watch_only, error::wallet_common_error, "submit_transfer should be called in watch-only wallet only");
// decrypt sources
std::string decrypted_src_blob = crypto::chacha_crypt(signed_tx_blob, m_account.get_keys().view_secret_key);
@ -4505,9 +4505,36 @@ void wallet2::submit_transfer(const std::string& signed_tx_blob, currency::trans
tx = ft.tx;
crypto::hash tx_hash = get_transaction_hash(tx);
// foolproof
// foolproof check
THROW_IF_FALSE_WALLET_CMN_ERR_EX(ft.ftp.spend_pub_key == m_account.get_keys().account_address.spend_public_key, "The given tx was created in a different wallet, keys missmatch, tx hash: " << tx_hash);
// prepare and check data for watch-only outkey2ki before sending the tx
std::vector<std::pair<crypto::public_key, crypto::key_image>> pk_ki_to_be_added;
std::vector<std::pair<uint64_t, crypto::key_image>> tri_ki_to_be_added;
if (m_watch_only)
{
for (auto& p : ft.outs_key_images)
{
THROW_IF_FALSE_WALLET_INT_ERR_EX(p.first < tx.vout.size(), "outs_key_images has invalid out index: " << p.first << ", tx.vout.size() = " << tx.vout.size());
std::list<htlc_info> stub{};
const crypto::public_key& pk = out_get_pub_key(tx.vout[p.first], stub);
pk_ki_to_be_added.push_back(std::make_pair(pk, p.second));
}
THROW_IF_FALSE_WALLET_INT_ERR_EX(tx.vin.size() == ft.ftp.sources.size(), "tx.vin and ft.ftp.sources sizes missmatch");
for (size_t i = 0; i < tx.vin.size(); ++i)
{
const crypto::key_image& ki = get_key_image_from_txin_v(tx.vin[i]);
const auto& src = ft.ftp.sources[i];
THROW_IF_FALSE_WALLET_INT_ERR_EX(src.real_output < src.outputs.size(), "src.real_output is out of bounds: " << src.real_output);
const crypto::public_key& out_key = src.outputs[src.real_output].stealth_address;
tri_ki_to_be_added.push_back(std::make_pair(src.transfer_index, ki));
pk_ki_to_be_added.push_back(std::make_pair(out_key, ki));
}
}
// SEND the transaction
try
{
send_transaction_to_network(tx);
@ -4523,38 +4550,16 @@ void wallet2::submit_transfer(const std::string& signed_tx_blob, currency::trans
add_sent_tx_detailed_info(tx, ft.ftp.attachments, ft.ftp.prepared_destinations, ft.ftp.selected_transfers);
m_tx_keys.insert(std::make_pair(tx_hash, ft.one_time_key));
// populate and store key images from own outputs, because otherwise a watch-only wallet cannot calculate it
if (m_watch_only)
{
std::vector<std::pair<crypto::public_key, crypto::key_image>> pk_ki_to_be_added;
std::vector<std::pair<uint64_t, crypto::key_image>> tri_ki_to_be_added;
for (auto& p : ft.outs_key_images)
{
THROW_IF_FALSE_WALLET_INT_ERR_EX(p.first < tx.vout.size(), "outs_key_images has invalid out index: " << p.first << ", tx.vout.size() = " << tx.vout.size());
std::list<htlc_info> stub{};
const crypto::public_key& pk = out_get_pub_key(tx.vout[p.first], stub);
pk_ki_to_be_added.push_back(std::make_pair(pk, p.second));
}
THROW_IF_FALSE_WALLET_INT_ERR_EX(tx.vin.size() == ft.ftp.sources.size(), "tx.vin and ft.ftp.sources sizes missmatch");
for (size_t i = 0; i < tx.vin.size(); ++i)
{
const crypto::key_image& ki = get_key_image_from_txin_v(tx.vin[i]);
const auto& src = ft.ftp.sources[i];
THROW_IF_FALSE_WALLET_INT_ERR_EX(src.real_output < src.outputs.size(), "src.real_output is out of bounds: " << src.real_output);
const crypto::public_key& out_key = src.outputs[src.real_output].stealth_address;
tri_ki_to_be_added.push_back(std::make_pair(src.transfer_index, ki));
pk_ki_to_be_added.push_back(std::make_pair(out_key, ki));
}
for (auto& p : pk_ki_to_be_added)
{
auto it = m_pending_key_images.find(p.first);
if (it != m_pending_key_images.end())
{
LOG_PRINT_YELLOW("warning: for tx " << tx_hash << " out pub key " << p.first << " already exist in m_pending_key_images, ki: " << it->second << ", proposed new ki: " << p.second, LOG_LEVEL_0);
if (it->second != p.second)
LOG_PRINT_YELLOW("warning: for tx " << tx_hash << " out pub key " << p.first << " already exist in m_pending_key_images, ki: " << it->second << ", proposed new ki: " << p.second, LOG_LEVEL_0);
}
else
{

View file

@ -71,10 +71,10 @@ namespace tools
std::string to_string() const
{
std::ostringstream ss;
ss << m_loc << '[' << boost::replace_all_copy(std::string(typeid(*this).name()), "struct ", "");
ss << '[' << boost::replace_all_copy(std::string(typeid(*this).name()), "struct ", "");
if (!m_error_code.empty())
ss << "[" << m_error_code << "]";
ss << "] " << Base::what();
ss << "] " << m_loc << ENDL << " " << Base::what();
return ss.str();
}