aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/wallet.cpp32
-rw-r--r--src/wallet/wallet2.cpp148
-rw-r--r--src/wallet/wallet2.h18
3 files changed, 176 insertions, 22 deletions
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index c0974f880..7afc1f449 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -379,7 +379,32 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas
const cryptonote::account_public_address address = m_wallet->get_account().get_keys().m_account_address;
try {
+ // Generate view only wallet
view_wallet->generate(path, password, address, viewkey);
+
+ // Export/Import outputs
+ auto outputs = m_wallet->export_outputs();
+ view_wallet->import_outputs(outputs);
+
+ // Copy scanned blockchain
+ auto bc = m_wallet->export_blockchain();
+ view_wallet->import_blockchain(bc);
+
+ // copy payments
+ auto payments = m_wallet->export_payments();
+ view_wallet->import_payments(payments);
+
+ // copy confirmed outgoing payments
+ std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> out_payments;
+ m_wallet->get_payments_out(out_payments, 0);
+ view_wallet->import_payments_out(out_payments);
+
+ // Export/Import key images
+ // We already know the spent status from the outputs we exported, thus no need to check them again
+ auto key_images = m_wallet->export_key_images();
+ uint64_t spent = 0;
+ uint64_t unspent = 0;
+ view_wallet->import_key_images(key_images,spent,unspent,false);
m_status = Status_Ok;
} catch (const std::exception &e) {
LOG_ERROR("Error creating view only wallet: " << e.what());
@@ -387,6 +412,8 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas
m_errorString = e.what();
return false;
}
+ // Store wallet
+ view_wallet->store();
return true;
}
@@ -862,6 +889,11 @@ bool WalletImpl::exportKeyImages(const string &filename)
bool WalletImpl::importKeyImages(const string &filename)
{
+ if (!trustedDaemon()) {
+ m_status = Status_Error;
+ m_errorString = tr("Key images can only be imported with a trusted daemon");
+ return false;
+ }
try
{
uint64_t spent = 0, unspent = 0;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 0b6b6f3f7..4feef21ed 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1963,6 +1963,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p
value2.SetInt(m_merge_destinations ? 1 :0);
json.AddMember("merge_destinations", value2, json.GetAllocator());
+ value2.SetInt(m_confirm_backlog ? 1 :0);
+ json.AddMember("confirm_backlog", value2, json.GetAllocator());
+
value2.SetInt(m_testnet ? 1 :0);
json.AddMember("testnet", value2, json.GetAllocator());
@@ -2037,6 +2040,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
m_min_output_count = 0;
m_min_output_value = 0;
m_merge_destinations = false;
+ m_confirm_backlog = true;
}
else
{
@@ -2107,6 +2111,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
m_min_output_value = field_min_output_value;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false);
m_merge_destinations = field_merge_destinations;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_backlog, int, Int, false, true);
+ m_confirm_backlog = field_confirm_backlog;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, testnet, int, Int, false, m_testnet);
// Wallet is being opened with testnet flag, but is saved as a mainnet wallet
THROW_WALLET_EXCEPTION_IF(m_testnet && !field_testnet, error::wallet_internal_error, "Mainnet wallet can not be opened as testnet wallet");
@@ -5327,7 +5333,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent)
+uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent)
{
COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
@@ -5376,34 +5382,88 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
m_transfers[n].m_key_image_known = true;
}
- m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
- THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
- "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
- std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
-
+ if(check_spent)
+ {
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
+ "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
+ std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
+ for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
+ {
+ transfer_details &td = m_transfers[n];
+ td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
+ }
+ }
spent = 0;
unspent = 0;
- for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
+ for(size_t i = 0; i < m_transfers.size(); ++i)
{
- transfer_details &td = m_transfers[n];
+ transfer_details &td = m_transfers[i];
uint64_t amount = td.amount();
- td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
if (td.m_spent)
spent += amount;
else
unspent += amount;
- LOG_PRINT_L2("Transfer " << n << ": " << print_money(amount) << " (" << td.m_global_output_index << "): "
- << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[n] << ")");
+ LOG_PRINT_L2("Transfer " << i << ": " << print_money(amount) << " (" << td.m_global_output_index << "): "
+ << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
}
- LOG_PRINT_L1("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
-
+ MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
return m_transfers[signed_key_images.size() - 1].m_block_height;
}
+wallet2::payment_container wallet2::export_payments() const
+{
+ payment_container payments;
+ for (auto const &p : m_payments)
+ {
+ payments.emplace(p);
+ }
+ return payments;
+}
+void wallet2::import_payments(const payment_container &payments)
+{
+ m_payments.clear();
+ for (auto const &p : payments)
+ {
+ m_payments.emplace(p);
+ }
+}
+void wallet2::import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments)
+{
+ m_confirmed_txs.clear();
+ for (auto const &p : confirmed_payments)
+ {
+ m_confirmed_txs.emplace(p);
+ }
+}
+
+std::vector<crypto::hash> wallet2::export_blockchain() const
+{
+ std::vector<crypto::hash> bc;
+ for (auto const &b : m_blockchain)
+ {
+ bc.push_back(b);
+ }
+ return bc;
+}
+
+void wallet2::import_blockchain(const std::vector<crypto::hash> &bc)
+{
+ m_blockchain.clear();
+ for (auto const &b : bc)
+ {
+ m_blockchain.push_back(b);
+ }
+ cryptonote::block genesis;
+ generate_genesis(genesis);
+ crypto::hash genesis_hash = get_block_hash(genesis);
+ check_genesis(genesis_hash);
+ m_local_bc_height = m_blockchain.size();
+}
//----------------------------------------------------------------------------------------------------
std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
{
@@ -5744,6 +5804,58 @@ bool wallet2::is_synced() const
return get_blockchain_current_height() >= height;
}
//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::estimate_backlog(uint64_t blob_size, uint64_t fee)
+{
+ THROW_WALLET_EXCEPTION_IF(blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
+
+ // get txpool backlog
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request> req = AUTO_VAL_INIT(req);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response, std::string> res = AUTO_VAL_INIT(res);
+ m_daemon_rpc_mutex.lock();
+ req.jsonrpc = "2.0";
+ req.id = epee::serialization::storage_entry(0);
+ req.method = "get_txpool_backlog";
+ bool r = net_utils::invoke_http_json("/json_rpc", req, res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "Failed to connect to daemon");
+ THROW_WALLET_EXCEPTION_IF(res.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
+ THROW_WALLET_EXCEPTION_IF(res.result.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
+
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_INFO::request> req_t = AUTO_VAL_INIT(req_t);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_INFO::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
+ m_daemon_rpc_mutex.lock();
+ req_t.jsonrpc = "2.0";
+ req_t.id = epee::serialization::storage_entry(0);
+ req_t.method = "get_info";
+ r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info");
+ THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info");
+ THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
+
+ double our_fee_byte = fee / (double)blob_size;
+ uint64_t priority_size = 0;
+ for (const auto &i: res.result.backlog)
+ {
+ if (i.blob_size == 0)
+ {
+ MWARNING("Got 0 sized blob from txpool, ignored");
+ continue;
+ }
+ double this_fee_byte = i.fee / (double)i.blob_size;
+ if (this_fee_byte < our_fee_byte)
+ continue;
+ priority_size += i.blob_size;
+ }
+
+ uint64_t full_reward_zone = resp_t.result.block_size_limit / 2;
+ uint64_t nblocks = (priority_size + full_reward_zone - 1) / full_reward_zone;
+ MDEBUG("estimate_backlog: priority_size " << priority_size << " for " << our_fee_byte << " (" << our_fee_byte << " piconero fee/byte), "
+ << nblocks << " blocks at block size " << full_reward_zone);
+ return nblocks;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::generate_genesis(cryptonote::block& b) {
if (m_testnet)
{
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index ba9abacf5..3d5033535 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -105,7 +105,7 @@ namespace tools
};
private:
- wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_is_initialized(false),m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
+ wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false),m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
public:
static const char* tr(const char* str);
@@ -131,7 +131,7 @@ namespace tools
static bool verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only);
- wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
+ wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
struct transfer_details
{
@@ -539,6 +539,8 @@ namespace tools
uint64_t get_min_output_value() const { return m_min_output_value; }
void merge_destinations(bool merge) { m_merge_destinations = merge; }
bool merge_destinations() const { return m_merge_destinations; }
+ bool confirm_backlog() const { return m_confirm_backlog; }
+ void confirm_backlog(bool always) { m_confirm_backlog = always; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const;
@@ -580,12 +582,17 @@ namespace tools
std::string sign(const std::string &data) const;
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const;
+ // Import/Export wallet data
std::vector<tools::wallet2::transfer_details> export_outputs() const;
size_t import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs);
-
+ payment_container export_payments() const;
+ void import_payments(const payment_container &payments);
+ void import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments);
+ std::vector<crypto::hash> export_blockchain() const;
+ void import_blockchain(const std::vector<crypto::hash> &bc);
bool export_key_images(const std::string filename);
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const;
- uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent);
+ uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
void update_pool_state(bool refreshed = false);
@@ -602,6 +609,8 @@ namespace tools
bool is_synced() const;
+ uint64_t estimate_backlog(uint64_t blob_size, uint64_t fee);
+
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -700,6 +709,7 @@ namespace tools
uint32_t m_min_output_count;
uint64_t m_min_output_value;
bool m_merge_destinations;
+ bool m_confirm_backlog;
bool m_is_initialized;
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];