2019-04-12 06:36:43 +02:00
|
|
|
// Copyright (c) 2014-2019, The Monero Project
|
2018-04-10 06:49:20 +02:00
|
|
|
// Copyright (c) 2018, The Loki Project
|
2016-04-20 12:01:00 +02:00
|
|
|
//
|
2016-02-15 19:04:00 +01:00
|
|
|
// All rights reserved.
|
2016-04-20 12:01:00 +02:00
|
|
|
//
|
2016-02-15 19:04:00 +01:00
|
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
|
|
// permitted provided that the following conditions are met:
|
2016-04-20 12:01:00 +02:00
|
|
|
//
|
2016-02-15 19:04:00 +01:00
|
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
|
|
// conditions and the following disclaimer.
|
2016-04-20 12:01:00 +02:00
|
|
|
//
|
2016-02-15 19:04:00 +01:00
|
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
|
|
// materials provided with the distribution.
|
2016-04-20 12:01:00 +02:00
|
|
|
//
|
2016-02-15 19:04:00 +01:00
|
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
|
|
// used to endorse or promote products derived from this software without specific
|
|
|
|
// prior written permission.
|
2016-04-20 12:01:00 +02:00
|
|
|
//
|
2016-02-15 19:04:00 +01:00
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2016-04-20 12:01:00 +02:00
|
|
|
//
|
2016-02-15 19:04:00 +01:00
|
|
|
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
|
|
|
|
2018-12-03 09:36:45 +01:00
|
|
|
#ifdef _WIN32
|
2021-01-04 01:09:45 +01:00
|
|
|
#define __STDC_FORMAT_MACROS // NOTE(oxen): Explicitly define the PRIu64 macro on Mingw
|
2018-12-03 09:36:45 +01:00
|
|
|
#endif
|
2016-04-03 13:34:38 +02:00
|
|
|
|
2016-04-20 12:01:00 +02:00
|
|
|
#include "wallet.h"
|
|
|
|
#include "pending_transaction.h"
|
2017-01-08 13:17:09 +01:00
|
|
|
#include "unsigned_transaction.h"
|
2020-11-16 09:20:24 +01:00
|
|
|
#include "stake_unlock_result.h"
|
2016-04-22 12:21:08 +02:00
|
|
|
#include "transaction_history.h"
|
2016-12-12 00:42:46 +01:00
|
|
|
#include "address_book.h"
|
2017-02-19 03:42:10 +01:00
|
|
|
#include "subaddress.h"
|
|
|
|
#include "subaddress_account.h"
|
2016-04-20 12:01:00 +02:00
|
|
|
#include "common_defines.h"
|
2017-08-09 12:38:29 +02:00
|
|
|
#include "common/util.h"
|
2020-10-22 19:55:33 +02:00
|
|
|
#include "common/fs.h"
|
2016-04-03 13:34:38 +02:00
|
|
|
|
2016-04-20 12:01:00 +02:00
|
|
|
#include "mnemonics/electrum-words.h"
|
2018-02-25 14:04:36 +01:00
|
|
|
#include "mnemonics/english.h"
|
2016-04-03 13:34:38 +02:00
|
|
|
#include <boost/format.hpp>
|
2016-06-16 15:42:33 +02:00
|
|
|
#include <sstream>
|
2016-12-12 00:42:46 +01:00
|
|
|
#include <unordered_map>
|
2020-01-15 05:17:26 +01:00
|
|
|
#include <thread>
|
2016-04-03 13:34:38 +02:00
|
|
|
|
2016-04-20 12:01:00 +02:00
|
|
|
using namespace cryptonote;
|
2016-02-20 17:04:56 +01:00
|
|
|
|
2021-01-04 04:19:42 +01:00
|
|
|
#undef OXEN_DEFAULT_LOG_CATEGORY
|
|
|
|
#define OXEN_DEFAULT_LOG_CATEGORY "WalletAPI"
|
2017-02-05 20:12:01 +01:00
|
|
|
|
2020-10-22 23:37:54 +02:00
|
|
|
namespace Wallet {
|
2016-02-15 19:04:00 +01:00
|
|
|
|
|
|
|
namespace {
|
2016-09-26 21:50:10 +02:00
|
|
|
static const int DEFAULT_REFRESH_INTERVAL_MILLIS = 1000 * 10;
|
2016-09-27 23:35:10 +02:00
|
|
|
// limit maximum refresh interval as one minute
|
|
|
|
static const int MAX_REFRESH_INTERVAL_MILLIS = 1000 * 60 * 1;
|
2017-01-08 13:47:13 +01:00
|
|
|
// Default refresh interval when connected to remote node
|
|
|
|
static const int DEFAULT_REMOTE_NODE_REFRESH_INTERVAL_MILLIS = 1000 * 10;
|
2017-01-14 14:41:56 +01:00
|
|
|
// Connection timeout 30 sec
|
|
|
|
static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 30;
|
2018-03-04 14:32:35 +01:00
|
|
|
|
2020-10-22 19:55:33 +02:00
|
|
|
fs::path get_default_ringdb_path(cryptonote::network_type nettype)
|
2018-03-04 14:32:35 +01:00
|
|
|
{
|
2020-10-22 19:55:33 +02:00
|
|
|
auto dir = tools::get_default_data_dir();
|
2021-01-04 01:09:45 +01:00
|
|
|
// remove .oxen, replace with .shared-ringdb
|
2020-10-22 19:55:33 +02:00
|
|
|
dir.replace_filename(".shared-ringdb");
|
2018-03-27 05:08:28 +02:00
|
|
|
if (nettype == cryptonote::TESTNET)
|
|
|
|
dir /= "testnet";
|
2020-08-11 23:41:54 +02:00
|
|
|
else if (nettype == cryptonote::DEVNET)
|
|
|
|
dir /= "devnet";
|
2020-10-22 19:55:33 +02:00
|
|
|
return dir;
|
2018-03-04 14:32:35 +01:00
|
|
|
}
|
2018-03-21 16:57:15 +01:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
void checkMultisigWalletReady(LockedWallet& wallet) {
|
|
|
|
if (!wallet.wallet)
|
2020-10-22 23:43:29 +02:00
|
|
|
throw std::runtime_error("Wallet is not initialized yet");
|
2018-03-21 16:57:15 +01:00
|
|
|
|
|
|
|
bool ready;
|
2020-10-22 23:43:29 +02:00
|
|
|
if (!wallet->multisig(&ready))
|
|
|
|
throw std::runtime_error("Wallet is not multisig");
|
2018-03-21 16:57:15 +01:00
|
|
|
|
2020-10-22 23:43:29 +02:00
|
|
|
if (!ready)
|
|
|
|
throw std::runtime_error("Multisig wallet is not finalized yet");
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
void checkMultisigWalletNotReady(LockedWallet& wallet) {
|
|
|
|
if (!wallet.wallet)
|
2020-10-22 23:43:29 +02:00
|
|
|
throw std::runtime_error("Wallet is not initialized yet");
|
2018-03-21 16:57:15 +01:00
|
|
|
|
|
|
|
bool ready;
|
2020-10-22 23:43:29 +02:00
|
|
|
if (!wallet->multisig(&ready))
|
|
|
|
throw std::runtime_error("Wallet is not multisig");
|
2018-03-21 16:57:15 +01:00
|
|
|
|
2020-10-22 23:43:29 +02:00
|
|
|
if (ready)
|
|
|
|
throw std::runtime_error("Multisig wallet is already finalized");
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
2016-04-03 13:34:38 +02:00
|
|
|
}
|
2016-02-20 17:04:56 +01:00
|
|
|
|
2016-05-05 21:24:00 +02:00
|
|
|
struct Wallet2CallbackImpl : public tools::i_wallet2_callback
|
|
|
|
{
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-10-06 23:29:13 +02:00
|
|
|
Wallet2CallbackImpl(WalletImpl * wallet)
|
2016-05-16 12:11:44 +02:00
|
|
|
: m_listener(nullptr)
|
2016-10-06 23:29:13 +02:00
|
|
|
, m_wallet(wallet)
|
2016-05-16 12:11:44 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-05-05 21:24:00 +02:00
|
|
|
~Wallet2CallbackImpl()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-05-05 21:24:00 +02:00
|
|
|
void setListener(WalletListener * listener)
|
|
|
|
{
|
2016-05-13 15:25:31 +02:00
|
|
|
m_listener = listener;
|
2016-05-05 21:24:00 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-05-05 21:24:00 +02:00
|
|
|
WalletListener * getListener() const
|
|
|
|
{
|
|
|
|
return m_listener;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_new_block(uint64_t height, const cryptonote::block& block) override
|
2016-05-05 21:24:00 +02:00
|
|
|
{
|
2017-01-30 18:00:10 +01:00
|
|
|
// Don't flood the GUI with signals. On fast refresh - send signal every 1000th block
|
|
|
|
// get_refresh_from_block_height() returns the blockheight from when the wallet was
|
|
|
|
// created or the restore height specified when wallet was recovered
|
2021-06-23 03:08:12 +02:00
|
|
|
//
|
|
|
|
if(height >= m_wallet->m_wallet_ptr->get_refresh_from_block_height() || height % 1000 == 0) {
|
2017-01-30 18:00:10 +01:00
|
|
|
// LOG_PRINT_L3(__FUNCTION__ << ": new block. height: " << height);
|
|
|
|
if (m_listener) {
|
|
|
|
m_listener->newBlock(height);
|
|
|
|
}
|
2016-09-23 21:36:37 +02:00
|
|
|
}
|
2016-05-05 21:24:00 +02:00
|
|
|
}
|
2016-05-13 15:25:31 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time, bool blink) override
|
2016-05-05 21:24:00 +02:00
|
|
|
{
|
2020-10-23 22:32:28 +02:00
|
|
|
std::string tx_hash = tools::type_to_hex(txid);
|
2016-05-13 15:25:31 +02:00
|
|
|
|
2020-01-13 07:50:22 +01:00
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": money received." << (blink ? "blink: " : "height: ") << height
|
2016-05-13 15:25:31 +02:00
|
|
|
<< ", tx: " << tx_hash
|
2017-02-19 03:42:10 +01:00
|
|
|
<< ", amount: " << print_money(amount)
|
|
|
|
<< ", idx: " << subaddr_index);
|
2016-10-06 23:29:13 +02:00
|
|
|
// do not signal on received tx if wallet is not syncronized completely
|
|
|
|
if (m_listener && m_wallet->synchronized()) {
|
2016-05-13 15:25:31 +02:00
|
|
|
m_listener->moneyReceived(tx_hash, amount);
|
2016-07-10 16:17:23 +02:00
|
|
|
m_listener->updated();
|
2016-05-13 15:25:31 +02:00
|
|
|
}
|
2016-05-05 21:24:00 +02:00
|
|
|
}
|
2016-05-13 15:25:31 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) override
|
2017-01-12 18:23:23 +01:00
|
|
|
{
|
|
|
|
|
2020-10-23 22:32:28 +02:00
|
|
|
std::string tx_hash = tools::type_to_hex(txid);
|
2017-01-12 18:23:23 +01:00
|
|
|
|
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": unconfirmed money received. height: " << height
|
|
|
|
<< ", tx: " << tx_hash
|
2017-02-19 03:42:10 +01:00
|
|
|
<< ", amount: " << print_money(amount)
|
|
|
|
<< ", idx: " << subaddr_index);
|
2017-01-12 18:23:23 +01:00
|
|
|
// do not signal on received tx if wallet is not syncronized completely
|
|
|
|
if (m_listener && m_wallet->synchronized()) {
|
|
|
|
m_listener->unconfirmedMoneyReceived(tx_hash, amount);
|
|
|
|
m_listener->updated();
|
|
|
|
}
|
2016-05-05 21:24:00 +02:00
|
|
|
}
|
2016-05-13 15:25:31 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_money_spent(uint64_t height,
|
|
|
|
const crypto::hash &txid,
|
|
|
|
const cryptonote::transaction &in_tx,
|
|
|
|
uint64_t amount,
|
|
|
|
const cryptonote::transaction &spend_tx,
|
|
|
|
const cryptonote::subaddress_index &subaddr_index) override
|
2016-05-05 21:24:00 +02:00
|
|
|
{
|
|
|
|
// TODO;
|
2020-10-23 22:32:28 +02:00
|
|
|
std::string tx_hash = tools::type_to_hex(txid);
|
2016-07-14 11:47:01 +02:00
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": money spent. height: " << height
|
2016-05-13 15:25:31 +02:00
|
|
|
<< ", tx: " << tx_hash
|
2017-02-19 03:42:10 +01:00
|
|
|
<< ", amount: " << print_money(amount)
|
|
|
|
<< ", idx: " << subaddr_index);
|
2016-10-06 23:29:13 +02:00
|
|
|
// do not signal on sent tx if wallet is not syncronized completely
|
|
|
|
if (m_listener && m_wallet->synchronized()) {
|
2016-05-13 15:25:31 +02:00
|
|
|
m_listener->moneySpent(tx_hash, amount);
|
2016-07-10 16:17:23 +02:00
|
|
|
m_listener->updated();
|
2016-05-13 15:25:31 +02:00
|
|
|
}
|
2016-05-05 21:24:00 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) override
|
2016-05-05 21:24:00 +02:00
|
|
|
{
|
|
|
|
// TODO;
|
|
|
|
}
|
|
|
|
|
2017-08-05 17:01:50 +02:00
|
|
|
// Light wallet callbacks
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_lw_new_block(uint64_t height) override
|
2017-08-05 17:01:50 +02:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
|
|
|
m_listener->newBlock(height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) override
|
2017-08-05 17:01:50 +02:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
2020-10-23 22:32:28 +02:00
|
|
|
std::string tx_hash = tools::type_to_hex(txid);
|
2017-08-05 17:01:50 +02:00
|
|
|
m_listener->moneyReceived(tx_hash, amount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) override
|
2017-08-05 17:01:50 +02:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
2020-10-23 22:32:28 +02:00
|
|
|
std::string tx_hash = tools::type_to_hex(txid);
|
2017-08-05 17:01:50 +02:00
|
|
|
m_listener->unconfirmedMoneyReceived(tx_hash, amount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) override
|
2017-08-05 17:01:50 +02:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
2020-10-23 22:32:28 +02:00
|
|
|
std::string tx_hash = tools::type_to_hex(txid);
|
2017-08-05 17:01:50 +02:00
|
|
|
m_listener->moneySpent(tx_hash, amount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_device_button_request(uint64_t code) override
|
2019-02-23 15:28:18 +01:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
|
|
|
m_listener->onDeviceButtonRequest(code);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_device_button_pressed() override
|
2019-04-05 21:47:24 +02:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
|
|
|
m_listener->onDeviceButtonPressed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-06-02 00:30:19 +02:00
|
|
|
std::optional<epee::wipeable_string> on_device_pin_request() override
|
2019-02-23 15:28:18 +01:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
|
|
|
auto pin = m_listener->onDevicePinRequest();
|
|
|
|
if (pin){
|
2020-06-02 00:30:19 +02:00
|
|
|
return std::make_optional(epee::wipeable_string(pin->data(), pin->size()));
|
2019-02-23 15:28:18 +01:00
|
|
|
}
|
|
|
|
}
|
2020-06-02 00:30:19 +02:00
|
|
|
return std::nullopt;
|
2019-02-23 15:28:18 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-07-27 05:52:24 +02:00
|
|
|
std::optional<epee::wipeable_string> on_device_passphrase_request(bool& on_device) override
|
2019-02-23 15:28:18 +01:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
|
|
|
auto passphrase = m_listener->onDevicePassphraseRequest(on_device);
|
2020-04-07 18:25:25 +02:00
|
|
|
if (passphrase) {
|
2020-06-02 00:30:19 +02:00
|
|
|
return std::make_optional(epee::wipeable_string(passphrase->data(), passphrase->size()));
|
2019-02-23 15:28:18 +01:00
|
|
|
}
|
2020-04-07 18:25:25 +02:00
|
|
|
} else {
|
|
|
|
on_device = true;
|
2019-02-23 15:28:18 +01:00
|
|
|
}
|
2020-06-02 00:30:19 +02:00
|
|
|
return std::nullopt;
|
2019-02-23 15:28:18 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-01-14 00:26:23 +01:00
|
|
|
void on_device_progress(const hw::device_progress & event) override
|
2019-02-23 15:28:18 +01:00
|
|
|
{
|
|
|
|
if (m_listener) {
|
|
|
|
m_listener->onDeviceProgress(DeviceProgress(event.progress(), event.indeterminate()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-05 21:24:00 +02:00
|
|
|
WalletListener * m_listener;
|
2016-10-06 23:29:13 +02:00
|
|
|
WalletImpl * m_wallet;
|
2016-05-05 21:24:00 +02:00
|
|
|
};
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-02-21 19:18:16 +01:00
|
|
|
Wallet::~Wallet() {}
|
2016-04-03 13:34:38 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-05-16 12:11:44 +02:00
|
|
|
WalletListener::~WalletListener() {}
|
|
|
|
|
2016-07-14 11:47:01 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
std::string Wallet::displayAmount(uint64_t amount)
|
2016-04-19 11:25:03 +02:00
|
|
|
{
|
|
|
|
return cryptonote::print_money(amount);
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
uint64_t Wallet::amountFromString(const std::string &amount)
|
2016-06-16 15:42:33 +02:00
|
|
|
{
|
2016-08-23 12:46:51 +02:00
|
|
|
uint64_t result = 0;
|
2016-06-16 15:42:33 +02:00
|
|
|
cryptonote::parse_amount(result, amount);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-06-16 15:42:33 +02:00
|
|
|
uint64_t Wallet::amountFromDouble(double amount)
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << std::fixed << std::setprecision(CRYPTONOTE_DISPLAY_DECIMAL_POINT) << amount;
|
|
|
|
return amountFromString(ss.str());
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-06-23 13:38:22 +02:00
|
|
|
std::string Wallet::genPaymentId()
|
|
|
|
{
|
|
|
|
crypto::hash8 payment_id = crypto::rand<crypto::hash8>();
|
2020-10-23 22:32:28 +02:00
|
|
|
return tools::type_to_hex(payment_id);
|
2016-06-23 13:38:22 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-23 22:32:28 +02:00
|
|
|
bool Wallet::paymentIdValid(const std::string &payment_id)
|
2016-06-24 15:17:06 +02:00
|
|
|
{
|
2021-01-14 21:28:50 +01:00
|
|
|
return payment_id.size() == 16 && oxenmq::is_hex(payment_id);
|
2016-06-24 15:17:06 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-11-15 03:22:14 +01:00
|
|
|
bool Wallet::serviceNodePubkeyValid(const std::string &str)
|
|
|
|
{
|
|
|
|
crypto::public_key sn_key;
|
2021-01-14 21:28:50 +01:00
|
|
|
return str.size() == 64 && oxenmq::is_hex(str);
|
2018-11-15 03:22:14 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-05 15:59:16 +01:00
|
|
|
bool Wallet::addressValid(const std::string &str, NetworkType nettype)
|
2016-10-16 17:52:45 +02:00
|
|
|
{
|
2017-02-19 03:42:10 +01:00
|
|
|
cryptonote::address_parse_info info;
|
2018-03-05 15:59:16 +01:00
|
|
|
return get_account_address_from_str(info, static_cast<cryptonote::network_type>(nettype), str);
|
2016-10-16 17:52:45 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-05 15:59:16 +01:00
|
|
|
bool Wallet::keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, NetworkType nettype, std::string &error)
|
2017-01-26 21:33:36 +01:00
|
|
|
{
|
2017-02-19 03:42:10 +01:00
|
|
|
cryptonote::address_parse_info info;
|
2018-03-05 15:59:16 +01:00
|
|
|
if(!get_account_address_from_str(info, static_cast<cryptonote::network_type>(nettype), address_string)) {
|
2017-01-26 21:33:36 +01:00
|
|
|
error = tr("Failed to parse address");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
cryptonote::blobdata key_data;
|
|
|
|
if(!epee::string_tools::parse_hexstr_to_binbuff(secret_key_string, key_data) || key_data.size() != sizeof(crypto::secret_key))
|
|
|
|
{
|
|
|
|
error = tr("Failed to parse key");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
crypto::secret_key key = *reinterpret_cast<const crypto::secret_key*>(key_data.data());
|
|
|
|
|
|
|
|
// check the key match the given address
|
|
|
|
crypto::public_key pkey;
|
|
|
|
if (!crypto::secret_key_to_public_key(key, pkey)) {
|
|
|
|
error = tr("failed to verify key");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool matchAddress = false;
|
|
|
|
if(isViewKey)
|
2017-02-19 03:42:10 +01:00
|
|
|
matchAddress = info.address.m_view_public_key == pkey;
|
2017-01-26 21:33:36 +01:00
|
|
|
else
|
2017-02-19 03:42:10 +01:00
|
|
|
matchAddress = info.address.m_spend_public_key == pkey;
|
2017-01-26 21:33:36 +01:00
|
|
|
|
|
|
|
if(!matchAddress) {
|
|
|
|
error = tr("key does not match address");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-05 15:59:16 +01:00
|
|
|
std::string Wallet::paymentIdFromAddress(const std::string &str, NetworkType nettype)
|
2016-10-16 17:52:45 +02:00
|
|
|
{
|
2017-02-19 03:42:10 +01:00
|
|
|
cryptonote::address_parse_info info;
|
2018-03-05 15:59:16 +01:00
|
|
|
if (!get_account_address_from_str(info, static_cast<cryptonote::network_type>(nettype), str))
|
2016-10-16 17:52:45 +02:00
|
|
|
return "";
|
2017-02-19 03:42:10 +01:00
|
|
|
if (!info.has_payment_id)
|
2016-10-16 17:52:45 +02:00
|
|
|
return "";
|
2020-10-23 22:32:28 +02:00
|
|
|
return tools::type_to_hex(info.payment_id);
|
2016-10-16 17:52:45 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-08-23 11:35:01 +02:00
|
|
|
uint64_t Wallet::maximumAllowedAmount()
|
|
|
|
{
|
|
|
|
return std::numeric_limits<uint64_t>::max();
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
void Wallet::init(const char *argv0, const char *default_log_base_name, const std::string& log_path, bool console) {
|
2017-02-18 21:44:23 +01:00
|
|
|
epee::string_tools::set_module_name_and_folder(argv0);
|
2020-10-26 19:20:00 +01:00
|
|
|
mlog_configure(log_path.empty() ? mlog_get_default_log_path(default_log_base_name) : log_path, console);
|
2017-02-18 21:44:23 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-04 18:04:18 +01:00
|
|
|
void Wallet::debug(const std::string &category, const std::string &str) {
|
2021-01-04 04:19:42 +01:00
|
|
|
MCDEBUG(category.empty() ? OXEN_DEFAULT_LOG_CATEGORY : category.c_str(), str);
|
2018-03-04 18:04:18 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-04 18:04:18 +01:00
|
|
|
void Wallet::info(const std::string &category, const std::string &str) {
|
2021-01-04 04:19:42 +01:00
|
|
|
MCINFO(category.empty() ? OXEN_DEFAULT_LOG_CATEGORY : category.c_str(), str);
|
2018-03-04 18:04:18 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-04 18:04:18 +01:00
|
|
|
void Wallet::warning(const std::string &category, const std::string &str) {
|
2021-01-04 04:19:42 +01:00
|
|
|
MCWARNING(category.empty() ? OXEN_DEFAULT_LOG_CATEGORY : category.c_str(), str);
|
2018-03-04 18:04:18 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-04 18:04:18 +01:00
|
|
|
void Wallet::error(const std::string &category, const std::string &str) {
|
2021-01-04 04:19:42 +01:00
|
|
|
MCERROR(category.empty() ? OXEN_DEFAULT_LOG_CATEGORY : category.c_str(), str);
|
2017-02-05 20:12:01 +01:00
|
|
|
}
|
2016-06-16 15:42:33 +02:00
|
|
|
|
2016-04-20 12:01:00 +02:00
|
|
|
///////////////////////// WalletImpl implementation ////////////////////////
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-07-30 07:38:55 +02:00
|
|
|
WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
|
2021-06-23 03:08:12 +02:00
|
|
|
:m_wallet_ptr(nullptr)
|
2020-10-26 19:20:00 +01:00
|
|
|
, m_status(Wallet::Status_Ok, "")
|
2016-10-06 23:29:13 +02:00
|
|
|
, m_wallet2Callback(nullptr)
|
|
|
|
, m_recoveringFromSeed(false)
|
2018-06-04 09:58:13 +02:00
|
|
|
, m_recoveringFromDevice(false)
|
2016-10-06 23:29:13 +02:00
|
|
|
, m_synchronized(false)
|
2016-11-09 18:46:03 +01:00
|
|
|
, m_rebuildWalletCache(false)
|
2016-12-14 12:18:52 +01:00
|
|
|
, m_is_connected(false)
|
2018-10-16 13:58:22 +02:00
|
|
|
, m_refreshShouldRescan(false)
|
2016-02-15 19:04:00 +01:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
m_wallet_ptr.reset(new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true));
|
2018-10-04 15:37:41 +02:00
|
|
|
m_history.reset(new TransactionHistoryImpl(this));
|
|
|
|
m_wallet2Callback.reset(new Wallet2CallbackImpl(this));
|
2021-06-23 03:08:12 +02:00
|
|
|
m_wallet_ptr->callback(m_wallet2Callback.get());
|
2016-07-10 16:17:23 +02:00
|
|
|
m_refreshThreadDone = false;
|
|
|
|
m_refreshEnabled = false;
|
2018-10-04 15:37:41 +02:00
|
|
|
m_addressBook.reset(new AddressBookImpl(this));
|
|
|
|
m_subaddress.reset(new SubaddressImpl(this));
|
|
|
|
m_subaddressAccount.reset(new SubaddressAccountImpl(this));
|
2016-09-30 21:42:15 +02:00
|
|
|
|
2016-09-20 19:08:33 +02:00
|
|
|
|
2016-09-26 21:50:10 +02:00
|
|
|
m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS;
|
2016-09-20 19:08:33 +02:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
m_refreshThread = std::thread([this] { refreshThreadFunc(); });
|
2016-07-10 16:17:23 +02:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
m_longPollThread = std::thread([this] {
|
2020-01-15 05:17:26 +01:00
|
|
|
for (;;)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
if (m_wallet_ptr->m_long_poll_disabled)
|
2020-10-27 16:08:08 +01:00
|
|
|
return true;
|
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
if (m_refreshEnabled && m_wallet_ptr->long_poll_pool_state())
|
2020-01-15 05:17:26 +01:00
|
|
|
m_refreshCV.notify_one();
|
2020-10-27 16:08:08 +01:00
|
|
|
} catch (...) { /* ignore */ }
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
std::this_thread::sleep_for(1s);
|
2020-01-15 05:17:26 +01:00
|
|
|
}
|
|
|
|
});
|
2016-02-15 19:04:00 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-02-15 19:04:00 +01:00
|
|
|
WalletImpl::~WalletImpl()
|
|
|
|
{
|
2016-12-14 12:18:52 +01:00
|
|
|
|
|
|
|
LOG_PRINT_L1(__FUNCTION__);
|
2021-06-23 03:08:12 +02:00
|
|
|
m_wallet_ptr->callback(nullptr);
|
|
|
|
// Stop refresh and long poll threads
|
2016-07-10 16:17:23 +02:00
|
|
|
stopRefresh();
|
2021-06-23 03:08:12 +02:00
|
|
|
m_wallet_ptr->cancel_long_poll();
|
2020-10-27 16:08:08 +01:00
|
|
|
if (m_longPollThread.joinable())
|
|
|
|
m_longPollThread.join();
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
// Close wallet - stores cache and stops ongoing refresh operation
|
|
|
|
close(false); // do not store wallet as part of the closing activities
|
|
|
|
|
2019-03-26 12:29:08 +01:00
|
|
|
if (m_wallet2Callback->getListener()) {
|
|
|
|
m_wallet2Callback->getListener()->onSetWallet(nullptr);
|
|
|
|
}
|
|
|
|
|
2016-12-14 12:18:52 +01:00
|
|
|
LOG_PRINT_L1(__FUNCTION__ << " finished");
|
2016-02-15 19:04:00 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::create(std::string_view path_, const std::string &password, const std::string &language)
|
2016-02-15 19:04:00 +01:00
|
|
|
{
|
2016-03-10 17:43:10 +01:00
|
|
|
|
2020-10-26 19:20:00 +01:00
|
|
|
auto path = fs::u8path(path_);
|
2016-03-10 17:43:10 +01:00
|
|
|
clearStatus();
|
2016-10-01 20:04:49 +02:00
|
|
|
m_recoveringFromSeed = false;
|
2018-06-04 09:58:13 +02:00
|
|
|
m_recoveringFromDevice = false;
|
2016-02-15 19:04:00 +01:00
|
|
|
bool keys_file_exists;
|
|
|
|
bool wallet_file_exists;
|
|
|
|
tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists);
|
2020-10-26 19:20:00 +01:00
|
|
|
LOG_PRINT_L3("wallet_path: " << path);
|
2016-02-15 19:04:00 +01:00
|
|
|
LOG_PRINT_L3("keys_file_exists: " << std::boolalpha << keys_file_exists << std::noboolalpha
|
|
|
|
<< " wallet_file_exists: " << std::boolalpha << wallet_file_exists << std::noboolalpha);
|
|
|
|
|
|
|
|
|
|
|
|
// add logic to error out if new wallet requested but named wallet file exists
|
|
|
|
if (keys_file_exists || wallet_file_exists) {
|
2018-03-21 18:34:15 +01:00
|
|
|
std::string error = "attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.";
|
|
|
|
LOG_ERROR(error);
|
|
|
|
setStatusCritical(error);
|
2016-02-15 19:04:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// TODO: validate language
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
w->set_seed_language(language);
|
2016-02-15 19:04:00 +01:00
|
|
|
crypto::secret_key recovery_val, secret_key;
|
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
recovery_val = w->generate(path, password, secret_key, false, false);
|
2016-03-15 21:11:38 +01:00
|
|
|
m_password = password;
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2016-03-10 17:43:10 +01:00
|
|
|
} catch (const std::exception &e) {
|
|
|
|
LOG_ERROR("Error creating wallet: " << e.what());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusCritical(e.what());
|
2016-02-15 19:04:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-03-15 21:11:38 +01:00
|
|
|
|
2016-02-15 19:04:00 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::createWatchOnly(std::string_view path_, const std::string &password, const std::string &language) const
|
2017-01-10 22:34:15 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
auto path = fs::u8path(path_);
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
2017-01-10 22:34:15 +01:00
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
std::unique_ptr<tools::wallet2> view_wallet(new tools::wallet2(w->nettype()));
|
2017-01-10 22:34:15 +01:00
|
|
|
|
|
|
|
// Store same refresh height as original wallet
|
2021-06-23 03:08:12 +02:00
|
|
|
view_wallet->set_refresh_from_block_height(w->get_refresh_from_block_height());
|
2017-01-10 22:34:15 +01:00
|
|
|
|
|
|
|
bool keys_file_exists;
|
|
|
|
bool wallet_file_exists;
|
|
|
|
tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists);
|
2020-10-26 19:20:00 +01:00
|
|
|
LOG_PRINT_L3("wallet_path: " << path);
|
2017-01-10 22:34:15 +01:00
|
|
|
LOG_PRINT_L3("keys_file_exists: " << std::boolalpha << keys_file_exists << std::noboolalpha
|
|
|
|
<< " wallet_file_exists: " << std::boolalpha << wallet_file_exists << std::noboolalpha);
|
|
|
|
|
|
|
|
// add logic to error out if new wallet requested but named wallet file exists
|
|
|
|
if (keys_file_exists || wallet_file_exists) {
|
2018-03-21 18:34:15 +01:00
|
|
|
std::string error = "attempting to generate view only wallet, but specified file(s) exist. Exiting to not risk overwriting.";
|
|
|
|
LOG_ERROR(error);
|
|
|
|
setStatusError(error);
|
2017-01-10 22:34:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// TODO: validate language
|
|
|
|
view_wallet->set_seed_language(language);
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
const crypto::secret_key viewkey = w->get_account().get_keys().m_view_secret_key;
|
|
|
|
const cryptonote::account_public_address address = w->get_account().get_keys().m_account_address;
|
2017-01-10 22:34:15 +01:00
|
|
|
|
|
|
|
try {
|
2017-08-18 14:28:46 +02:00
|
|
|
// Generate view only wallet
|
2017-01-10 22:34:15 +01:00
|
|
|
view_wallet->generate(path, password, address, viewkey);
|
2017-08-18 14:28:46 +02:00
|
|
|
|
|
|
|
// Export/Import outputs
|
2021-06-23 03:08:12 +02:00
|
|
|
auto outputs = w->export_outputs();
|
2017-08-18 14:28:46 +02:00
|
|
|
view_wallet->import_outputs(outputs);
|
|
|
|
|
|
|
|
// Copy scanned blockchain
|
2021-06-23 03:08:12 +02:00
|
|
|
auto bc = w->export_blockchain();
|
2017-08-18 14:28:46 +02:00
|
|
|
view_wallet->import_blockchain(bc);
|
|
|
|
|
|
|
|
// copy payments
|
2021-06-23 03:08:12 +02:00
|
|
|
auto payments = w->export_payments();
|
2017-08-18 14:28:46 +02:00
|
|
|
view_wallet->import_payments(payments);
|
|
|
|
|
|
|
|
// copy confirmed outgoing payments
|
|
|
|
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> out_payments;
|
2021-06-23 03:08:12 +02:00
|
|
|
w->get_payments_out(out_payments, 0);
|
2017-08-18 14:28:46 +02:00
|
|
|
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
|
2021-06-23 03:08:12 +02:00
|
|
|
auto key_images = w->export_key_images(false /* requested_ki_only */);
|
2017-08-18 14:28:46 +02:00
|
|
|
uint64_t spent = 0;
|
|
|
|
uint64_t unspent = 0;
|
2018-11-07 07:09:43 +01:00
|
|
|
view_wallet->import_key_images(key_images.second, key_images.first, spent, unspent, false);
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2017-01-10 22:34:15 +01:00
|
|
|
} catch (const std::exception &e) {
|
|
|
|
LOG_ERROR("Error creating view only wallet: " << e.what());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-01-10 22:34:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
2017-08-18 14:28:46 +02:00
|
|
|
// Store wallet
|
|
|
|
view_wallet->store();
|
2017-01-10 22:34:15 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::recoverFromKeysWithPassword(std::string_view path_,
|
2018-01-11 16:12:27 +01:00
|
|
|
const std::string &password,
|
|
|
|
const std::string &language,
|
|
|
|
const std::string &address_string,
|
|
|
|
const std::string &viewkey_string,
|
|
|
|
const std::string &spendkey_string)
|
2017-01-26 21:33:36 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
auto path = fs::u8path(path_);
|
2017-02-19 03:42:10 +01:00
|
|
|
cryptonote::address_parse_info info;
|
2021-06-23 03:08:12 +02:00
|
|
|
if(!get_account_address_from_str(info, m_wallet_ptr->nettype(), address_string))
|
2017-01-26 21:33:36 +01:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("failed to parse address"));
|
2017-01-26 21:33:36 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse optional spend key
|
|
|
|
crypto::secret_key spendkey;
|
|
|
|
bool has_spendkey = false;
|
|
|
|
if (!spendkey_string.empty()) {
|
|
|
|
cryptonote::blobdata spendkey_data;
|
|
|
|
if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("failed to parse secret spend key"));
|
2017-01-26 21:33:36 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
has_spendkey = true;
|
|
|
|
spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse view secret key
|
2018-06-06 01:32:03 +02:00
|
|
|
bool has_viewkey = true;
|
|
|
|
crypto::secret_key viewkey;
|
2017-01-26 21:33:36 +01:00
|
|
|
if (viewkey_string.empty()) {
|
2018-06-06 01:32:03 +02:00
|
|
|
if(has_spendkey) {
|
|
|
|
has_viewkey = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setStatusError(tr("Neither view key nor spend key supplied, cancelled"));
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-26 21:33:36 +01:00
|
|
|
}
|
2018-06-06 01:32:03 +02:00
|
|
|
if(has_viewkey) {
|
|
|
|
cryptonote::blobdata viewkey_data;
|
|
|
|
if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
|
|
|
|
{
|
|
|
|
setStatusError(tr("failed to parse secret view key"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
|
2017-01-26 21:33:36 +01:00
|
|
|
}
|
|
|
|
// check the spend and view keys match the given address
|
|
|
|
crypto::public_key pkey;
|
|
|
|
if(has_spendkey) {
|
|
|
|
if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("failed to verify secret spend key"));
|
2017-01-26 21:33:36 +01:00
|
|
|
return false;
|
|
|
|
}
|
2017-02-19 03:42:10 +01:00
|
|
|
if (info.address.m_spend_public_key != pkey) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("spend key does not match address"));
|
2017-01-26 21:33:36 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-06-06 01:32:03 +02:00
|
|
|
if(has_viewkey) {
|
|
|
|
if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
|
|
|
|
setStatusError(tr("failed to verify secret view key"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (info.address.m_view_public_key != pkey) {
|
|
|
|
setStatusError(tr("view key does not match address"));
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-26 21:33:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
2018-06-06 01:32:03 +02:00
|
|
|
if (has_spendkey && has_viewkey) {
|
2021-06-23 03:08:12 +02:00
|
|
|
w->generate(path, password, info.address, spendkey, viewkey);
|
2018-06-06 01:32:03 +02:00
|
|
|
LOG_PRINT_L1("Generated new wallet from spend key and view key");
|
2017-01-26 21:33:36 +01:00
|
|
|
}
|
2018-06-06 01:32:03 +02:00
|
|
|
if(!has_spendkey && has_viewkey) {
|
2021-06-23 03:08:12 +02:00
|
|
|
w->generate(path, password, info.address, viewkey);
|
2017-01-26 21:33:36 +01:00
|
|
|
LOG_PRINT_L1("Generated new view only wallet from keys");
|
|
|
|
}
|
2018-06-06 01:32:03 +02:00
|
|
|
if(has_spendkey && !has_viewkey) {
|
2021-06-23 03:08:12 +02:00
|
|
|
w->generate(path, password, spendkey, true, false);
|
|
|
|
setSeedLanguage(language);
|
|
|
|
LOG_PRINT_L1("Generated deterministic wallet from spend key with seed language: " + language);
|
2018-06-06 01:32:03 +02:00
|
|
|
}
|
2017-01-26 21:33:36 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("failed to generate new wallet: ")) + e.what());
|
2017-01-26 21:33:36 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::recoverFromDevice(std::string_view path_, const std::string &password, const std::string &device_name)
|
2018-06-04 09:58:13 +02:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
auto path = fs::u8path(path_);
|
2018-06-04 09:58:13 +02:00
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
2018-06-04 09:58:13 +02:00
|
|
|
m_recoveringFromSeed = false;
|
|
|
|
m_recoveringFromDevice = true;
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
w->restore_from_device(path, password, device_name);
|
2018-06-04 09:58:13 +02:00
|
|
|
LOG_PRINT_L1("Generated new wallet from device: " + device_name);
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("failed to generate new wallet: ")) + e.what());
|
2018-06-04 09:58:13 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2017-01-26 21:33:36 +01:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-08-16 10:31:48 +02:00
|
|
|
Wallet::Device WalletImpl::getDeviceType() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return static_cast<Wallet::Device>(m_wallet_ptr->get_device_type());
|
2018-08-16 10:31:48 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::open(std::string_view path_, const std::string &password)
|
2016-03-10 17:43:10 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
auto path = fs::u8path(path_);
|
2016-03-10 17:43:10 +01:00
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
2016-10-01 20:04:49 +02:00
|
|
|
m_recoveringFromSeed = false;
|
2018-06-04 09:58:13 +02:00
|
|
|
m_recoveringFromDevice = false;
|
2016-03-10 17:43:10 +01:00
|
|
|
try {
|
|
|
|
// TODO: handle "deprecated"
|
2016-11-09 18:46:03 +01:00
|
|
|
// Check if wallet cache exists
|
|
|
|
bool keys_file_exists;
|
|
|
|
bool wallet_file_exists;
|
|
|
|
tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists);
|
|
|
|
if(!wallet_file_exists){
|
|
|
|
// Rebuilding wallet cache, using refresh height from .keys file
|
|
|
|
m_rebuildWalletCache = true;
|
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
w->set_ring_database(get_default_ringdb_path(w->nettype()));
|
|
|
|
w->load(path, password);
|
2016-03-15 21:11:38 +01:00
|
|
|
|
|
|
|
m_password = password;
|
2016-03-11 15:05:36 +01:00
|
|
|
} catch (const std::exception &e) {
|
2016-03-10 17:43:10 +01:00
|
|
|
LOG_ERROR("Error opening wallet: " << e.what());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusCritical(e.what());
|
2016-03-10 17:43:10 +01:00
|
|
|
}
|
2020-10-26 19:20:00 +01:00
|
|
|
return good();
|
2018-01-11 16:12:27 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::recover(std::string_view path_, const std::string &password, const std::string &seed, const std::string &seed_offset/* = {}*/)
|
2016-03-12 15:41:11 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
auto path = fs::u8path(path_);
|
2016-03-15 21:11:38 +01:00
|
|
|
clearStatus();
|
2016-03-12 15:41:11 +01:00
|
|
|
if (seed.empty()) {
|
2018-03-21 18:34:15 +01:00
|
|
|
LOG_ERROR("Electrum seed is empty");
|
|
|
|
setStatusError(tr("Electrum seed is empty"));
|
2016-03-12 15:41:11 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-10-01 20:04:49 +02:00
|
|
|
m_recoveringFromSeed = true;
|
2018-06-04 09:58:13 +02:00
|
|
|
m_recoveringFromDevice = false;
|
2016-03-12 15:41:11 +01:00
|
|
|
crypto::secret_key recovery_key;
|
|
|
|
std::string old_language;
|
|
|
|
if (!crypto::ElectrumWords::words_to_bytes(seed, recovery_key, old_language)) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Electrum-style word list failed verification"));
|
2016-03-12 15:41:11 +01:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-12 00:27:30 +01:00
|
|
|
if (!seed_offset.empty())
|
|
|
|
{
|
|
|
|
recovery_key = cryptonote::decrypt_key(recovery_key, seed_offset);
|
|
|
|
}
|
2016-03-12 15:41:11 +01:00
|
|
|
|
2018-02-25 14:04:36 +01:00
|
|
|
if (old_language == crypto::ElectrumWords::old_language_name)
|
|
|
|
old_language = Language::English().get_language_name();
|
|
|
|
|
2016-03-12 15:41:11 +01:00
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
w->set_seed_language(old_language);
|
|
|
|
w->generate(path, password, recovery_key, true, false);
|
2016-10-01 20:04:49 +02:00
|
|
|
|
2016-03-12 15:41:11 +01:00
|
|
|
} catch (const std::exception &e) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusCritical(e.what());
|
2016-03-12 15:41:11 +01:00
|
|
|
}
|
2020-10-26 19:20:00 +01:00
|
|
|
return good();
|
2016-03-12 15:41:11 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-09-23 00:52:09 +02:00
|
|
|
bool WalletImpl::close(bool store)
|
2016-03-10 17:43:10 +01:00
|
|
|
{
|
2016-08-22 14:53:07 +02:00
|
|
|
|
2016-03-10 17:43:10 +01:00
|
|
|
bool result = false;
|
2016-12-14 12:18:52 +01:00
|
|
|
LOG_PRINT_L1("closing wallet...");
|
2016-03-10 17:43:10 +01:00
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
2017-09-23 00:52:09 +02:00
|
|
|
if (store) {
|
|
|
|
// Do not store wallet with invalid status
|
|
|
|
// Status Critical refers to errors on opening or creating wallets.
|
2020-10-26 19:20:00 +01:00
|
|
|
if (status().first != Status_Critical)
|
2021-06-23 03:08:12 +02:00
|
|
|
w->store();
|
2017-09-23 00:52:09 +02:00
|
|
|
else
|
2018-01-09 22:37:30 +01:00
|
|
|
LOG_ERROR("Status_Critical - not saving wallet");
|
2017-09-23 00:52:09 +02:00
|
|
|
LOG_PRINT_L1("wallet::store done");
|
|
|
|
}
|
2016-12-14 12:18:52 +01:00
|
|
|
LOG_PRINT_L1("Calling wallet::stop...");
|
2021-06-23 03:08:12 +02:00
|
|
|
w->stop();
|
2016-12-14 12:18:52 +01:00
|
|
|
LOG_PRINT_L1("wallet::stop done");
|
2021-06-23 03:08:12 +02:00
|
|
|
w->deinit();
|
2016-03-10 17:43:10 +01:00
|
|
|
result = true;
|
2016-08-22 14:53:07 +02:00
|
|
|
clearStatus();
|
2016-03-10 17:43:10 +01:00
|
|
|
} catch (const std::exception &e) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusCritical(e.what());
|
2016-03-10 17:43:10 +01:00
|
|
|
LOG_ERROR("Error closing wallet: " << e.what());
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-02-20 17:04:56 +01:00
|
|
|
std::string WalletImpl::seed() const
|
|
|
|
{
|
2018-07-07 01:03:15 +02:00
|
|
|
epee::wipeable_string seed;
|
2021-06-23 03:08:12 +02:00
|
|
|
if (m_wallet_ptr)
|
|
|
|
wallet()->get_seed(seed);
|
2018-07-07 01:03:15 +02:00
|
|
|
return std::string(seed.data(), seed.size()); // TODO
|
2016-02-21 19:18:16 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-02-21 19:18:16 +01:00
|
|
|
std::string WalletImpl::getSeedLanguage() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_seed_language();
|
2016-02-20 17:04:56 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-02-21 19:18:16 +01:00
|
|
|
void WalletImpl::setSeedLanguage(const std::string &arg)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->set_seed_language(arg);
|
2016-02-21 19:18:16 +01:00
|
|
|
}
|
2016-02-15 19:04:00 +01:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
std::pair<int, std::string> WalletImpl::status() const {
|
2020-06-02 01:01:24 +02:00
|
|
|
std::lock_guard l{m_statusMutex};
|
2016-03-10 17:43:10 +01:00
|
|
|
return m_status;
|
|
|
|
}
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::good() const {
|
2020-06-02 01:01:24 +02:00
|
|
|
std::lock_guard l{m_statusMutex};
|
2020-10-26 19:20:00 +01:00
|
|
|
return m_status.first == Status_Ok;
|
2018-03-21 18:34:15 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-03-11 15:05:36 +01:00
|
|
|
bool WalletImpl::setPassword(const std::string &password)
|
|
|
|
{
|
2016-03-15 21:11:38 +01:00
|
|
|
clearStatus();
|
2016-03-11 15:05:36 +01:00
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
w->change_password(w->get_wallet_file(), m_password, password);
|
2016-03-15 21:11:38 +01:00
|
|
|
m_password = password;
|
2016-03-11 15:05:36 +01:00
|
|
|
} catch (const std::exception &e) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2016-03-11 15:05:36 +01:00
|
|
|
}
|
2020-10-26 19:20:00 +01:00
|
|
|
return good();
|
2016-03-11 15:05:36 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2019-02-23 15:28:18 +01:00
|
|
|
bool WalletImpl::setDevicePin(const std::string &pin)
|
|
|
|
{
|
|
|
|
clearStatus();
|
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->get_account().get_device().set_pin(epee::wipeable_string(pin.data(), pin.size()));
|
2019-02-23 15:28:18 +01:00
|
|
|
} catch (const std::exception &e) {
|
|
|
|
setStatusError(e.what());
|
|
|
|
}
|
2020-10-26 19:20:00 +01:00
|
|
|
return good();
|
2019-02-23 15:28:18 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2019-02-23 15:28:18 +01:00
|
|
|
bool WalletImpl::setDevicePassphrase(const std::string &passphrase)
|
|
|
|
{
|
|
|
|
clearStatus();
|
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->get_account().get_device().set_passphrase(epee::wipeable_string(passphrase.data(), passphrase.size()));
|
2019-02-23 15:28:18 +01:00
|
|
|
} catch (const std::exception &e) {
|
|
|
|
setStatusError(e.what());
|
|
|
|
}
|
2020-10-26 19:20:00 +01:00
|
|
|
return good();
|
2019-02-23 15:28:18 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const
|
2016-03-12 15:52:58 +01:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_subaddress_as_str({accountIndex, addressIndex});
|
2016-03-12 15:52:58 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-06-23 13:38:22 +02:00
|
|
|
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
|
|
|
|
{
|
|
|
|
crypto::hash8 pid;
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(payment_id, pid))
|
2016-10-16 14:17:29 +02:00
|
|
|
return "";
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_integrated_address_as_str(pid);
|
2016-06-23 13:38:22 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-05-02 16:48:38 +02:00
|
|
|
std::string WalletImpl::secretViewKey() const
|
2017-01-10 22:34:15 +01:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return tools::type_to_hex(wallet()->get_account().get_keys().m_view_secret_key);
|
2017-01-10 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-05-02 16:48:38 +02:00
|
|
|
std::string WalletImpl::publicViewKey() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return tools::type_to_hex(wallet()->get_account().get_keys().m_account_address.m_view_public_key);
|
2017-05-02 16:48:38 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-05-02 16:48:38 +02:00
|
|
|
std::string WalletImpl::secretSpendKey() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return tools::type_to_hex(wallet()->get_account().get_keys().m_spend_secret_key);
|
2017-05-02 16:48:38 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-05-02 16:48:38 +02:00
|
|
|
std::string WalletImpl::publicSpendKey() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return tools::type_to_hex(wallet()->get_account().get_keys().m_account_address.m_spend_public_key);
|
2017-05-02 16:48:38 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-04-10 18:21:25 +02:00
|
|
|
std::string WalletImpl::publicMultisigSignerKey() const
|
|
|
|
{
|
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
crypto::public_key signer = wallet()->get_multisig_signer_public_key();
|
2020-10-23 22:32:28 +02:00
|
|
|
return tools::type_to_hex(signer);
|
2018-04-10 18:21:25 +02:00
|
|
|
} catch (const std::exception&) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
std::string WalletImpl::path() const
|
2016-11-26 15:19:57 +01:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->path().u8string();
|
2016-11-26 15:19:57 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::store(std::string_view path_)
|
2016-03-15 21:11:38 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
auto path = fs::u8path(path_);
|
2016-03-15 21:11:38 +01:00
|
|
|
clearStatus();
|
|
|
|
try {
|
|
|
|
if (path.empty()) {
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->store();
|
2016-03-15 21:11:38 +01:00
|
|
|
} else {
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->store_to(path, m_password);
|
2016-03-15 21:11:38 +01:00
|
|
|
}
|
|
|
|
} catch (const std::exception &e) {
|
2018-01-09 22:37:30 +01:00
|
|
|
LOG_ERROR("Error saving wallet: " << e.what());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
|
|
|
return false;
|
2016-03-15 21:11:38 +01:00
|
|
|
}
|
|
|
|
|
2018-03-21 18:34:15 +01:00
|
|
|
return true;
|
2016-03-15 21:11:38 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
std::string WalletImpl::filename() const
|
2016-06-10 11:51:09 +02:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_wallet_file().u8string();
|
2016-06-10 11:51:09 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
std::string WalletImpl::keysFilename() const
|
2016-06-10 11:51:09 +02:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_keys_file().u8string();
|
2016-06-10 11:51:09 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2021-09-02 18:59:49 +02:00
|
|
|
bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl ENABLE_IF_LIGHT_WALLET(, bool lightWallet))
|
2016-03-25 15:06:30 +01:00
|
|
|
{
|
|
|
|
clearStatus();
|
2021-09-02 18:59:49 +02:00
|
|
|
#ifdef ENABLE_LIGHT_WALLET
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->set_light_wallet(lightWallet);
|
2021-09-02 18:59:49 +02:00
|
|
|
#endif
|
2017-02-25 22:08:50 +01:00
|
|
|
if(daemon_username != "")
|
|
|
|
m_daemon_login.emplace(daemon_username, daemon_password);
|
2017-08-04 23:28:38 +02:00
|
|
|
return doInit(daemon_address, upper_transaction_size_limit, use_ssl);
|
2016-07-14 11:47:01 +02:00
|
|
|
}
|
|
|
|
|
2021-09-02 18:59:49 +02:00
|
|
|
#ifdef ENABLE_LIGHT_WALLET
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-08-04 23:31:33 +02:00
|
|
|
bool WalletImpl::lightWalletLogin(bool &isNewWallet) const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->light_wallet_login(isNewWallet);
|
2017-08-04 23:31:33 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-08-04 23:31:33 +02:00
|
|
|
bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2020-07-27 05:52:24 +02:00
|
|
|
tools::light_rpc::IMPORT_WALLET_REQUEST::response response{};
|
2021-06-23 03:08:12 +02:00
|
|
|
if(!wallet()->light_wallet_import_wallet_request(response)){
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to send import wallet request"));
|
2017-08-04 23:31:33 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
fee = response.import_fee;
|
|
|
|
payment_id = response.payment_id;
|
|
|
|
new_request = response.new_request;
|
|
|
|
request_fulfilled = response.request_fulfilled;
|
|
|
|
payment_address = response.payment_address;
|
|
|
|
status = response.status;
|
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
|
|
|
LOG_ERROR("Error sending import wallet request: " << e.what());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-08-04 23:31:33 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2021-09-02 18:59:49 +02:00
|
|
|
#endif
|
2017-08-04 23:31:33 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-10-08 00:57:09 +02:00
|
|
|
void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->set_refresh_from_block_height(refresh_from_block_height);
|
2016-10-08 00:57:09 +02:00
|
|
|
}
|
2016-07-14 11:47:01 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-10-10 19:34:25 +02:00
|
|
|
void WalletImpl::setRecoveringFromSeed(bool recoveringFromSeed)
|
|
|
|
{
|
|
|
|
m_recoveringFromSeed = recoveringFromSeed;
|
|
|
|
}
|
2016-07-14 11:47:01 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-06-04 09:58:13 +02:00
|
|
|
void WalletImpl::setRecoveringFromDevice(bool recoveringFromDevice)
|
|
|
|
{
|
|
|
|
m_recoveringFromDevice = recoveringFromDevice;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-06-04 09:58:13 +02:00
|
|
|
void WalletImpl::setSubaddressLookahead(uint32_t major, uint32_t minor)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->set_subaddress_lookahead(major, minor);
|
2018-06-04 09:58:13 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
uint64_t WalletImpl::balance(uint32_t accountIndex) const
|
2016-03-25 15:06:30 +01:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->balance(accountIndex, false);
|
2016-03-25 15:06:30 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const
|
2016-03-31 11:48:41 +02:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->unlocked_balance(accountIndex, false);
|
2016-03-31 11:48:41 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-12-17 19:29:39 +01:00
|
|
|
std::vector<std::pair<std::string, uint64_t>>* WalletImpl::listCurrentStakes() const
|
2020-11-21 01:40:11 +01:00
|
|
|
{
|
2020-12-17 19:29:39 +01:00
|
|
|
std::vector<std::pair<std::string, uint64_t>>* stakes = new std::vector<std::pair<std::string, uint64_t>>;
|
2020-11-21 02:35:32 +01:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
auto response = wallet()->list_current_stakes();
|
2020-11-21 02:35:32 +01:00
|
|
|
|
|
|
|
for (rpc::GET_SERVICE_NODES::response::entry const &node_info : response)
|
|
|
|
{
|
2020-12-08 03:33:26 +01:00
|
|
|
for (const auto& contributor : node_info.contributors)
|
2020-11-21 02:35:32 +01:00
|
|
|
{
|
2020-12-08 03:33:26 +01:00
|
|
|
stakes->push_back(std::make_pair(node_info.service_node_pubkey, contributor.amount));
|
2020-11-21 02:35:32 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-21 01:40:11 +01:00
|
|
|
return stakes;
|
|
|
|
}
|
|
|
|
|
2021-07-07 23:10:29 +02:00
|
|
|
EXPORT
|
|
|
|
uint64_t WalletImpl::blockChainHeight() const
|
|
|
|
{
|
|
|
|
// This call is thread-safe
|
|
|
|
auto& w = m_wallet_ptr;
|
2021-09-02 18:59:49 +02:00
|
|
|
#ifdef ENABLE_LIGHT_WALLET
|
2021-06-23 03:08:12 +02:00
|
|
|
if(w->light_wallet()) {
|
|
|
|
return w->get_light_wallet_scanned_block_height();
|
|
|
|
}
|
2021-09-02 18:59:49 +02:00
|
|
|
#endif
|
2021-06-23 03:08:12 +02:00
|
|
|
return w->get_blockchain_current_height();
|
|
|
|
}
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-11-10 15:36:16 +01:00
|
|
|
uint64_t WalletImpl::approximateBlockChainHeight() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_approximate_blockchain_height();
|
2016-11-10 15:36:16 +01:00
|
|
|
}
|
2019-01-02 01:08:46 +01:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2019-01-02 01:08:46 +01:00
|
|
|
uint64_t WalletImpl::estimateBlockChainHeight() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->estimate_blockchain_height();
|
2019-01-02 01:08:46 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-09-26 20:35:00 +02:00
|
|
|
uint64_t WalletImpl::daemonBlockChainHeight() const
|
|
|
|
{
|
2021-07-07 23:10:29 +02:00
|
|
|
// I *think* the calls here are thread-safe, so we can do this without locking
|
|
|
|
//auto w = wallet();
|
|
|
|
auto& w = m_wallet_ptr;
|
|
|
|
|
2021-09-02 18:59:49 +02:00
|
|
|
#ifdef ENABLE_LIGHT_WALLET
|
2021-06-23 03:08:12 +02:00
|
|
|
if(w->light_wallet()) {
|
|
|
|
return w->get_light_wallet_scanned_block_height();
|
2017-08-05 00:08:32 +02:00
|
|
|
}
|
2021-09-02 18:59:49 +02:00
|
|
|
#endif
|
2016-12-14 12:18:52 +01:00
|
|
|
if (!m_is_connected)
|
|
|
|
return 0;
|
2016-09-26 20:35:00 +02:00
|
|
|
std::string err;
|
2021-06-23 03:08:12 +02:00
|
|
|
uint64_t result = w->get_daemon_blockchain_height(err);
|
2016-09-26 20:35:00 +02:00
|
|
|
if (!err.empty()) {
|
|
|
|
LOG_ERROR(__FUNCTION__ << ": " << err);
|
2016-09-27 23:31:21 +02:00
|
|
|
result = 0;
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(err);
|
2016-09-26 20:35:00 +02:00
|
|
|
} else {
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2016-09-26 20:35:00 +02:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-10-03 20:47:41 +02:00
|
|
|
uint64_t WalletImpl::daemonBlockChainTargetHeight() const
|
|
|
|
{
|
2021-07-07 23:10:29 +02:00
|
|
|
// As above
|
|
|
|
//auto w = wallet();
|
|
|
|
auto& w = m_wallet_ptr;
|
|
|
|
|
2021-09-02 18:59:49 +02:00
|
|
|
#ifdef ENABLE_LIGHT_WALLET
|
2021-06-23 03:08:12 +02:00
|
|
|
if(w->light_wallet()) {
|
|
|
|
return w->get_light_wallet_blockchain_height();
|
2017-08-05 00:08:32 +02:00
|
|
|
}
|
2021-09-02 18:59:49 +02:00
|
|
|
#endif
|
2016-12-14 12:18:52 +01:00
|
|
|
if (!m_is_connected)
|
|
|
|
return 0;
|
2016-10-03 20:47:41 +02:00
|
|
|
std::string err;
|
2021-06-23 03:08:12 +02:00
|
|
|
uint64_t result = w->get_daemon_blockchain_target_height(err);
|
2016-10-03 20:47:41 +02:00
|
|
|
if (!err.empty()) {
|
|
|
|
LOG_ERROR(__FUNCTION__ << ": " << err);
|
|
|
|
result = 0;
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(err);
|
2016-10-03 20:47:41 +02:00
|
|
|
} else {
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2016-10-03 20:47:41 +02:00
|
|
|
}
|
2016-12-14 12:18:52 +01:00
|
|
|
// Target height can be 0 when daemon is synced. Use blockchain height instead.
|
|
|
|
if(result == 0)
|
|
|
|
result = daemonBlockChainHeight();
|
2016-10-03 20:47:41 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-12-14 12:18:52 +01:00
|
|
|
bool WalletImpl::daemonSynced() const
|
|
|
|
{
|
|
|
|
if(connected() == Wallet::ConnectionStatus_Disconnected)
|
|
|
|
return false;
|
|
|
|
uint64_t blockChainHeight = daemonBlockChainHeight();
|
|
|
|
return (blockChainHeight >= daemonBlockChainTargetHeight() && blockChainHeight > 1);
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-10-06 23:29:13 +02:00
|
|
|
bool WalletImpl::synchronized() const
|
|
|
|
{
|
|
|
|
return m_synchronized;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-03-31 15:38:57 +02:00
|
|
|
bool WalletImpl::refresh()
|
|
|
|
{
|
|
|
|
clearStatus();
|
2018-03-21 18:34:15 +01:00
|
|
|
//TODO: make doRefresh return bool to know whether the error occured during refresh or not
|
|
|
|
//otherwise one may try, say, to send transaction, transfer fails and this method returns false
|
2016-07-10 16:17:23 +02:00
|
|
|
doRefresh();
|
2020-10-26 19:20:00 +01:00
|
|
|
return good();
|
2016-03-31 15:38:57 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-07-10 16:17:23 +02:00
|
|
|
void WalletImpl::refreshAsync()
|
|
|
|
{
|
2016-12-04 14:13:54 +01:00
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": Refreshing asynchronously..");
|
2016-07-10 16:17:23 +02:00
|
|
|
clearStatus();
|
|
|
|
m_refreshCV.notify_one();
|
|
|
|
}
|
|
|
|
|
2021-07-07 23:10:29 +02:00
|
|
|
EXPORT
|
|
|
|
bool WalletImpl::isRefreshing(std::chrono::milliseconds max_wait) {
|
|
|
|
std::unique_lock lock{m_refreshMutex2, std::defer_lock};
|
|
|
|
return !lock.try_lock_for(max_wait);
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-10-16 13:58:22 +02:00
|
|
|
bool WalletImpl::rescanBlockchain()
|
|
|
|
{
|
|
|
|
clearStatus();
|
|
|
|
m_refreshShouldRescan = true;
|
|
|
|
doRefresh();
|
2020-10-26 19:20:00 +01:00
|
|
|
return good();
|
2018-10-16 13:58:22 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-10-16 13:58:22 +02:00
|
|
|
void WalletImpl::rescanBlockchainAsync()
|
|
|
|
{
|
|
|
|
m_refreshShouldRescan = true;
|
|
|
|
refreshAsync();
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-09-26 21:50:10 +02:00
|
|
|
void WalletImpl::setAutoRefreshInterval(int millis)
|
2016-09-20 19:40:58 +02:00
|
|
|
{
|
2016-09-27 23:35:10 +02:00
|
|
|
if (millis > MAX_REFRESH_INTERVAL_MILLIS) {
|
|
|
|
LOG_ERROR(__FUNCTION__<< ": invalid refresh interval " << millis
|
|
|
|
<< " ms, maximum allowed is " << MAX_REFRESH_INTERVAL_MILLIS << " ms");
|
|
|
|
m_refreshIntervalMillis = MAX_REFRESH_INTERVAL_MILLIS;
|
|
|
|
} else {
|
|
|
|
m_refreshIntervalMillis = millis;
|
|
|
|
}
|
2016-09-20 19:40:58 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-09-20 19:40:58 +02:00
|
|
|
int WalletImpl::autoRefreshInterval() const
|
|
|
|
{
|
2016-09-26 21:50:10 +02:00
|
|
|
return m_refreshIntervalMillis;
|
2016-09-20 19:40:58 +02:00
|
|
|
}
|
|
|
|
|
2020-10-26 19:20:00 +01:00
|
|
|
UnsignedTransaction* WalletImpl::loadUnsignedTx(std::string_view unsigned_filename_) {
|
|
|
|
auto unsigned_filename = fs::u8path(unsigned_filename_);
|
2017-01-08 13:17:09 +01:00
|
|
|
clearStatus();
|
2020-10-22 23:43:29 +02:00
|
|
|
UnsignedTransactionImpl* transaction = new UnsignedTransactionImpl(*this);
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!wallet()->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to load unsigned transactions"));
|
2020-10-26 19:20:00 +01:00
|
|
|
transaction->m_status = {UnsignedTransaction::Status::Status_Error, status().second};
|
2019-09-09 02:03:55 +02:00
|
|
|
|
|
|
|
return transaction;
|
2017-01-08 13:17:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check tx data and construct confirmation message
|
|
|
|
std::string extra_message;
|
2018-11-07 07:09:43 +01:00
|
|
|
if (!transaction->m_unsigned_tx_set.transfers.second.empty())
|
|
|
|
extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.second.size()).str();
|
2020-07-22 21:29:36 +02:00
|
|
|
transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const wallet::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message);
|
2020-10-26 19:20:00 +01:00
|
|
|
auto [code, msg] = transaction->status();
|
|
|
|
setStatus(code, std::move(msg));
|
|
|
|
|
2017-01-08 13:17:09 +01:00
|
|
|
return transaction;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::submitTransaction(std::string_view filename_) {
|
|
|
|
auto fileName = fs::u8path(filename_);
|
2017-01-08 13:17:09 +01:00
|
|
|
clearStatus();
|
2017-01-13 23:00:03 +01:00
|
|
|
std::unique_ptr<PendingTransactionImpl> transaction(new PendingTransactionImpl(*this));
|
2017-01-08 13:17:09 +01:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
bool r = wallet()->load_tx(fileName, transaction->m_pending_tx);
|
2017-01-08 13:17:09 +01:00
|
|
|
if (!r) {
|
2020-10-26 19:20:00 +01:00
|
|
|
setStatus(Status_Error, tr("Failed to load transaction from file"));
|
2017-01-08 13:17:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!transaction->commit()) {
|
2020-10-26 19:20:00 +01:00
|
|
|
setStatusError(transaction->status().second);
|
2017-01-08 13:17:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:00:03 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::exportKeyImages(std::string_view filename_)
|
2017-01-13 23:00:03 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
auto filename = fs::u8path(filename_);
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
if (w->watch_only())
|
2017-01-13 23:00:03 +01:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Wallet is view only"));
|
2017-01-13 23:00:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!w->export_key_images_to_file(filename, false /* requested_ki_only */))
|
2017-01-13 23:00:03 +01:00
|
|
|
{
|
2020-10-22 19:55:33 +02:00
|
|
|
setStatusError(tr("failed to save file ") + filename.u8string());
|
2017-01-13 23:00:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2017-03-18 13:56:07 +01:00
|
|
|
catch (const std::exception &e)
|
2017-01-13 23:00:03 +01:00
|
|
|
{
|
|
|
|
LOG_ERROR("Error exporting key images: " << e.what());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-01-13 23:00:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::importKeyImages(std::string_view filename_)
|
2017-01-13 23:00:03 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
auto filename = fs::u8path(filename_);
|
2017-08-18 14:29:34 +02:00
|
|
|
if (!trustedDaemon()) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Key images can only be imported with a trusted daemon"));
|
2017-08-18 14:29:34 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-01-13 23:00:03 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
uint64_t spent = 0, unspent = 0;
|
2021-06-23 03:08:12 +02:00
|
|
|
uint64_t height = wallet()->import_key_images_from_file(filename, spent, unspent);
|
2017-01-13 23:00:03 +01:00
|
|
|
LOG_PRINT_L2("Signed key images imported to height " << height << ", "
|
|
|
|
<< print_money(spent) << " spent, " << print_money(unspent) << " unspent");
|
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
|
|
|
LOG_ERROR("Error exporting key images: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to import key images: ")) + e.what());
|
2017-01-13 23:00:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
2017-01-08 13:17:09 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
void WalletImpl::addSubaddressAccount(const std::string& label)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->add_subaddress_account(label);
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
size_t WalletImpl::numSubaddressAccounts() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_num_subaddress_accounts();
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
size_t WalletImpl::numSubaddresses(uint32_t accountIndex) const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_num_subaddresses(accountIndex);
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
void WalletImpl::addSubaddress(uint32_t accountIndex, const std::string& label)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->add_subaddress(accountIndex, label);
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_subaddress_label({accountIndex, addressIndex});
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error getting subaddress label: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to get subaddress label: ")) + e.what());
|
2017-02-19 03:42:10 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->set_subaddress_label({accountIndex, addressIndex}, label);
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error setting subaddress label: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to set subaddress label: ")) + e.what());
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
MultisigState WalletImpl::multisig(LockedWallet& w) {
|
2018-03-21 16:57:15 +01:00
|
|
|
MultisigState state;
|
2021-06-23 03:08:12 +02:00
|
|
|
state.isMultisig = w->multisig(&state.isReady, &state.threshold, &state.total);
|
2018-03-21 16:57:15 +01:00
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
EXPORT
|
|
|
|
MultisigState WalletImpl::multisig() const {
|
|
|
|
auto w = wallet();
|
|
|
|
return multisig(w);
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
std::string WalletImpl::getMultisigInfo() const {
|
2018-03-21 16:57:15 +01:00
|
|
|
try {
|
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_multisig_info();
|
2020-10-22 23:43:29 +02:00
|
|
|
} catch (const std::exception& e) {
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error on generating multisig info: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to get multisig info: ")) + e.what());
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
2020-10-22 23:43:29 +02:00
|
|
|
return {};
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
std::string WalletImpl::makeMultisig(const std::vector<std::string>& info, uint32_t threshold) {
|
2018-03-21 16:57:15 +01:00
|
|
|
try {
|
|
|
|
clearStatus();
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
if (w->multisig())
|
2020-10-22 23:43:29 +02:00
|
|
|
throw std::runtime_error("Wallet is already multisig");
|
2018-03-21 16:57:15 +01:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
return w->make_multisig(epee::wipeable_string(m_password), info, threshold);
|
2020-10-22 23:43:29 +02:00
|
|
|
} catch (const std::exception& e) {
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error on making multisig wallet: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to make multisig: ")) + e.what());
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
2020-10-22 23:43:29 +02:00
|
|
|
return {};
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-07-12 11:55:52 +02:00
|
|
|
std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &info) {
|
|
|
|
try {
|
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
checkMultisigWalletNotReady(w);
|
2018-07-12 11:55:52 +02:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
return w->exchange_multisig_keys(epee::wipeable_string(m_password), info);
|
2020-10-22 23:43:29 +02:00
|
|
|
} catch (const std::exception& e) {
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error on exchanging multisig keys: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to make multisig: ")) + e.what());
|
2018-07-12 11:55:52 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 23:43:29 +02:00
|
|
|
return {};
|
2018-07-12 11:55:52 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
bool WalletImpl::finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) {
|
2018-03-21 16:57:15 +01:00
|
|
|
try {
|
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
checkMultisigWalletNotReady(w);
|
2018-03-21 16:57:15 +01:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
if (w->finalize_multisig(epee::wipeable_string(m_password), extraMultisigInfo)) {
|
2018-03-21 16:57:15 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
setStatusError(tr("Failed to finalize multisig wallet creation"));
|
2020-10-22 23:43:29 +02:00
|
|
|
} catch (const std::exception& e) {
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error on finalizing multisig wallet creation: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to finalize multisig wallet creation: ")) + e.what());
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
bool WalletImpl::exportMultisigImages(std::string& images) {
|
2018-03-21 16:57:15 +01:00
|
|
|
try {
|
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
checkMultisigWalletReady(w);
|
2018-03-21 16:57:15 +01:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
auto blob = w->export_multisig();
|
2021-01-14 21:28:50 +01:00
|
|
|
images = oxenmq::to_hex(blob);
|
2018-03-21 16:57:15 +01:00
|
|
|
return true;
|
2020-10-22 23:43:29 +02:00
|
|
|
} catch (const std::exception& e) {
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error on exporting multisig images: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to export multisig images: ")) + e.what());
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
size_t WalletImpl::importMultisigImages(const std::vector<std::string>& images) {
|
2018-03-21 16:57:15 +01:00
|
|
|
try {
|
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
checkMultisigWalletReady(w);
|
2018-03-21 16:57:15 +01:00
|
|
|
|
|
|
|
std::vector<std::string> blobs;
|
|
|
|
blobs.reserve(images.size());
|
|
|
|
|
|
|
|
for (const auto& image: images) {
|
|
|
|
std::string blob;
|
|
|
|
if (!epee::string_tools::parse_hexstr_to_binbuff(image, blob)) {
|
|
|
|
LOG_ERROR("Failed to parse imported multisig images");
|
|
|
|
setStatusError(tr("Failed to parse imported multisig images"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
blobs.emplace_back(std::move(blob));
|
|
|
|
}
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
return w->import_multisig(blobs);
|
2020-10-22 23:43:29 +02:00
|
|
|
} catch (const std::exception& e) {
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error on importing multisig images: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to import multisig images: ")) + e.what());
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-09-10 18:18:25 +02:00
|
|
|
bool WalletImpl::hasMultisigPartialKeyImages() const {
|
|
|
|
try {
|
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
checkMultisigWalletReady(w);
|
2018-09-10 18:18:25 +02:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
return w->has_multisig_partial_key_images();
|
2020-10-22 23:43:29 +02:00
|
|
|
} catch (const std::exception& e) {
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error on checking for partial multisig key images: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to check for partial multisig key images: ")) + e.what());
|
2018-09-10 18:18:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
PendingTransaction* WalletImpl::restoreMultisigTransaction(const std::string& signData) {
|
2018-03-21 16:57:15 +01:00
|
|
|
try {
|
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
checkMultisigWalletReady(w);
|
2018-03-21 16:57:15 +01:00
|
|
|
|
2020-10-22 23:43:29 +02:00
|
|
|
std::string binary;
|
|
|
|
if (!epee::string_tools::parse_hexstr_to_binbuff(signData, binary))
|
|
|
|
throw std::runtime_error("Failed to deserialize multisig transaction");
|
2018-03-21 16:57:15 +01:00
|
|
|
|
|
|
|
tools::wallet2::multisig_tx_set txSet;
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!w->load_multisig_tx(binary, txSet, {}))
|
2020-10-22 23:43:29 +02:00
|
|
|
throw std::runtime_error("couldn't parse multisig transaction data");
|
2018-03-21 16:57:15 +01:00
|
|
|
|
|
|
|
auto ptx = new PendingTransactionImpl(*this);
|
|
|
|
ptx->m_pending_tx = txSet.m_ptx;
|
|
|
|
ptx->m_signers = txSet.m_signers;
|
|
|
|
|
|
|
|
return ptx;
|
2020-10-22 23:43:29 +02:00
|
|
|
} catch (std::exception& e) {
|
2019-01-07 12:56:50 +01:00
|
|
|
LOG_ERROR("Error on restoring multisig transaction: " << e.what());
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("Failed to restore multisig transaction: ")) + e.what());
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-04-05 14:24:44 +02:00
|
|
|
// TODO:
|
2020-10-23 22:32:28 +02:00
|
|
|
// - check / design how "Transaction" can be single interface
|
2016-04-05 14:24:44 +02:00
|
|
|
// (instead of few different data structures within wallet2 implementation:
|
|
|
|
// - pending_tx;
|
|
|
|
// - transfer_details;
|
|
|
|
// - payment_details;
|
|
|
|
// - unconfirmed_transfer_details;
|
|
|
|
// - confirmed_transfer_details)
|
2016-06-23 15:23:09 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-23 22:32:28 +02:00
|
|
|
PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<std::string> &dst_addr, std::optional<std::vector<uint64_t>> amount, uint32_t priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
|
2016-06-27 13:55:13 +02:00
|
|
|
|
2016-04-03 13:34:38 +02:00
|
|
|
{
|
|
|
|
clearStatus();
|
2016-11-08 13:20:42 +01:00
|
|
|
// Pause refresh thread while creating transaction
|
|
|
|
pauseRefresh();
|
2017-01-08 13:17:09 +01:00
|
|
|
|
2017-02-19 03:42:10 +01:00
|
|
|
cryptonote::address_parse_info info;
|
2016-04-12 21:30:20 +02:00
|
|
|
|
2016-06-23 13:38:22 +02:00
|
|
|
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
|
2016-04-03 13:34:38 +02:00
|
|
|
|
2016-04-05 14:24:44 +02:00
|
|
|
do {
|
2019-05-03 03:50:42 +02:00
|
|
|
std::vector<uint8_t> extra;
|
|
|
|
std::string extra_nonce;
|
2020-10-22 23:43:29 +02:00
|
|
|
std::vector<cryptonote::tx_destination_entry> dsts;
|
2019-05-03 03:50:42 +02:00
|
|
|
if (!amount && dst_addr.size() > 1) {
|
|
|
|
setStatusError(tr("Sending all requires one destination address"));
|
2016-04-05 14:24:44 +02:00
|
|
|
break;
|
|
|
|
}
|
2019-05-03 03:50:42 +02:00
|
|
|
if (amount && (dst_addr.size() != (*amount).size())) {
|
|
|
|
setStatusError(tr("Destinations and amounts are unequal"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bool error = false;
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
2019-05-03 03:50:42 +02:00
|
|
|
for (size_t i = 0; i < dst_addr.size() && !error; i++) {
|
2021-06-23 03:08:12 +02:00
|
|
|
if(!cryptonote::get_account_address_from_str(info, m_wallet_ptr->nettype(), dst_addr[i])) {
|
2019-05-03 03:50:42 +02:00
|
|
|
// TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
|
|
|
|
setStatusError(tr("Invalid destination address"));
|
|
|
|
error = true;
|
2016-11-18 20:13:43 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-05-03 03:50:42 +02:00
|
|
|
if (info.has_payment_id) {
|
|
|
|
if (!extra_nonce.empty()) {
|
|
|
|
setStatusError(tr("a single transaction cannot use more than one payment id"));
|
|
|
|
error = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
|
|
|
|
}
|
2016-06-23 13:38:22 +02:00
|
|
|
|
2016-11-09 13:19:22 +01:00
|
|
|
if (amount) {
|
|
|
|
cryptonote::tx_destination_entry de;
|
2019-05-03 03:50:42 +02:00
|
|
|
de.original = dst_addr[i];
|
2017-02-19 03:42:10 +01:00
|
|
|
de.addr = info.address;
|
2019-05-03 03:50:42 +02:00
|
|
|
de.amount = (*amount)[i];
|
2017-02-19 03:42:10 +01:00
|
|
|
de.is_subaddress = info.is_subaddress;
|
2018-12-23 15:31:54 +01:00
|
|
|
de.is_integrated = info.has_payment_id;
|
2016-11-09 13:19:22 +01:00
|
|
|
dsts.push_back(de);
|
2020-02-11 07:36:34 +01:00
|
|
|
|
2016-11-09 13:19:22 +01:00
|
|
|
} else {
|
2019-05-03 03:50:42 +02:00
|
|
|
if (subaddr_indices.empty()) {
|
2021-06-23 03:08:12 +02:00
|
|
|
for (uint32_t index = 0; index < w->get_num_subaddresses(subaddr_account); ++index)
|
2017-02-19 03:42:10 +01:00
|
|
|
subaddr_indices.insert(index);
|
|
|
|
}
|
2016-11-09 13:19:22 +01:00
|
|
|
}
|
2019-05-03 03:50:42 +02:00
|
|
|
}
|
|
|
|
if (error) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
|
|
|
|
setStatusError(tr("failed to set up payment id, though it was decoded correctly"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
std::optional<uint8_t> hf_version = w->get_hard_fork_version();
|
2020-04-20 09:25:53 +02:00
|
|
|
if (!hf_version)
|
|
|
|
{
|
2021-09-02 18:59:49 +02:00
|
|
|
setStatusError(tools::wallet2::ERR_MSG_NETWORK_VERSION_QUERY_FAILED);
|
2020-04-20 09:25:53 +02:00
|
|
|
return transaction;
|
|
|
|
}
|
2016-04-05 14:24:44 +02:00
|
|
|
|
2019-05-03 03:50:42 +02:00
|
|
|
if (amount) {
|
2021-01-04 01:09:45 +01:00
|
|
|
oxen_construct_tx_params tx_params = tools::wallet2::construct_params(*hf_version, txtype::standard, priority);
|
2021-06-23 03:08:12 +02:00
|
|
|
transaction->m_pending_tx = w->create_transactions_2(
|
|
|
|
dsts, CRYPTONOTE_DEFAULT_TX_MIXIN, 0 /* unlock_time */,
|
|
|
|
priority,
|
|
|
|
extra, subaddr_account, subaddr_indices, tx_params);
|
2019-05-03 03:50:42 +02:00
|
|
|
} else {
|
2021-06-23 03:08:12 +02:00
|
|
|
transaction->m_pending_tx = w->create_transactions_all(
|
|
|
|
0, info.address, info.is_subaddress, 1, CRYPTONOTE_DEFAULT_TX_MIXIN, 0 /* unlock_time */,
|
|
|
|
priority,
|
|
|
|
extra, subaddr_account, subaddr_indices);
|
2016-11-09 13:19:22 +01:00
|
|
|
}
|
2019-02-23 15:28:18 +01:00
|
|
|
pendingTxPostProcess(transaction);
|
|
|
|
|
2018-03-21 16:57:15 +01:00
|
|
|
if (multisig().isMultisig) {
|
2021-06-23 03:08:12 +02:00
|
|
|
auto tx_set = w->make_multisig_tx_set(transaction->m_pending_tx);
|
2019-03-11 17:19:50 +01:00
|
|
|
transaction->m_pending_tx = tx_set.m_ptx;
|
|
|
|
transaction->m_signers = tx_set.m_signers;
|
2018-03-21 16:57:15 +01:00
|
|
|
}
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::daemon_busy&) {
|
|
|
|
// TODO: make it translatable with "tr"?
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("daemon is busy. Please try again later."));
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::no_connection_to_daemon&) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::wallet_rpc_error& e) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("RPC error: ") + e.to_string());
|
2018-08-13 18:18:32 +02:00
|
|
|
} catch (const tools::error::get_outs_error &e) {
|
|
|
|
setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str());
|
2017-10-16 15:13:23 +02:00
|
|
|
} catch (const tools::error::not_enough_unlocked_money& e) {
|
2016-06-20 21:56:30 +02:00
|
|
|
std::ostringstream writer;
|
2016-04-05 14:24:44 +02:00
|
|
|
|
2016-10-15 15:30:50 +02:00
|
|
|
writer << boost::format(tr("not enough money to transfer, available only %s, sent amount %s")) %
|
|
|
|
print_money(e.available()) %
|
|
|
|
print_money(e.tx_amount());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(writer.str());
|
2017-10-16 15:13:23 +02:00
|
|
|
} catch (const tools::error::not_enough_money& e) {
|
|
|
|
std::ostringstream writer;
|
|
|
|
|
|
|
|
writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) %
|
|
|
|
print_money(e.available()) %
|
|
|
|
print_money(e.tx_amount());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(writer.str());
|
2016-10-15 15:30:50 +02:00
|
|
|
} catch (const tools::error::tx_not_possible& e) {
|
|
|
|
std::ostringstream writer;
|
|
|
|
|
2016-04-05 14:24:44 +02:00
|
|
|
writer << boost::format(tr("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)")) %
|
|
|
|
print_money(e.available()) %
|
|
|
|
print_money(e.tx_amount() + e.fee()) %
|
|
|
|
print_money(e.tx_amount()) %
|
|
|
|
print_money(e.fee());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(writer.str());
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::not_enough_outs_to_mix& e) {
|
2016-06-20 21:56:30 +02:00
|
|
|
std::ostringstream writer;
|
2017-07-31 08:50:41 +02:00
|
|
|
writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
|
2016-08-02 22:48:09 +02:00
|
|
|
for (const std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) {
|
2017-07-31 08:50:41 +02:00
|
|
|
writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
|
2016-04-05 14:24:44 +02:00
|
|
|
}
|
2018-01-31 02:37:54 +01:00
|
|
|
writer << "\n" << tr("Please sweep unmixable outputs.");
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(writer.str());
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::tx_not_constructed&) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("transaction was not constructed"));
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::tx_rejected& e) {
|
2016-06-20 21:56:30 +02:00
|
|
|
std::ostringstream writer;
|
2016-04-05 14:24:44 +02:00
|
|
|
writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(writer.str());
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::tx_sum_overflow& e) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::zero_destination&) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("one of destinations is zero"));
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::tx_too_big& e) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("failed to find a suitable way to split transactions"));
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::transfer_error& e) {
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("unknown transfer error: ")) + e.what());
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const tools::error::wallet_internal_error& e) {
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("internal error: ")) + e.what());
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (const std::exception& e) {
|
2020-10-22 23:43:29 +02:00
|
|
|
setStatusError(std::string(tr("unexpected error: ")) + e.what());
|
2016-04-05 14:24:44 +02:00
|
|
|
} catch (...) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("unknown error"));
|
2016-04-05 14:24:44 +02:00
|
|
|
}
|
|
|
|
} while (false);
|
|
|
|
|
2020-10-26 19:20:00 +01:00
|
|
|
transaction->m_status = status();
|
2016-11-08 13:20:42 +01:00
|
|
|
// Resume refresh thread
|
|
|
|
startRefresh();
|
2016-04-05 14:24:44 +02:00
|
|
|
return transaction;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-23 22:32:28 +02:00
|
|
|
PendingTransaction *WalletImpl::createTransaction(const std::string &dst_addr, std::optional<uint64_t> amount,
|
2020-04-20 09:25:53 +02:00
|
|
|
uint32_t priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
|
2019-05-03 03:50:42 +02:00
|
|
|
|
|
|
|
{
|
2020-10-23 22:32:28 +02:00
|
|
|
return createTransactionMultDest(std::vector<std::string> {dst_addr}, amount ? (std::vector<uint64_t> {*amount}) : (std::optional<std::vector<uint64_t>>()), priority, subaddr_account, subaddr_indices);
|
2019-05-03 03:50:42 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-11-08 21:03:07 +01:00
|
|
|
PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
|
|
|
|
|
|
|
|
{
|
|
|
|
clearStatus();
|
|
|
|
cryptonote::tx_destination_entry de;
|
|
|
|
|
|
|
|
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
try {
|
|
|
|
transaction->m_pending_tx = wallet()->create_unmixable_sweep_transactions();
|
|
|
|
pendingTxPostProcess(transaction);
|
|
|
|
|
|
|
|
} catch (const tools::error::daemon_busy&) {
|
|
|
|
// TODO: make it translatable with "tr"?
|
|
|
|
setStatusError(tr("daemon is busy. Please try again later."));
|
|
|
|
} catch (const tools::error::no_connection_to_daemon&) {
|
|
|
|
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
|
|
|
|
} catch (const tools::error::wallet_rpc_error& e) {
|
|
|
|
setStatusError(tr("RPC error: ") + e.to_string());
|
|
|
|
} catch (const tools::error::get_outs_error&) {
|
|
|
|
setStatusError(tr("failed to get outputs to mix"));
|
|
|
|
} catch (const tools::error::not_enough_unlocked_money& e) {
|
|
|
|
setStatusError("");
|
|
|
|
std::ostringstream writer;
|
|
|
|
|
|
|
|
writer << boost::format(tr("not enough money to transfer, available only %s, sent amount %s")) %
|
|
|
|
print_money(e.available()) %
|
|
|
|
print_money(e.tx_amount());
|
|
|
|
setStatusError(writer.str());
|
|
|
|
} catch (const tools::error::not_enough_money& e) {
|
|
|
|
setStatusError("");
|
|
|
|
std::ostringstream writer;
|
|
|
|
|
|
|
|
writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) %
|
|
|
|
print_money(e.available()) %
|
|
|
|
print_money(e.tx_amount());
|
|
|
|
setStatusError(writer.str());
|
|
|
|
} catch (const tools::error::tx_not_possible& e) {
|
|
|
|
setStatusError("");
|
|
|
|
std::ostringstream writer;
|
|
|
|
|
|
|
|
writer << boost::format(tr("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)")) %
|
|
|
|
print_money(e.available()) %
|
|
|
|
print_money(e.tx_amount() + e.fee()) %
|
|
|
|
print_money(e.tx_amount()) %
|
|
|
|
print_money(e.fee());
|
|
|
|
setStatusError(writer.str());
|
|
|
|
} catch (const tools::error::not_enough_outs_to_mix& e) {
|
|
|
|
std::ostringstream writer;
|
|
|
|
writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
|
|
|
|
for (const std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) {
|
|
|
|
writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
|
2016-04-05 14:24:44 +02:00
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
setStatusError(writer.str());
|
|
|
|
} catch (const tools::error::tx_not_constructed&) {
|
|
|
|
setStatusError(tr("transaction was not constructed"));
|
|
|
|
} catch (const tools::error::tx_rejected& e) {
|
|
|
|
std::ostringstream writer;
|
|
|
|
writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
|
|
|
|
setStatusError(writer.str());
|
|
|
|
} catch (const tools::error::tx_sum_overflow& e) {
|
|
|
|
setStatusError(e.what());
|
|
|
|
} catch (const tools::error::zero_destination&) {
|
|
|
|
setStatusError(tr("one of destinations is zero"));
|
|
|
|
} catch (const tools::error::tx_too_big& e) {
|
|
|
|
setStatusError(tr("failed to find a suitable way to split transactions"));
|
|
|
|
} catch (const tools::error::transfer_error& e) {
|
|
|
|
setStatusError(std::string(tr("unknown transfer error: ")) + e.what());
|
|
|
|
} catch (const tools::error::wallet_internal_error& e) {
|
|
|
|
setStatusError(std::string(tr("internal error: ")) + e.what());
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
setStatusError(std::string(tr("unexpected error: ")) + e.what());
|
|
|
|
} catch (...) {
|
|
|
|
setStatusError(tr("unknown error"));
|
2020-10-26 19:20:00 +01:00
|
|
|
}
|
2016-04-05 14:24:44 +02:00
|
|
|
|
2020-10-26 19:20:00 +01:00
|
|
|
transaction->m_status = status();
|
2016-04-05 14:24:44 +02:00
|
|
|
return transaction;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-04-05 14:24:44 +02:00
|
|
|
void WalletImpl::disposeTransaction(PendingTransaction *t)
|
|
|
|
{
|
|
|
|
delete t;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-11-10 19:48:26 +01:00
|
|
|
uint64_t WalletImpl::estimateTransactionFee(uint32_t priority, uint32_t recipients) const
|
|
|
|
{
|
|
|
|
constexpr uint32_t typical_size = 2000;
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
const auto base_fee = w->get_base_fees();
|
|
|
|
uint64_t pct = w->get_fee_percent(priority == 1 ? 1 : 5, txtype::standard);
|
2020-11-10 19:48:26 +01:00
|
|
|
return (base_fee.first * typical_size + base_fee.second * (recipients + 1)) * pct / 100;
|
2020-01-22 17:30:47 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
TransactionHistory *WalletImpl::history()
|
2016-04-19 11:25:03 +02:00
|
|
|
{
|
2018-09-29 22:11:32 +02:00
|
|
|
return m_history.get();
|
2016-04-19 11:25:03 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
AddressBook *WalletImpl::addressBook()
|
2016-12-12 00:42:46 +01:00
|
|
|
{
|
2018-09-29 22:11:32 +02:00
|
|
|
return m_addressBook.get();
|
2016-12-12 00:42:46 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
Subaddress *WalletImpl::subaddress()
|
|
|
|
{
|
2018-09-29 22:11:32 +02:00
|
|
|
return m_subaddress.get();
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-02-19 03:42:10 +01:00
|
|
|
SubaddressAccount *WalletImpl::subaddressAccount()
|
|
|
|
{
|
2018-09-29 22:11:32 +02:00
|
|
|
return m_subaddressAccount.get();
|
2017-02-19 03:42:10 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-05-05 21:24:00 +02:00
|
|
|
void WalletImpl::setListener(WalletListener *l)
|
|
|
|
{
|
|
|
|
// TODO thread synchronization;
|
|
|
|
m_wallet2Callback->setListener(l);
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2019-05-10 17:45:44 +02:00
|
|
|
bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->set_attribute(key, val);
|
2019-05-10 17:45:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2019-05-10 17:45:44 +02:00
|
|
|
std::string WalletImpl::getCacheAttribute(const std::string &key) const
|
|
|
|
{
|
2019-07-28 16:03:45 +02:00
|
|
|
std::string value;
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->get_attribute(key, value);
|
2019-07-28 16:03:45 +02:00
|
|
|
return value;
|
2019-05-10 17:45:44 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-11-05 21:53:45 +01:00
|
|
|
bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e)
|
|
|
|
{
|
|
|
|
cryptonote::blobdata txid_data;
|
2017-02-14 20:35:44 +01:00
|
|
|
if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
|
2016-11-05 21:53:45 +01:00
|
|
|
return false;
|
|
|
|
const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->set_tx_note(htxid, note);
|
2016-11-05 21:53:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-11-05 21:53:45 +01:00
|
|
|
std::string WalletImpl::getUserNote(const std::string &txid) const
|
|
|
|
{
|
|
|
|
cryptonote::blobdata txid_data;
|
2017-02-14 20:35:44 +01:00
|
|
|
if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
|
2016-11-05 21:53:45 +01:00
|
|
|
return "";
|
|
|
|
const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_tx_note(htxid);
|
2016-11-05 21:53:45 +01:00
|
|
|
}
|
2016-05-05 21:24:00 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-09-12 03:05:41 +02:00
|
|
|
std::string WalletImpl::getTxKey(const std::string &txid_str) const
|
2016-11-06 19:04:59 +01:00
|
|
|
{
|
2017-09-12 03:05:41 +02:00
|
|
|
crypto::hash txid;
|
2020-10-23 22:32:28 +02:00
|
|
|
if(!tools::hex_to_type(txid_str, txid))
|
2016-11-06 19:04:59 +01:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse txid"));
|
2017-09-12 03:05:41 +02:00
|
|
|
return "";
|
2016-11-06 19:04:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
crypto::secret_key tx_key;
|
2017-02-19 03:42:10 +01:00
|
|
|
std::vector<crypto::secret_key> additional_tx_keys;
|
2019-06-19 12:07:50 +02:00
|
|
|
try
|
2016-11-06 19:04:59 +01:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
if (wallet()->get_tx_key(txid, tx_key, additional_tx_keys))
|
2019-06-19 12:07:50 +02:00
|
|
|
{
|
|
|
|
clearStatus();
|
|
|
|
std::ostringstream oss;
|
2020-10-23 22:32:28 +02:00
|
|
|
oss << tools::type_to_hex(tx_key);
|
2019-06-19 12:07:50 +02:00
|
|
|
for (size_t i = 0; i < additional_tx_keys.size(); ++i)
|
2020-10-23 22:32:28 +02:00
|
|
|
oss << tools::type_to_hex(additional_tx_keys[i]);
|
2019-06-19 12:07:50 +02:00
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setStatusError(tr("no tx keys found for this txid"));
|
|
|
|
return "";
|
|
|
|
}
|
2016-11-06 19:04:59 +01:00
|
|
|
}
|
2019-06-19 12:07:50 +02:00
|
|
|
catch (const std::exception &e)
|
2016-11-06 19:04:59 +01:00
|
|
|
{
|
2019-06-19 12:07:50 +02:00
|
|
|
setStatusError(e.what());
|
2017-09-12 03:05:41 +02:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-23 22:32:28 +02:00
|
|
|
bool WalletImpl::checkTxKey(const std::string &txid_str, std::string_view tx_key_str, const std::string &address_str, uint64_t &received, bool &in_pool, uint64_t &confirmations)
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
|
|
|
crypto::hash txid;
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(txid_str, txid))
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse txid"));
|
2017-09-12 03:05:41 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
crypto::secret_key tx_key;
|
|
|
|
std::vector<crypto::secret_key> additional_tx_keys;
|
2020-10-23 22:32:28 +02:00
|
|
|
bool first = true;
|
|
|
|
while (first || !tx_key_str.empty())
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
2020-10-23 22:32:28 +02:00
|
|
|
auto& key = first ? tx_key : additional_tx_keys.emplace_back();
|
|
|
|
if (first) first = false;
|
|
|
|
|
|
|
|
if (!tools::hex_to_type(tx_key_str.substr(0, 64), key))
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse tx key"));
|
2017-09-12 03:05:41 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-10-23 22:32:28 +02:00
|
|
|
tx_key_str.remove_prefix(64);
|
2017-09-12 03:05:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cryptonote::address_parse_info info;
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_wallet_ptr->nettype(), address_str))
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse address"));
|
2017-09-12 03:05:41 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->check_tx_key(txid, tx_key, additional_tx_keys, info.address, received, in_pool, confirmations);
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2017-09-12 03:05:41 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-09-12 03:05:41 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-11-20 10:10:58 +01:00
|
|
|
std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
|
|
|
crypto::hash txid;
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(txid_str, txid))
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse txid"));
|
2017-09-12 03:05:41 +02:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
cryptonote::address_parse_info info;
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_wallet_ptr->nettype(), address_str))
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse address"));
|
2017-09-12 03:05:41 +02:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_tx_proof(txid, info.address, info.is_subaddress, message);
|
2017-09-12 03:05:41 +02:00
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-09-12 03:05:41 +02:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-09-12 03:05:41 +02:00
|
|
|
bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations)
|
|
|
|
{
|
|
|
|
crypto::hash txid;
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(txid_str, txid))
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse txid"));
|
2017-09-12 03:05:41 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
cryptonote::address_parse_info info;
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_wallet_ptr->nettype(), address_str))
|
2017-09-12 03:05:41 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse address"));
|
2017-09-12 03:05:41 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
good = wallet()->check_tx_proof(txid, info.address, info.is_subaddress, message, signature, received, in_pool, confirmations);
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2017-09-12 03:05:41 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-09-12 03:05:41 +02:00
|
|
|
return false;
|
2016-11-06 19:04:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-08-28 17:34:17 +02:00
|
|
|
std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const {
|
|
|
|
crypto::hash txid;
|
2020-10-23 22:32:28 +02:00
|
|
|
if(!tools::hex_to_type(txid_str, txid))
|
2017-08-28 17:34:17 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse txid"));
|
2017-08-28 17:34:17 +02:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_spend_proof(txid, message);
|
2017-08-28 17:34:17 +02:00
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-08-28 17:34:17 +02:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-08-28 17:34:17 +02:00
|
|
|
bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string &message, const std::string &signature, bool &good) const {
|
|
|
|
good = false;
|
|
|
|
crypto::hash txid;
|
2020-10-23 22:32:28 +02:00
|
|
|
if(!tools::hex_to_type(txid_str, txid))
|
2017-08-28 17:34:17 +02:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse txid"));
|
2017-08-28 17:34:17 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
good = wallet()->check_spend_proof(txid, message, signature);
|
2017-08-28 17:34:17 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-08-28 17:34:17 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-12-28 14:50:10 +01:00
|
|
|
std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const {
|
|
|
|
try
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2020-06-02 00:30:19 +02:00
|
|
|
std::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
|
2017-12-28 14:50:10 +01:00
|
|
|
if (!all)
|
|
|
|
{
|
|
|
|
account_minreserve = std::make_pair(account_index, amount);
|
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->get_reserve_proof(account_minreserve, message);
|
2017-12-28 14:50:10 +01:00
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-12-28 14:50:10 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-12-28 14:50:10 +01:00
|
|
|
bool WalletImpl::checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const {
|
|
|
|
cryptonote::address_parse_info info;
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_wallet_ptr->nettype(), address))
|
2017-12-28 14:50:10 +01:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse address"));
|
2017-12-28 14:50:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (info.is_subaddress)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Address must not be a subaddress"));
|
2017-12-28 14:50:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
good = false;
|
|
|
|
try
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2021-06-23 03:08:12 +02:00
|
|
|
good = wallet()->check_reserve_proof(info.address, message, signature, total, spent);
|
2017-12-28 14:50:10 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (const std::exception &e)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-12-28 14:50:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-11-08 10:58:46 +01:00
|
|
|
std::string WalletImpl::signMessage(const std::string &message)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->sign(message);
|
2016-11-08 10:58:46 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-11-08 10:58:46 +01:00
|
|
|
bool WalletImpl::verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const
|
|
|
|
{
|
2017-02-19 03:42:10 +01:00
|
|
|
cryptonote::address_parse_info info;
|
2016-11-08 10:58:46 +01:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_wallet_ptr->nettype(), address))
|
2016-11-08 10:58:46 +01:00
|
|
|
return false;
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->verify(message, info.address, signature);
|
2016-11-08 10:58:46 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-04-10 17:38:54 +02:00
|
|
|
std::string WalletImpl::signMultisigParticipant(const std::string &message) const
|
|
|
|
{
|
|
|
|
clearStatus();
|
|
|
|
|
|
|
|
bool ready = false;
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
if (!w->multisig(&ready) || !ready) {
|
2020-10-26 19:20:00 +01:00
|
|
|
setStatusError(tr("The wallet must be in multisig ready state"));
|
2018-04-10 17:38:54 +02:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
return w->sign_multisig_participant(message);
|
2018-04-10 17:38:54 +02:00
|
|
|
} catch (const std::exception& e) {
|
2020-10-26 19:20:00 +01:00
|
|
|
setStatusError(e.what());
|
2018-04-10 17:38:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-04-10 17:38:54 +02:00
|
|
|
bool WalletImpl::verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const
|
|
|
|
{
|
|
|
|
clearStatus();
|
|
|
|
|
|
|
|
cryptonote::blobdata pkeyData;
|
|
|
|
if(!epee::string_tools::parse_hexstr_to_binbuff(publicKey, pkeyData) || pkeyData.size() != sizeof(crypto::public_key))
|
2020-10-26 19:20:00 +01:00
|
|
|
return setStatusError(tr("Given string is not a key"));
|
2018-04-10 17:38:54 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
crypto::public_key pkey = *reinterpret_cast<const crypto::public_key*>(pkeyData.data());
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->verify_with_public_key(message, pkey, signature);
|
2018-04-10 17:38:54 +02:00
|
|
|
} catch (const std::exception& e) {
|
2020-10-26 19:20:00 +01:00
|
|
|
return setStatusError(e.what());
|
2018-04-10 17:38:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-04-05 14:24:44 +02:00
|
|
|
bool WalletImpl::connectToDaemon()
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
bool result = w->check_connection(NULL, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS);
|
2016-04-05 14:24:44 +02:00
|
|
|
if (!result) {
|
2021-06-23 03:08:12 +02:00
|
|
|
setStatusError("Error connecting to daemon at " + w->get_daemon_address());
|
2016-07-10 16:17:23 +02:00
|
|
|
} else {
|
2018-03-21 18:34:15 +01:00
|
|
|
clearStatus();
|
2016-07-10 16:17:23 +02:00
|
|
|
// start refreshing here
|
2016-04-03 13:34:38 +02:00
|
|
|
}
|
2016-04-05 14:24:44 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-11-07 13:00:29 +01:00
|
|
|
Wallet::ConnectionStatus WalletImpl::connected() const
|
|
|
|
{
|
RPC overhaul
High-level details:
This redesigns the RPC layer to make it much easier to work with,
decouples it from an embedded HTTP server, and gets the vast majority of
the RPC serialization and dispatch code out of a very commonly included
header.
There is unfortunately rather a lot of interconnected code here that
cannot be easily separated out into separate commits. The full details
of what happens here are as follows:
Major details:
- All of the RPC code is now in a `cryptonote::rpc` namespace; this
renames quite a bit to be less verbose: e.g. CORE_RPC_STATUS_OK
becomes `rpc::STATUS_OK`, and `cryptonote::COMMAND_RPC_SOME_LONG_NAME`
becomes `rpc::SOME_LONG_NAME` (or just SOME_LONG_NAME for code already
working in the `rpc` namespace).
- `core_rpc_server` is now completely decoupled from providing any
request protocol: it is now *just* the core RPC call handler.
- The HTTP RPC interface now lives in a new rpc/http_server.h; this code
handles listening for HTTP requests and dispatching them to
core_rpc_server, then sending the results back to the caller.
- There is similarly a rpc/lmq_server.h for LMQ RPC code; more details
on this (and other LMQ specifics) below.
- RPC implementing code now returns the response object and throws when
things go wrong which simplifies much of the rpc error handling. They
can throw anything; generic exceptions get logged and a generic
"internal error" message gets returned to the caller, but there is
also an `rpc_error` class to return an error code and message used by
some json-rpc commands.
- RPC implementing functions now overload `core_rpc_server::invoke`
following the pattern:
RPC_BLAH_BLAH::response core_rpc_server::invoke(RPC_BLAH_BLAH::request&& req, rpc_context context);
This overloading makes the code vastly simpler: all instantiations are
now done with a small amount of generic instantiation code in a single
.cpp rather than needing to go to hell and back with a nest of epee
macros in a core header.
- each RPC endpoint is now defined by the RPC types themselves,
including its accessible names and permissions, in
core_rpc_server_commands_defs.h:
- every RPC structure now has a static `names()` function that returns
the names by which the end point is accessible. (The first one is
the primary, the others are for deprecated aliases).
- RPC command wrappers define their permissions and type by inheriting
from special tag classes:
- rpc::RPC_COMMAND is a basic, admin-only, JSON command, available
via JSON RPC. *All* JSON commands are now available via JSON RPC,
instead of the previous mix of some being at /foo and others at
/json_rpc. (Ones that were previously at /foo are still there for
backwards compatibility; see `rpc::LEGACY` below).
- rpc::PUBLIC specifies that the command should be available via a
restricted RPC connection.
- rpc::BINARY specifies that the command is not JSON, but rather is
accessible as /name and takes and returns values in the magic epee
binary "portable storage" (lol) data format.
- rpc::LEGACY specifies that the command should be available via the
non-json-rpc interface at `/name` for backwards compatibility (in
addition to the JSON-RPC interface).
- some epee serialization got unwrapped and de-templatized so that it
can be moved into a .cpp file with just declarations in the .h. (This
makes a *huge* difference for core_rpc_server_commands_defs.h and for
every compilation unit that includes it which previously had to
compile all the serialization code and then throw all by one copy away
at link time). This required some new macros so as to not break a ton
of places that will use the old way putting everything in the headers;
The RPC code uses this as does a few other places; there are comments
in contrib/epee/include/serialization/keyvalue_serialization.h as to
how to use it.
- Detemplatized a bunch of epee/storages code. Most of it should have
have been using templates at all (because it can only ever be called
with one type!), and now it isn't. This broke some things that didn't
properly compile because of missing headers or (in one case) a messed
up circular dependency.
- Significantly simplified a bunch of over-templatized serialization
code.
- All RPC serialization definitions is now out of
core_rpc_server_commands_defs.h and into a single .cpp file
(core_rpc_server_commands_defs.cpp).
- core RPC no longer uses the disgusting
BEGIN_URI_MAP2/MAP_URI_BLAH_BLAH macros. This was a terrible design
that forced slamming tons of code into a common header that didn't
need to be there.
- epee::struct_init is gone. It was a horrible hack that instiated
multiple templates just so the coder could be so lazy and write
`some_type var;` instead of properly value initializing with
`some_type var{};`.
- Removed a bunch of useless crap from epee. In particular, forcing
extra template instantiations all over the place in order to nest
return objects inside JSON RPC values is no longer needed, as are a
bunch of stuff related to the above de-macroization of the code.
- get_all_service_nodes, get_service_nodes, and get_n_service_nodes are
now combined into a single `get_service_nodes` (with deprecated
aliases for the others), which eliminates a fair amount of
duplication. The biggest obstacle here was getting the requested
fields reference passed through: this is now done by a new ability to
stash a context in the serialization object that can be retrieved by a
sub-serialized type.
LMQ-specifics:
- The LokiMQ instance moves into `cryptonote::core` rather than being
inside cryptonote_protocol. Currently the instance is used both for
qnet and rpc calls (and so needs to be in a common place), but I also
intend future PRs to use the batching code for job processing
(replacing the current threaded job queue).
- rpc/lmq_server.h handles the actual LMQ-request-to-core-RPC glue.
Unlike http_server it isn't technically running the whole LMQ stack
from here, but the parallel name with http_server seemed appropriate.
- All RPC endpoints are supported by LMQ under the same names as defined
generically, but prefixed with `rpc.` for public commands and `admin.`
for restricted ones.
- service node keys are now always available, even when not running in
`--service-node` mode: this is because we want the x25519 key for
being able to offer CURVE encryption for lmq RPC end-points, and
because it doesn't hurt to have them available all the time. In the
RPC layer this is now called "get_service_keys" (with
"get_service_node_key" as an alias) since they aren't strictly only
for service nodes. This also means code needs to check
m_service_node, and not m_service_node_keys, to tell if it is running
as a service node. (This is also easier to notice because
m_service_node_keys got renamed to `m_service_keys`).
- Added block and mempool monitoring LMQ RPC endpoints: `sub.block` and
`sub.mempool` subscribes the connection for new block and new mempool
TX notifications. The latter can notify on just blink txes, or all
new mempool txes (but only new ones -- txes dumped from a block don't
trigger it). The client gets pushed a [`notify.block`, `height`,
`hash`] or [`notify.tx`, `txhash`, `blob`] message when something
arrives.
Minor details:
- rpc::version_t is now a {major,minor} pair. Forcing everyone to pack
and unpack a uint32_t was gross.
- Changed some macros to constexprs (e.g. CORE_RPC_ERROR_CODE_...).
(This immediately revealed a couple of bugs in the RPC code that was
assigning CORE_RPC_ERROR_CODE_... to a string, and it worked because
the macro allows implicit conversion to a char).
- De-templatizing useless templates in epee (i.e. a bunch of templated
types that were never invoked with different types) revealed a painful
circular dependency between epee and non-epee code for tor_address and
i2p_address. This crap is now handled in a suitably named
`net/epee_network_address_hack.cpp` hack because it really isn't
trivial to extricate this mess.
- Removed `epee/include/serialization/serialize_base.h`. Amazingly the
code somehow still all works perfectly with this previously vital
header removed.
- Removed bitrotted, unused epee "crypted_storage" and
"gzipped_inmemstorage" code.
- Replaced a bunch of epee::misc_utils::auto_scope_leave_caller with
LOKI_DEFERs. The epee version involves quite a bit more instantiation
and is ugly as sin. Also made the `loki::defer` class invokable for
some edge cases that need calling before destruction in particular
conditions.
- Moved the systemd code around; it makes much more sense to do the
systemd started notification as in daemon.cpp as late as possible
rather than in core (when we can still have startup failures, e.g. if
the RPC layer can't start).
- Made the systemd short status string available in the get_info RPC
(and no longer require building with systemd).
- during startup, print (only) the x25519 when not in SN mode, and
continue to print all three when in SN mode.
- DRYed out some RPC implementation code (such as set_limit)
- Made wallet_rpc stop using a raw m_wallet pointer
2020-04-28 01:25:43 +02:00
|
|
|
rpc::version_t version;
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
m_is_connected = w->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS);
|
2016-12-14 12:18:52 +01:00
|
|
|
if (!m_is_connected)
|
2016-11-07 13:00:29 +01:00
|
|
|
return Wallet::ConnectionStatus_Disconnected;
|
2021-09-02 18:59:49 +02:00
|
|
|
if (
|
|
|
|
#ifdef ENABLE_LIGHT_WALLET
|
2017-08-05 00:08:32 +02:00
|
|
|
// Version check is not implemented in light wallets nodes/wallets
|
2021-09-02 18:59:49 +02:00
|
|
|
!w->light_wallet() &&
|
|
|
|
#endif
|
|
|
|
version.first != rpc::VERSION.first)
|
2016-11-07 13:00:29 +01:00
|
|
|
return Wallet::ConnectionStatus_WrongVersion;
|
|
|
|
return Wallet::ConnectionStatus_Connected;
|
2016-07-14 11:47:01 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-04-26 12:46:20 +02:00
|
|
|
void WalletImpl::setTrustedDaemon(bool arg)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->set_trusted_daemon(arg);
|
2016-04-26 12:46:20 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-04-26 12:46:20 +02:00
|
|
|
bool WalletImpl::trustedDaemon() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->is_trusted_daemon();
|
2016-04-26 12:46:20 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-01-10 22:34:15 +01:00
|
|
|
bool WalletImpl::watchOnly() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->watch_only();
|
2017-01-10 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-01-10 22:34:15 +01:00
|
|
|
void WalletImpl::clearStatus() const
|
2016-04-05 14:24:44 +02:00
|
|
|
{
|
2020-06-02 01:01:24 +02:00
|
|
|
std::lock_guard l{m_statusMutex};
|
2020-10-26 19:20:00 +01:00
|
|
|
m_status = {Status_Ok, ""};
|
2016-04-05 14:24:44 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::setStatusError(std::string message) const
|
2018-03-21 18:34:15 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
return setStatus(Status_Error, std::move(message));
|
2018-03-21 18:34:15 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::setStatusCritical(std::string message) const
|
2018-03-21 18:34:15 +01:00
|
|
|
{
|
2020-10-26 19:20:00 +01:00
|
|
|
return setStatus(Status_Critical, std::move(message));
|
2018-03-21 18:34:15 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-26 19:20:00 +01:00
|
|
|
bool WalletImpl::setStatus(int status, std::string message) const
|
2018-03-21 18:34:15 +01:00
|
|
|
{
|
2020-06-02 01:01:24 +02:00
|
|
|
std::lock_guard l{m_statusMutex};
|
2020-10-26 19:20:00 +01:00
|
|
|
m_status.first = status;
|
|
|
|
m_status.second = std::move(message);
|
|
|
|
return status == Status_Ok;
|
2018-03-21 18:34:15 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-07-10 16:17:23 +02:00
|
|
|
void WalletImpl::refreshThreadFunc()
|
|
|
|
{
|
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": starting refresh thread");
|
|
|
|
|
|
|
|
while (true) {
|
2020-06-02 01:01:24 +02:00
|
|
|
std::unique_lock lock{m_refreshMutex};
|
2016-07-10 16:17:23 +02:00
|
|
|
if (m_refreshThreadDone) {
|
|
|
|
break;
|
|
|
|
}
|
2016-07-14 11:47:01 +02:00
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": waiting for refresh...");
|
2016-09-20 19:40:58 +02:00
|
|
|
// if auto refresh enabled, we wait for the "m_refreshIntervalSeconds" interval.
|
|
|
|
// if not - we wait forever
|
2021-06-23 03:08:12 +02:00
|
|
|
if (std::chrono::milliseconds max_delay{m_refreshIntervalMillis.load()};
|
|
|
|
max_delay > 0ms) {
|
|
|
|
m_refreshCV.wait_for(lock, max_delay);
|
2016-09-20 19:40:58 +02:00
|
|
|
} else {
|
|
|
|
m_refreshCV.wait(lock);
|
|
|
|
}
|
|
|
|
|
2016-07-14 11:47:01 +02:00
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired...");
|
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled);
|
2020-10-26 19:20:00 +01:00
|
|
|
auto st = status();
|
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << st.first << ": " << st.second);
|
2018-10-16 13:58:22 +02:00
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan);
|
2016-12-14 12:18:52 +01:00
|
|
|
if (m_refreshEnabled) {
|
2016-07-14 11:47:01 +02:00
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": refreshing...");
|
2016-07-10 16:17:23 +02:00
|
|
|
doRefresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": refresh thread stopped");
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-07-10 16:17:23 +02:00
|
|
|
void WalletImpl::doRefresh()
|
|
|
|
{
|
2018-10-16 13:58:22 +02:00
|
|
|
bool rescan = m_refreshShouldRescan.exchange(false);
|
2016-07-10 16:17:23 +02:00
|
|
|
// synchronizing async and sync refresh calls
|
2020-06-02 01:01:24 +02:00
|
|
|
std::lock_guard guard{m_refreshMutex2};
|
2020-10-26 19:20:00 +01:00
|
|
|
do {
|
|
|
|
try {
|
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<<rescan);
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
|
2020-10-26 19:20:00 +01:00
|
|
|
// Syncing daemon and refreshing wallet simultaneously is very resource intensive.
|
|
|
|
// Disable refresh if wallet is disconnected or daemon isn't synced.
|
2021-09-02 18:59:49 +02:00
|
|
|
if (
|
|
|
|
#ifdef ENABLE_LIGHT_WALLET
|
|
|
|
w->light_wallet() ||
|
|
|
|
#endif
|
|
|
|
daemonSynced()) {
|
2020-10-26 19:20:00 +01:00
|
|
|
if(rescan)
|
2021-06-23 03:08:12 +02:00
|
|
|
w->rescan_blockchain(false);
|
|
|
|
w->refresh(trustedDaemon());
|
2020-10-26 19:20:00 +01:00
|
|
|
if (!m_synchronized) {
|
|
|
|
m_synchronized = true;
|
|
|
|
}
|
|
|
|
// assuming if we have empty history, it wasn't initialized yet
|
|
|
|
// for further history changes client need to update history in
|
|
|
|
// "on_money_received" and "on_money_sent" callbacks
|
|
|
|
if (m_history->count() == 0) {
|
|
|
|
m_history->refresh();
|
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
w->find_and_save_rings(false);
|
2020-10-26 19:20:00 +01:00
|
|
|
} else {
|
|
|
|
LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced");
|
2016-12-14 12:18:52 +01:00
|
|
|
}
|
2020-10-26 19:20:00 +01:00
|
|
|
} catch (const std::exception &e) {
|
|
|
|
setStatusError(e.what());
|
|
|
|
break;
|
2017-01-10 22:34:15 +01:00
|
|
|
}
|
2020-10-26 19:20:00 +01:00
|
|
|
} while (!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested
|
2018-10-16 13:58:22 +02:00
|
|
|
|
2016-07-14 12:33:49 +02:00
|
|
|
if (m_wallet2Callback->getListener()) {
|
|
|
|
m_wallet2Callback->getListener()->refreshed();
|
|
|
|
}
|
2016-07-10 16:17:23 +02:00
|
|
|
}
|
|
|
|
|
2016-07-14 12:33:49 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-07-10 16:17:23 +02:00
|
|
|
void WalletImpl::startRefresh()
|
|
|
|
{
|
|
|
|
if (!m_refreshEnabled) {
|
2017-01-30 18:00:10 +01:00
|
|
|
LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed...");
|
2016-07-10 16:17:23 +02:00
|
|
|
m_refreshEnabled = true;
|
|
|
|
m_refreshCV.notify_one();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-14 12:33:49 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-07-10 16:17:23 +02:00
|
|
|
void WalletImpl::stopRefresh()
|
|
|
|
{
|
|
|
|
if (!m_refreshThreadDone) {
|
|
|
|
m_refreshEnabled = false;
|
|
|
|
m_refreshThreadDone = true;
|
2016-09-20 19:08:33 +02:00
|
|
|
m_refreshCV.notify_one();
|
2016-07-10 16:17:23 +02:00
|
|
|
m_refreshThread.join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-07-14 12:33:49 +02:00
|
|
|
void WalletImpl::pauseRefresh()
|
|
|
|
{
|
2016-11-08 13:20:42 +01:00
|
|
|
LOG_PRINT_L2(__FUNCTION__ << ": refresh paused...");
|
2016-07-14 12:33:49 +02:00
|
|
|
// TODO synchronize access
|
|
|
|
if (!m_refreshThreadDone) {
|
|
|
|
m_refreshEnabled = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-05 14:24:44 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2016-09-30 01:11:28 +02:00
|
|
|
bool WalletImpl::isNewWallet() const
|
|
|
|
{
|
2016-09-30 21:42:15 +02:00
|
|
|
// in case wallet created without daemon connection, closed and opened again,
|
|
|
|
// it's the same case as if it created from scratch, i.e. we need "fast sync"
|
2016-11-09 18:46:03 +01:00
|
|
|
// with the daemon (pull hashes instead of pull blocks).
|
2017-01-10 22:34:15 +01:00
|
|
|
// If wallet cache is rebuilt, creation height stored in .keys is used.
|
|
|
|
// Watch only wallet is a copy of an existing wallet.
|
2018-06-04 09:58:13 +02:00
|
|
|
return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly();
|
2016-09-30 01:11:28 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2019-02-23 15:28:18 +01:00
|
|
|
void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending)
|
|
|
|
{
|
|
|
|
// If the device being used is HW device with cold signing protocol, cold sign then.
|
2021-06-23 03:08:12 +02:00
|
|
|
if (!wallet()->get_account().get_device().has_tx_cold_sign()){
|
2019-02-23 15:28:18 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tools::wallet2::signed_tx_set exported_txs;
|
|
|
|
std::vector<cryptonote::address_parse_info> dsts_info;
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->cold_sign_tx(pending->m_pending_tx, exported_txs, dsts_info, pending->m_tx_device_aux);
|
2019-02-23 15:28:18 +01:00
|
|
|
pending->m_key_images = exported_txs.key_images;
|
|
|
|
pending->m_pending_tx = exported_txs.ptx;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-10-22 23:43:29 +02:00
|
|
|
bool WalletImpl::doInit(const std::string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
|
2016-09-30 01:11:28 +02:00
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
if (!w->init(daemon_address, m_daemon_login, /*proxy=*/ "", upper_transaction_size_limit))
|
2017-01-25 06:16:05 +01:00
|
|
|
return false;
|
2016-09-30 01:11:28 +02:00
|
|
|
|
|
|
|
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
|
2016-12-27 19:38:05 +01:00
|
|
|
// If daemon isn't synced a calculated block height will be used instead
|
2017-08-04 23:28:38 +02:00
|
|
|
//TODO: Handle light wallet scenario where block height = 0.
|
2016-12-27 19:38:05 +01:00
|
|
|
if (isNewWallet() && daemonSynced()) {
|
2017-01-10 22:34:15 +01:00
|
|
|
LOG_PRINT_L2(__FUNCTION__ << ":New Wallet - fast refresh until " << daemonBlockChainHeight());
|
2021-06-23 03:08:12 +02:00
|
|
|
w->set_refresh_from_block_height(daemonBlockChainHeight());
|
2016-09-30 01:11:28 +02:00
|
|
|
}
|
|
|
|
|
2017-01-10 22:34:15 +01:00
|
|
|
if (m_rebuildWalletCache)
|
2021-06-23 03:08:12 +02:00
|
|
|
LOG_PRINT_L2(__FUNCTION__ << ": Rebuilding wallet cache, fast refresh until block " << w->get_refresh_from_block_height());
|
2017-01-10 22:34:15 +01:00
|
|
|
|
2016-09-30 01:11:28 +02:00
|
|
|
if (Utils::isAddressLocal(daemon_address)) {
|
|
|
|
this->setTrustedDaemon(true);
|
2017-01-08 13:47:13 +01:00
|
|
|
m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS;
|
|
|
|
} else {
|
|
|
|
this->setTrustedDaemon(false);
|
|
|
|
m_refreshIntervalMillis = DEFAULT_REMOTE_NODE_REFRESH_INTERVAL_MILLIS;
|
2016-09-30 01:11:28 +02:00
|
|
|
}
|
2017-01-25 06:16:05 +01:00
|
|
|
return true;
|
2016-09-30 01:11:28 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-01-08 23:53:24 +01:00
|
|
|
bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return m_wallet_ptr->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error);
|
2017-01-08 23:53:24 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-08-09 12:38:29 +02:00
|
|
|
std::string WalletImpl::getDefaultDataDir() const
|
|
|
|
{
|
|
|
|
return tools::get_default_data_dir();
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-01-12 22:10:38 +01:00
|
|
|
bool WalletImpl::rescanSpent()
|
|
|
|
{
|
|
|
|
clearStatus();
|
|
|
|
if (!trustedDaemon()) {
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Rescan spent can only be used with a trusted daemon"));
|
2017-01-12 22:10:38 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
try {
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->rescan_spent();
|
2017-01-12 22:10:38 +01:00
|
|
|
} catch (const std::exception &e) {
|
|
|
|
LOG_ERROR(__FUNCTION__ << " error: " << e.what());
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(e.what());
|
2017-01-12 22:10:38 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2017-03-24 09:59:26 +01:00
|
|
|
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-03-24 09:59:26 +01:00
|
|
|
void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->get_hard_fork_info(version, earliest_height);
|
2017-03-24 09:59:26 +01:00
|
|
|
}
|
|
|
|
|
2021-01-07 07:13:08 +01:00
|
|
|
EXPORT
|
|
|
|
std::optional<uint8_t> WalletImpl::hardForkVersion() const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return m_wallet_ptr->get_hard_fork_version();
|
2021-01-07 07:13:08 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2017-03-24 09:59:26 +01:00
|
|
|
bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->use_fork_rules(version,early_blocks);
|
2017-03-24 09:59:26 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-09-16 15:24:19 +02:00
|
|
|
bool WalletImpl::blackballOutputs(const std::vector<std::string> &outputs, bool add)
|
2018-03-04 14:32:35 +01:00
|
|
|
{
|
2018-09-16 15:24:19 +02:00
|
|
|
std::vector<std::pair<uint64_t, uint64_t>> raw_outputs;
|
|
|
|
raw_outputs.reserve(outputs.size());
|
|
|
|
uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
|
|
|
|
for (const std::string &str: outputs)
|
2018-03-04 14:32:35 +01:00
|
|
|
{
|
2018-09-16 15:24:19 +02:00
|
|
|
if (sscanf(str.c_str(), "@%" PRIu64, &amount) == 1)
|
|
|
|
continue;
|
|
|
|
if (amount == std::numeric_limits<uint64_t>::max())
|
2018-03-04 14:32:35 +01:00
|
|
|
{
|
2018-09-16 15:24:19 +02:00
|
|
|
setStatusError("First line is not an amount");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (sscanf(str.c_str(), "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits<uint64_t>::max() - offset)
|
|
|
|
{
|
|
|
|
while (num_offsets--)
|
|
|
|
raw_outputs.push_back(std::make_pair(amount, offset++));
|
|
|
|
}
|
|
|
|
else if (sscanf(str.c_str(), "%" PRIu64, &offset) == 1)
|
|
|
|
{
|
|
|
|
raw_outputs.push_back(std::make_pair(amount, offset));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setStatusError(tr("Invalid output: ") + str);
|
|
|
|
return false;
|
2018-03-04 14:32:35 +01:00
|
|
|
}
|
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
bool ret = wallet()->set_blackballed_outputs(raw_outputs, add);
|
2018-03-04 14:32:35 +01:00
|
|
|
if (!ret)
|
|
|
|
{
|
2018-10-18 20:05:51 +02:00
|
|
|
setStatusError(tr("Failed to mark outputs as spent"));
|
2018-03-04 14:32:35 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-09-30 10:10:30 +02:00
|
|
|
bool WalletImpl::blackballOutput(const std::string &amount, const std::string &offset)
|
|
|
|
{
|
|
|
|
uint64_t raw_amount, raw_offset;
|
|
|
|
if (!epee::string_tools::get_xtype_from_string(raw_amount, amount))
|
|
|
|
{
|
|
|
|
setStatusError(tr("Failed to parse output amount"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!epee::string_tools::get_xtype_from_string(raw_offset, offset))
|
|
|
|
{
|
|
|
|
setStatusError(tr("Failed to parse output offset"));
|
|
|
|
return false;
|
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
bool ret = wallet()->blackball_output(std::make_pair(raw_amount, raw_offset));
|
2018-09-30 10:10:30 +02:00
|
|
|
if (!ret)
|
|
|
|
{
|
2018-10-18 20:05:51 +02:00
|
|
|
setStatusError(tr("Failed to mark output as spent"));
|
2018-09-30 10:10:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-09-16 15:24:19 +02:00
|
|
|
bool WalletImpl::unblackballOutput(const std::string &amount, const std::string &offset)
|
2018-03-04 14:32:35 +01:00
|
|
|
{
|
2018-09-16 15:24:19 +02:00
|
|
|
uint64_t raw_amount, raw_offset;
|
|
|
|
if (!epee::string_tools::get_xtype_from_string(raw_amount, amount))
|
|
|
|
{
|
|
|
|
setStatusError(tr("Failed to parse output amount"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!epee::string_tools::get_xtype_from_string(raw_offset, offset))
|
2018-03-04 14:32:35 +01:00
|
|
|
{
|
2018-09-16 15:24:19 +02:00
|
|
|
setStatusError(tr("Failed to parse output offset"));
|
2018-03-04 14:32:35 +01:00
|
|
|
return false;
|
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
bool ret = wallet()->unblackball_output(std::make_pair(raw_amount, raw_offset));
|
2018-03-04 14:32:35 +01:00
|
|
|
if (!ret)
|
|
|
|
{
|
2018-10-18 20:05:51 +02:00
|
|
|
setStatusError(tr("Failed to mark output as unspent"));
|
2018-03-04 14:32:35 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-05 16:04:59 +01:00
|
|
|
bool WalletImpl::getRing(const std::string &key_image, std::vector<uint64_t> &ring) const
|
|
|
|
{
|
|
|
|
crypto::key_image raw_key_image;
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(key_image, raw_key_image))
|
2018-03-05 16:04:59 +01:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse key image"));
|
2018-03-05 16:04:59 +01:00
|
|
|
return false;
|
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
bool ret = wallet()->get_ring(raw_key_image, ring);
|
2018-03-05 16:04:59 +01:00
|
|
|
if (!ret)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to get ring"));
|
2018-03-05 16:04:59 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-14 20:13:55 +01:00
|
|
|
bool WalletImpl::getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const
|
|
|
|
{
|
|
|
|
crypto::hash raw_txid;
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(txid, raw_txid))
|
2018-03-14 20:13:55 +01:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse txid"));
|
2018-03-14 20:13:55 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> raw_rings;
|
2021-06-23 03:08:12 +02:00
|
|
|
bool ret = wallet()->get_rings(raw_txid, raw_rings);
|
2018-03-14 20:13:55 +01:00
|
|
|
if (!ret)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to get rings"));
|
2018-03-14 20:13:55 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (const auto &r: raw_rings)
|
|
|
|
{
|
2020-10-23 22:32:28 +02:00
|
|
|
rings.push_back(std::make_pair(tools::type_to_hex(r.first), r.second));
|
2018-03-14 20:13:55 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-05 16:04:59 +01:00
|
|
|
bool WalletImpl::setRing(const std::string &key_image, const std::vector<uint64_t> &ring, bool relative)
|
|
|
|
{
|
|
|
|
crypto::key_image raw_key_image;
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(key_image, raw_key_image))
|
2018-03-05 16:04:59 +01:00
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to parse key image"));
|
2018-03-05 16:04:59 +01:00
|
|
|
return false;
|
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
bool ret = wallet()->set_ring(raw_key_image, ring, relative);
|
2018-03-05 16:04:59 +01:00
|
|
|
if (!ret)
|
|
|
|
{
|
2018-03-21 18:34:15 +01:00
|
|
|
setStatusError(tr("Failed to set ring"));
|
2018-03-05 16:04:59 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-12 13:33:05 +01:00
|
|
|
void WalletImpl::segregatePreForkOutputs(bool segregate)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->segregate_pre_fork_outputs(segregate);
|
2018-03-12 13:33:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-12 13:33:05 +01:00
|
|
|
void WalletImpl::segregationHeight(uint64_t height)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->segregation_height(height);
|
2018-03-12 13:33:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-03-12 13:33:05 +01:00
|
|
|
void WalletImpl::keyReuseMitigation2(bool mitigation)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->key_reuse_mitigation2(mitigation);
|
2018-03-12 13:33:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-09-19 11:21:56 +02:00
|
|
|
bool WalletImpl::lockKeysFile()
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->lock_keys_file();
|
2018-09-19 11:21:56 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-09-19 11:21:56 +02:00
|
|
|
bool WalletImpl::unlockKeysFile()
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->unlock_keys_file();
|
2018-09-19 11:21:56 +02:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2018-09-19 11:21:56 +02:00
|
|
|
bool WalletImpl::isKeysFileLocked()
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->is_keys_file_locked();
|
2018-09-19 11:21:56 +02:00
|
|
|
}
|
2018-11-15 03:22:14 +01:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-12-17 00:31:58 +01:00
|
|
|
PendingTransaction* WalletImpl::stakePending(const std::string& sn_key_str, const uint64_t& amount)
|
2018-11-15 03:22:14 +01:00
|
|
|
{
|
2020-12-17 00:31:58 +01:00
|
|
|
/// Note(maxim): need to be careful to call `WalletImpl::disposeTransaction` when it is no longer needed
|
|
|
|
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
|
|
|
|
std::string error_msg;
|
|
|
|
|
2018-11-15 03:22:14 +01:00
|
|
|
crypto::public_key sn_key;
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(sn_key_str, sn_key))
|
2019-01-10 07:10:34 +01:00
|
|
|
{
|
|
|
|
error_msg = "Failed to parse service node pubkey";
|
|
|
|
LOG_ERROR(error_msg);
|
2020-12-17 00:31:58 +01:00
|
|
|
transaction->setError(error_msg);
|
|
|
|
return transaction;
|
2018-11-15 03:22:14 +01:00
|
|
|
}
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
tools::wallet2::stake_result stake_result = wallet()->create_stake_tx(sn_key, amount);
|
2019-03-19 06:33:44 +01:00
|
|
|
if (stake_result.status != tools::wallet2::stake_result_status::success)
|
2019-01-10 07:10:34 +01:00
|
|
|
{
|
Infinite Staking Part 2 (#406)
* Cleanup and undoing some protocol breakages
* Simplify expiration of nodes
* Request unlock schedules entire node for expiration
* Fix off by one in expiring nodes
* Undo expiring code for pre v10 nodes
* Fix RPC returning register as unlock height and not checking 0
* Rename key image unlock height const
* Undo testnet hardfork debug changes
* Remove is_type for get_type, fix missing var rename
* Move serialisable data into public namespace
* Serialise tx types properly
* Fix typo in no service node known msg
* Code review
* Fix == to >= on serialising tx type
* Code review 2
* Fix tests and key image unlock
* Add command to print locked key images
* Update ui to display lock stakes, query in print cmd blacklist
* Modify print stakes to be less slow
* Remove autostaking code
* Refactor staking into sweep functions
It appears staking was derived off stake_main written separately at
implementation at the beginning. This merges them back into a common
code path, after removing autostake there's only some minor differences.
It also makes sure that any changes to sweeping upstream are going to be
considered in the staking process which we want.
* Display unlock height for stakes
* Begin creating output blacklist
* Make blacklist output a migration step
* Implement get_output_blacklist for lmdb
* In wallet output selection ignore blacklisted outputs
* Apply blacklisted outputs to output selection
* Fix broken tests, switch key image unlock
* Fix broken unit_tests
* Begin change to limit locked key images to 4 globally
* Revamp prepare registration for new min contribution rules
* Fix up old back case in prepare registration
* Remove debug code
* Cleanup debug code and some unecessary changes
* Fix migration step on mainnet db
* Fix blacklist outputs for pre-existing DB's
* Remove irrelevant note
* Tweak scanning addresses for locked stakes
Since we only now allow contributions from the primary address we can
skip checking all subaddress + lookahead to speed up wallet scanning
* Define macro for SCNu64 for Mingw
* Fix failure on empty DB
* Add missing error msg, remove contributor from stake
* Improve staking messages
* Flush prompt to always display
* Return the msg from stake failure and fix stake parsing error
* Tweak fork rules for smaller bulletproofs
* Tweak pooled nodes minimum amounts
* Fix crash on exit, there's no need to store on destructor
Since all information about service nodes is derived from the blockchain
and we store state every time we receive a block, storing in the
destructor is redundant as there is no new information to store.
* Make prompt be consistent with CLI
* Check max number of key images from per user to node
* Implement error message on get_output_blacklist failure
* Remove resolved TODO's/comments
* Handle infinite staking in print_sn
* Atoi->strtol, fix prepare_registration, virtual override, stale msgs
2019-02-14 02:12:57 +01:00
|
|
|
error_msg = "Failed to create a stake transaction: " + stake_result.msg;
|
2020-12-17 00:31:58 +01:00
|
|
|
LOG_ERROR(error_msg);
|
|
|
|
transaction->setError(error_msg);
|
|
|
|
return transaction;
|
2019-01-10 07:10:34 +01:00
|
|
|
}
|
|
|
|
|
2019-03-19 06:33:44 +01:00
|
|
|
transaction->m_pending_tx = {stake_result.ptx};
|
2018-11-15 03:22:14 +01:00
|
|
|
return transaction;
|
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-11-17 11:09:10 +01:00
|
|
|
StakeUnlockResult* WalletImpl::canRequestStakeUnlock(const std::string &sn_key)
|
2020-11-15 13:05:09 +01:00
|
|
|
{
|
2020-11-16 09:20:24 +01:00
|
|
|
crypto::public_key snode_key;
|
|
|
|
if (!tools::hex_to_type(sn_key, snode_key))
|
|
|
|
{
|
2020-12-12 18:32:07 +01:00
|
|
|
tools::wallet2::request_stake_unlock_result res{};
|
2020-11-16 09:20:24 +01:00
|
|
|
res.success = false;
|
|
|
|
res.msg = "Failed to Parse Service Node Key";
|
2020-12-12 18:32:07 +01:00
|
|
|
return new StakeUnlockResultImpl(*this, res);
|
2020-11-16 09:20:24 +01:00
|
|
|
}
|
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
return new StakeUnlockResultImpl(*this, wallet()->can_request_stake_unlock(snode_key));
|
2020-11-15 13:05:09 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2020-11-17 11:09:10 +01:00
|
|
|
StakeUnlockResult* WalletImpl::requestStakeUnlock(const std::string &sn_key)
|
2020-11-15 13:05:09 +01:00
|
|
|
{
|
2020-11-17 11:09:10 +01:00
|
|
|
tools::wallet2::request_stake_unlock_result res = {};
|
2020-11-16 09:20:24 +01:00
|
|
|
|
|
|
|
crypto::public_key snode_key;
|
|
|
|
if (!tools::hex_to_type(sn_key, snode_key))
|
|
|
|
{
|
|
|
|
res.success = false;
|
|
|
|
res.msg = "Failed to Parse Service Node Key";
|
2020-12-12 18:32:07 +01:00
|
|
|
return new StakeUnlockResultImpl(*this, res);
|
2020-11-16 09:20:24 +01:00
|
|
|
}
|
2021-06-23 03:08:12 +02:00
|
|
|
auto w = wallet();
|
|
|
|
tools::wallet2::request_stake_unlock_result unlock_result = w->can_request_stake_unlock(snode_key);
|
2020-11-16 09:20:24 +01:00
|
|
|
if (unlock_result.success)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
w->commit_tx(unlock_result.ptx);
|
2020-11-16 09:20:24 +01:00
|
|
|
}
|
|
|
|
catch(const std::exception &e)
|
|
|
|
{
|
|
|
|
res.success = false;
|
|
|
|
res.msg = "Failed to commit tx.";
|
2020-12-12 18:32:07 +01:00
|
|
|
return new StakeUnlockResultImpl(*this, res);
|
2020-11-16 09:20:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
res.success = false;
|
|
|
|
res.msg = tr("Cannot request stake unlock: " + unlock_result.msg);
|
2020-12-12 18:32:07 +01:00
|
|
|
return new StakeUnlockResultImpl(*this, res);
|
2020-11-16 09:20:24 +01:00
|
|
|
}
|
|
|
|
|
2020-12-12 18:32:07 +01:00
|
|
|
return new StakeUnlockResultImpl(*this, unlock_result);
|
2020-11-15 13:05:09 +01:00
|
|
|
}
|
2018-11-15 03:22:14 +01:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2019-02-23 15:28:18 +01:00
|
|
|
uint64_t WalletImpl::coldKeyImageSync(uint64_t &spent, uint64_t &unspent)
|
|
|
|
{
|
2021-06-23 03:08:12 +02:00
|
|
|
return wallet()->cold_key_image_sync(spent, unspent);
|
2019-02-23 15:28:18 +01:00
|
|
|
}
|
2019-05-31 10:41:52 +02:00
|
|
|
|
2021-01-08 20:51:05 +01:00
|
|
|
EXPORT
|
2019-05-31 10:41:52 +02:00
|
|
|
void WalletImpl::deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId)
|
|
|
|
{
|
2020-06-02 00:30:19 +02:00
|
|
|
std::optional<crypto::hash8> payment_id_param = std::nullopt;
|
2019-05-31 10:41:52 +02:00
|
|
|
if (!paymentId.empty())
|
2020-10-23 22:32:28 +02:00
|
|
|
if (!tools::hex_to_type(paymentId, payment_id_param.emplace()))
|
2020-10-22 23:37:54 +02:00
|
|
|
throw std::runtime_error("Invalid payment ID");
|
2019-05-31 10:41:52 +02:00
|
|
|
|
2021-06-23 03:08:12 +02:00
|
|
|
wallet()->device_show_address(accountIndex, addressIndex, payment_id_param);
|
2019-05-31 10:41:52 +02:00
|
|
|
}
|
2016-04-20 12:01:00 +02:00
|
|
|
} // namespace
|