Merge pull request 'Wowletify' (#396) from wowletify into master

Reviewed-on: #396
pull/398/head
wowario 3 years ago
commit ff5182f7f2

@ -35,12 +35,16 @@ set(wallet_api_sources
wallet_manager.cpp
transaction_info.cpp
transaction_history.cpp
transaction_construction_info.cpp
pending_transaction_info.cpp
pending_transaction.cpp
utils.cpp
address_book.cpp
subaddress.cpp
subaddress_account.cpp
unsigned_transaction.cpp)
unsigned_transaction.cpp
coins.cpp
coins_info.cpp)
set(wallet_api_headers
wallet2_api.h)
@ -50,12 +54,16 @@ set(wallet_api_private_headers
wallet_manager.h
transaction_info.h
transaction_history.h
transaction_construction_info.h
pending_transaction_info.h
pending_transaction.h
common_defines.h
address_book.h
subaddress.h
subaddress_account.h
unsigned_transaction.h)
unsigned_transaction.h
coins.h
coins_info.h)
monero_private_headers(wallet_api
${wallet_api_private_headers})

@ -0,0 +1,122 @@
#include "coins.h"
#include "coins_info.h"
#include "wallet.h"
#include "crypto/hash.h"
#include "wallet/wallet2.h"
#include "common_defines.h"
#include <string>
#include <vector>
using namespace epee;
namespace Monero {
Coins::~Coins() = default;
CoinsImpl::CoinsImpl(WalletImpl *wallet)
: m_wallet(wallet) {}
CoinsImpl::~CoinsImpl()
{
for (auto t : m_rows)
delete t;
}
int CoinsImpl::count() const
{
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
int result = m_rows.size();
return result;
}
CoinsInfo *CoinsImpl::coin(int index) const
{
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
// sanity check
if (index < 0)
return nullptr;
auto index_ = static_cast<unsigned>(index);
return index_ < m_rows.size() ? m_rows[index_] : nullptr;
}
std::vector<CoinsInfo *> CoinsImpl::getAll() const
{
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
return m_rows;
}
void CoinsImpl::refresh()
{
LOG_PRINT_L2("Refreshing coins");
boost::unique_lock<boost::shared_mutex> lock(m_rowsMutex);
boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_wallet->m_transfers_mutex);
// delete old outputs;
for (auto t : m_rows)
delete t;
m_rows.clear();
for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i)
{
const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i);
auto ci = new CoinsInfoImpl();
ci->m_blockHeight = td.m_block_height;
ci->m_hash = string_tools::pod_to_hex(td.m_txid);
ci->m_internalOutputIndex = td.m_internal_output_index;
ci->m_globalOutputIndex = td.m_global_output_index;
ci->m_spent = td.m_spent;
ci->m_frozen = td.m_frozen;
ci->m_spentHeight = td.m_spent_height;
ci->m_amount = td.m_amount;
ci->m_rct = td.m_rct;
ci->m_keyImageKnown = td.m_key_image_known;
ci->m_pkIndex = td.m_pk_index;
ci->m_subaddrIndex = td.m_subaddr_index.minor;
ci->m_subaddrAccount = td.m_subaddr_index.major;
ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe?
ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index);
ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image);
ci->m_unlockTime = td.m_tx.unlock_time;
ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td);
ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key());
ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
m_rows.push_back(ci);
}
}
void CoinsImpl::setFrozen(int index)
{
try
{
m_wallet->m_wallet->freeze(index);
refresh();
}
catch (const std::exception& e)
{
LOG_ERROR("setLabel: " << e.what());
}
}
void CoinsImpl::thaw(int index)
{
try
{
m_wallet->m_wallet->thaw(index);
refresh();
}
catch (const std::exception& e)
{
LOG_ERROR("thaw: " << e.what());
}
}
bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) {
return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight);
}
} // namespace

@ -0,0 +1,34 @@
#ifndef WOWLET_COINS_H
#define WOWLET_COINS_H
#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
namespace Monero {
class WalletImpl;
class CoinsImpl : public Coins
{
public:
explicit CoinsImpl(WalletImpl * wallet);
~CoinsImpl() override;
int count() const override;
CoinsInfo * coin(int index) const override;
std::vector<CoinsInfo*> getAll() const override;
void refresh() override;
void setFrozen(int index) override;
void thaw(int index) override;
bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override;
private:
WalletImpl *m_wallet;
std::vector<CoinsInfo*> m_rows;
mutable boost::shared_mutex m_rowsMutex;
};
}
#endif //WOWLET_COINS_H

@ -0,0 +1,118 @@
#include "coins_info.h"
using namespace std;
namespace Monero {
CoinsInfo::~CoinsInfo() = default;
CoinsInfoImpl::CoinsInfoImpl()
: m_blockHeight(0)
, m_internalOutputIndex(0)
, m_globalOutputIndex(0)
, m_spent(false)
, m_frozen(false)
, m_spentHeight(0)
, m_amount(0)
, m_rct(false)
, m_keyImageKnown(false)
, m_pkIndex(0)
, m_subaddrAccount(0)
, m_subaddrIndex(0)
, m_unlockTime(0)
, m_unlocked(false)
{
}
CoinsInfoImpl::~CoinsInfoImpl() = default;
uint64_t CoinsInfoImpl::blockHeight() const
{
return m_blockHeight;
}
string CoinsInfoImpl::hash() const
{
return m_hash;
}
size_t CoinsInfoImpl::internalOutputIndex() const {
return m_internalOutputIndex;
}
uint64_t CoinsInfoImpl::globalOutputIndex() const
{
return m_globalOutputIndex;
}
bool CoinsInfoImpl::spent() const
{
return m_spent;
}
bool CoinsInfoImpl::frozen() const
{
return m_frozen;
}
uint64_t CoinsInfoImpl::spentHeight() const
{
return m_spentHeight;
}
uint64_t CoinsInfoImpl::amount() const
{
return m_amount;
}
bool CoinsInfoImpl::rct() const {
return m_rct;
}
bool CoinsInfoImpl::keyImageKnown() const {
return m_keyImageKnown;
}
size_t CoinsInfoImpl::pkIndex() const {
return m_pkIndex;
}
uint32_t CoinsInfoImpl::subaddrIndex() const {
return m_subaddrIndex;
}
uint32_t CoinsInfoImpl::subaddrAccount() const {
return m_subaddrAccount;
}
string CoinsInfoImpl::address() const {
return m_address;
}
string CoinsInfoImpl::addressLabel() const {
return m_addressLabel;
}
string CoinsInfoImpl::keyImage() const {
return m_keyImage;
}
uint64_t CoinsInfoImpl::unlockTime() const {
return m_unlockTime;
}
bool CoinsInfoImpl::unlocked() const {
return m_unlocked;
}
string CoinsInfoImpl::pubKey() const {
return m_pubKey;
}
bool CoinsInfoImpl::coinbase() const {
return m_coinbase;
}
} // namespace
namespace Bitmonero = Monero;

@ -0,0 +1,67 @@
#ifndef WOWLET_COINS_INFO_H
#define WOWLET_COINS_INFO_H
#include "wallet/api/wallet2_api.h"
#include <string>
#include <ctime>
namespace Monero {
class CoinsImpl;
class CoinsInfoImpl : public CoinsInfo
{
public:
CoinsInfoImpl();
~CoinsInfoImpl();
virtual uint64_t blockHeight() const override;
virtual std::string hash() const override;
virtual size_t internalOutputIndex() const override;
virtual uint64_t globalOutputIndex() const override;
virtual bool spent() const override;
virtual bool frozen() const override;
virtual uint64_t spentHeight() const override;
virtual uint64_t amount() const override;
virtual bool rct() const override;
virtual bool keyImageKnown() const override;
virtual size_t pkIndex() const override;
virtual uint32_t subaddrIndex() const override;
virtual uint32_t subaddrAccount() const override;
virtual std::string address() const override;
virtual std::string addressLabel() const override;
virtual std::string keyImage() const override;
virtual uint64_t unlockTime() const override;
virtual bool unlocked() const override;
virtual std::string pubKey() const override;
virtual bool coinbase() const override;
private:
uint64_t m_blockHeight;
std::string m_hash;
size_t m_internalOutputIndex;
uint64_t m_globalOutputIndex;
bool m_spent;
bool m_frozen;
uint64_t m_spentHeight;
uint64_t m_amount;
bool m_rct;
bool m_keyImageKnown;
size_t m_pkIndex;
uint32_t m_subaddrIndex;
uint32_t m_subaddrAccount;
std::string m_address;
std::string m_addressLabel;
std::string m_keyImage;
uint64_t m_unlockTime;
bool m_unlocked;
std::string m_pubKey;
bool m_coinbase;
friend class CoinsImpl;
};
} // namespace
#endif //WOWLET_COINS_INFO_H

@ -35,6 +35,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "common/base58.h"
#include "string_coding.h"
#include <memory>
#include <vector>
@ -263,4 +264,50 @@ std::vector<std::string> PendingTransactionImpl::signersKeys() const {
return keys;
}
std::string PendingTransactionImpl::unsignedTxToBin() const {
return m_wallet.m_wallet->dump_tx_to_str(m_pending_tx);
}
std::string PendingTransactionImpl::unsignedTxToBase64() const {
return epee::string_encoding::base64_encode(m_wallet.m_wallet->dump_tx_to_str(m_pending_tx));
}
std::string PendingTransactionImpl::signedTxToHex(int index) const {
auto index_ = static_cast<unsigned>(index);
if (index < 0 || index_ >= m_pending_tx.size()) {
return "";
}
return epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(m_pending_tx[index_].tx));
}
size_t PendingTransactionImpl::signedTxSize(int index) const {
auto index_ = static_cast<unsigned>(index);
if (index < 0 || index_ >= m_pending_tx.size()) {
return 0;
}
return cryptonote::tx_to_blob(m_pending_tx[index_].tx).size();
}
PendingTransactionInfo * PendingTransactionImpl::transaction(int index) const {
if (index < 0)
return nullptr;
auto index_ = static_cast<unsigned>(index);
return index_ < m_pending_tx_info.size() ? m_pending_tx_info[index_] : nullptr;
}
void PendingTransactionImpl::refresh() {
for (auto t : m_pending_tx_info)
delete t;
m_pending_tx_info.clear();
for (const auto& p : m_pending_tx)
m_pending_tx_info.push_back(new PendingTransactionInfoImpl(m_wallet, p));
}
std::vector<PendingTransactionInfo*> PendingTransactionImpl::getAll() const {
return m_pending_tx_info;
}
}

@ -30,6 +30,7 @@
#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
#include "pending_transaction_info.h"
#include <string>
#include <vector>
@ -53,6 +54,13 @@ public:
uint64_t txCount() const override;
std::vector<uint32_t> subaddrAccount() const override;
std::vector<std::set<uint32_t>> subaddrIndices() const override;
std::string unsignedTxToBin() const override;
std::string unsignedTxToBase64() const override;
std::string signedTxToHex(int index) const override;
size_t signedTxSize(int index) const override;
void refresh() override;
std::vector<PendingTransactionInfo*> getAll() const override;
PendingTransactionInfo * transaction(int index) const override;
// TODO: continue with interface;
std::string multisigSignData() override;
@ -66,6 +74,7 @@ private:
int m_status;
std::string m_errorString;
std::vector<tools::wallet2::pending_tx> m_pending_tx;
std::vector<PendingTransactionInfo*> m_pending_tx_info;
std::unordered_set<crypto::public_key> m_signers;
std::vector<std::string> m_tx_device_aux;
std::vector<crypto::key_image> m_key_images;

@ -0,0 +1,47 @@
#include "pending_transaction_info.h"
#include "transaction_construction_info.h"
using namespace std;
namespace Monero {
PendingTransactionInfo::~PendingTransactionInfo() = default;
PendingTransactionInfoImpl::PendingTransactionInfoImpl(WalletImpl &wallet, const tools::wallet2::pending_tx & ptx)
: m_wallet(wallet)
, m_ptx(ptx)
, m_constructionData(new TransactionConstructionInfoImpl(wallet, ptx.construction_data))
{
}
PendingTransactionInfoImpl::~PendingTransactionInfoImpl() = default;
uint64_t PendingTransactionInfoImpl::fee() const
{
return m_ptx.fee;
}
uint64_t PendingTransactionInfoImpl::dust() const
{
return m_ptx.dust;
}
bool PendingTransactionInfoImpl::dustAddedToFee() const
{
return m_ptx.dust_added_to_fee;
}
std::string PendingTransactionInfoImpl::txKey() const
{
return epee::string_tools::pod_to_hex(m_ptx.tx_key);
}
TransactionConstructionInfo * PendingTransactionInfoImpl::constructionData() const {
return m_constructionData;
}
// TransactionConstructionInfo::Output TransactionConstructionInfoImpl::change() const {
// return Output(
// {m_ptx.change_dts.amount, m_ptx.change_dts.address(m_wallet.m_wallet->nettype(), crypto::hash())});
// }
}

@ -0,0 +1,37 @@
#ifndef WOWLET_PENDING_TX_H
#define WOWLET_PENDING_TX_H
#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
#include "wallet.h"
#include <string>
namespace Monero {
class PendingTransactionImpl;
class PendingTransactionInfoImpl : public PendingTransactionInfo
{
public:
PendingTransactionInfoImpl(WalletImpl &wallet, const tools::wallet2::pending_tx & ptx);
~PendingTransactionInfoImpl() override;
uint64_t fee() const override;
uint64_t dust() const override;
bool dustAddedToFee() const override;
std::string txKey() const override;
TransactionConstructionInfo *constructionData() const override;
// Output change() const override;
private:
friend class WalletImpl;
WalletImpl &m_wallet;
tools::wallet2::pending_tx m_ptx;
TransactionConstructionInfo *m_constructionData;
};
}
#endif //FEATHER_PENDING_TX_H

@ -67,7 +67,10 @@ void SubaddressImpl::refresh(uint32_t accountIndex)
clearRows();
for (size_t i = 0; i < m_wallet->m_wallet->get_num_subaddresses(accountIndex); ++i)
{
m_rows.push_back(new SubaddressRow(i, m_wallet->m_wallet->get_subaddress_as_str({accountIndex, (uint32_t)i}), m_wallet->m_wallet->get_subaddress_label({accountIndex, (uint32_t)i})));
m_rows.push_back(new SubaddressRow(i,
m_wallet->m_wallet->get_subaddress_as_str({accountIndex, (uint32_t)i}),
m_wallet->m_wallet->get_subaddress_label({accountIndex, (uint32_t)i}),
m_wallet->m_wallet->get_subaddress_used({accountIndex, (uint32_t)i})));
}
}

@ -0,0 +1,63 @@
#include "transaction_construction_info.h"
using namespace std;
namespace Monero {
TransactionConstructionInfo::~TransactionConstructionInfo() = default;
TransactionConstructionInfo::Input::Input(uint64_t _amount, const std::string &_pubkey)
: amount(_amount), pubkey(_pubkey) {}
TransactionConstructionInfo::Output::Output(uint64_t _amount, const std::string &_address)
: amount(_amount), address(_address) {}
TransactionConstructionInfoImpl::TransactionConstructionInfoImpl(WalletImpl &wallet, const tools::wallet2::tx_construction_data & txcd)
: m_wallet(wallet)
, m_txcd(txcd) {}
TransactionConstructionInfoImpl::~TransactionConstructionInfoImpl() = default;
uint64_t TransactionConstructionInfoImpl::unlockTime() const {
return m_txcd.unlock_time;
}
std::set<std::uint32_t> TransactionConstructionInfoImpl::subaddressIndices() const {
return m_txcd.subaddr_indices;
}
std::vector<std::string> TransactionConstructionInfoImpl::subaddresses() const {
std::vector<std::string> s;
auto major = m_txcd.subaddr_account;
for (const auto &minor : m_txcd.subaddr_indices) {
s.push_back(m_wallet.m_wallet->get_subaddress_as_str({major, minor}));
}
return s;
}
uint64_t TransactionConstructionInfoImpl::minMixinCount() const {
uint64_t min_mixin = -1;
for (const auto &source : m_txcd.sources) {
size_t mixin = source.outputs.size() - 1;
if (mixin < min_mixin)
min_mixin = mixin;
}
return min_mixin;
}
std::vector<TransactionConstructionInfo::Input> TransactionConstructionInfoImpl::inputs() const {
std::vector<Input> inputs;
for (const auto &i : m_txcd.sources) {
inputs.emplace_back(i.amount, epee::string_tools::pod_to_hex(i.real_out_tx_key));
}
return inputs;
}
std::vector<TransactionConstructionInfo::Output> TransactionConstructionInfoImpl::outputs() const {
std::vector<Output> outputs;
for (const auto &o : m_txcd.splitted_dsts) {
outputs.emplace_back(o.amount, o.address(m_wallet.m_wallet->nettype(), crypto::hash()));
}
return outputs;
}
}

@ -0,0 +1,32 @@
#ifndef WOWLET_TRANSACTION_CONSTRUCTION_INFO_H
#define WOWLET_TRANSACTION_CONSTRUCTION_INFO_H
#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
#include "wallet.h"
#include <string>
namespace Monero {
class TransactionConstructionInfoImpl : public TransactionConstructionInfo
{
public:
TransactionConstructionInfoImpl(WalletImpl &wallet, const tools::wallet2::tx_construction_data & ptx);
~TransactionConstructionInfoImpl() override;
uint64_t unlockTime() const override;
std::set<std::uint32_t> subaddressIndices() const override;
std::vector<std::string> subaddresses() const override;
uint64_t minMixinCount() const override;
std::vector<Input> inputs() const override;
std::vector<Output> outputs() const override;
private:
friend class WalletImpl;
WalletImpl &m_wallet;
tools::wallet2::tx_construction_data m_txcd;
};
}
#endif //WOWLET_TRANSACTION_CONSTRUCTION_INFO_H

@ -199,6 +199,9 @@ void TransactionHistoryImpl::refresh()
ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id)});
}
for (const auto &r: pd.m_rings) {
ti->m_rings.push_back({string_tools::pod_to_hex(r.first), cryptonote::relative_output_offsets_to_absolute(r.second)});
}
m_history.push_back(ti);
}
@ -229,10 +232,15 @@ void TransactionHistoryImpl::refresh()
ti->m_label = pd.m_subaddr_indices.size() == 1 ? m_wallet->m_wallet->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : "";
ti->m_timestamp = pd.m_timestamp;
ti->m_confirmations = 0;
for (const auto &d : pd.m_dests)
for (const auto &d : pd.m_dests) {
{
ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id)});
}
}
for (const auto &r: pd.m_rings) {
ti->m_rings.push_back({string_tools::pod_to_hex(r.first), cryptonote::relative_output_offsets_to_absolute(r.second)});
}
m_history.push_back(ti);
}
@ -266,3 +274,4 @@ void TransactionHistoryImpl::refresh()
}
} // namespace
}

@ -139,6 +139,11 @@ const std::vector<TransactionInfo::Transfer> &TransactionInfoImpl::transfers() c
return m_transfers;
}
const std::vector<std::pair<std::string, std::vector<uint64_t>>> &TransactionInfoImpl::rings() const
{
return m_rings;
}
uint64_t TransactionInfoImpl::confirmations() const
{
return m_confirmations;

@ -63,6 +63,8 @@ public:
virtual uint64_t confirmations() const override;
virtual uint64_t unlockTime() const override;
virtual const std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings() const override;
private:
int m_direction;
bool m_pending;
@ -81,6 +83,7 @@ private:
std::vector<Transfer> m_transfers;
uint64_t m_confirmations;
uint64_t m_unlock_time;
std::vector<std::pair<std::string, std::vector<uint64_t>>> m_rings;
friend class TransactionHistoryImpl;

@ -31,6 +31,7 @@
#include "unsigned_transaction.h"
#include "wallet.h"
#include "common_defines.h"
#include "transaction_construction_info.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
@ -315,4 +316,24 @@ uint64_t UnsignedTransactionImpl::minMixinCount() const
return min_mixin;
}
TransactionConstructionInfo * UnsignedTransactionImpl::transaction(int index) const {
if (index < 0)
return nullptr;
auto index_ = static_cast<unsigned>(index);
return index_ < m_constructionInfo.size() ? m_constructionInfo[index_] : nullptr;
}
void UnsignedTransactionImpl::refresh() {
for (auto t : m_constructionInfo)
delete t;
m_constructionInfo.clear();
for (const auto& p : m_unsigned_tx_set.txes)
m_constructionInfo.push_back(new TransactionConstructionInfoImpl(m_wallet, p));
}
std::vector<TransactionConstructionInfo*> UnsignedTransactionImpl::getAll() const {
return m_constructionInfo;
}
} // namespace

@ -55,6 +55,9 @@ public:
bool sign(const std::string &signedFileName) override;
std::string confirmationMessage() const override {return m_confirmationMessage;}
uint64_t minMixinCount() const override;
void refresh() override;
std::vector<TransactionConstructionInfo*> getAll() const override;
TransactionConstructionInfo * transaction(int index) const override;
private:
// Callback function to check all loaded tx's and generate confirmationMessage
@ -67,7 +70,7 @@ private:
std::string m_errorString;
tools::wallet2::unsigned_tx_set m_unsigned_tx_set;
std::string m_confirmationMessage;
std::vector<TransactionConstructionInfo*> m_constructionInfo;
};
}

@ -35,9 +35,11 @@
#include "transaction_history.h"
#include "address_book.h"
#include "subaddress.h"
#include "coins.h"
#include "subaddress_account.h"
#include "common_defines.h"
#include "common/util.h"
#include "string_coding.h"
#include "mnemonics/electrum-words.h"
#include "mnemonics/english.h"
@ -63,8 +65,8 @@ namespace {
static const int MAX_REFRESH_INTERVAL_MILLIS = 1000 * 60 * 1;
// Default refresh interval when connected to remote node
static const int DEFAULT_REMOTE_NODE_REFRESH_INTERVAL_MILLIS = 1000 * 10;
// Connection timeout 30 sec
static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 30;
// Connection timeout 10 sec
static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 10;
std::string get_default_ringdb_path(cryptonote::network_type nettype)
{
@ -163,11 +165,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount)
<< ", idx: " << subaddr_index);
// do not signal on received tx if wallet is not syncronized completely
if (m_listener && m_wallet->synchronized()) {
m_listener->moneyReceived(tx_hash, amount);
m_listener->updated();
}
m_listener->moneyReceived(tx_hash, amount);
m_listener->updated();
}
virtual 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)
@ -179,11 +178,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount)
<< ", idx: " << subaddr_index);
// 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();
}
m_listener->unconfirmedMoneyReceived(tx_hash, amount);
m_listener->updated();
}
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx,
@ -195,11 +191,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount)
<< ", idx: " << subaddr_index);
// do not signal on sent tx if wallet is not syncronized completely
if (m_listener && m_wallet->synchronized()) {
m_listener->moneySpent(tx_hash, amount);
m_listener->updated();
}
m_listener->moneySpent(tx_hash, amount);
m_listener->updated();
}
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx)
@ -433,15 +426,12 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
m_refreshEnabled = false;
m_addressBook.reset(new AddressBookImpl(this));
m_subaddress.reset(new SubaddressImpl(this));
m_coins.reset(new CoinsImpl(this));
m_subaddressAccount.reset(new SubaddressAccountImpl(this));
m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS;
m_refreshThread = boost::thread([this] () {
this->refreshThreadFunc();
});
}
WalletImpl::~WalletImpl()
@ -760,6 +750,35 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c
return status() == Status_Ok;
}
bool WalletImpl::recoverDeterministicWalletFromSpendKey(const std::string &path, const std::string &password, const std::string &language, const std::string &spendkey_string)
{
clearStatus();
m_errorString.clear();
m_recoveringFromSeed = true;
m_recoveringFromDevice = false;
// parse spend key
crypto::secret_key spendkey;
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))
{
setStatusError(tr("failed to parse secret spend key"));
return false;
}
spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
}
try {
m_wallet->generate(path, password, spendkey, true, false);
setSeedLanguage(language);
} catch (const std::exception &e) {
setStatusCritical(e.what());
}
return status() == Status_Ok;
}
bool WalletImpl::close(bool store)
{
@ -836,6 +855,11 @@ bool WalletImpl::setPassword(const std::string &password)
return status() == Status_Ok;
}
std::string WalletImpl::getPassword() const
{
return m_password;
}
bool WalletImpl::setDevicePin(const std::string &pin)
{
clearStatus();
@ -863,6 +887,27 @@ std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) co
return m_wallet->get_subaddress_as_str({accountIndex, addressIndex});
}
bool WalletImpl::subaddressIndex(std::string address, std::pair<uint32_t, uint32_t> &index) const
{
clearStatus();
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address)) {
setStatusError(tr("Failed to parse address"));
return false;
}
auto i = m_wallet->get_subaddress_index(info.address);
if (!i) {
setStatusError(tr("Address doesn't belong to the wallet"));
return false;
}
index.first = i->major;
index.second = i->minor;
return true;
}
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
{
crypto::hash8 pid;
@ -1031,7 +1076,7 @@ uint64_t WalletImpl::daemonBlockChainHeight() const
if(m_wallet->light_wallet()) {
return m_wallet->get_light_wallet_scanned_block_height();
}
if (!m_is_connected)
if (!m_is_connected && m_synchronized)
return 0;
std::string err;
uint64_t result = m_wallet->get_daemon_blockchain_height(err);
@ -1050,7 +1095,7 @@ uint64_t WalletImpl::daemonBlockChainTargetHeight() const
if(m_wallet->light_wallet()) {
return m_wallet->get_light_wallet_blockchain_height();
}
if (!m_is_connected)
if (!m_is_connected && m_synchronized)
return 0;
std::string err;
uint64_t result = m_wallet->get_daemon_blockchain_target_height(err);
@ -1147,6 +1192,48 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
return transaction;
}
UnsignedTransaction *WalletImpl::loadUnsignedTxFromStr(const std::string &unsigned_tx) {
clearStatus();
UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this);
if (!m_wallet->parse_unsigned_tx_from_str(unsigned_tx, transaction->m_unsigned_tx_set)) {
setStatusError(tr("Failed to load unsigned transactions"));
transaction->m_status = UnsignedTransaction::Status::Status_Error;
transaction->m_errorString = errorString();
return transaction;
}
// Check tx data and construct confirmation message
std::string extra_message;
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();
transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message);
setStatus(transaction->status(), transaction->errorString());
return transaction;
}
UnsignedTransaction *WalletImpl::loadUnsignedTxFromBase64Str(const std::string &unsigned_tx_base64) {
clearStatus();
std::string decoded_tx = epee::string_encoding::base64_decode(unsigned_tx_base64);
return this->loadUnsignedTxFromStr(decoded_tx);
}
PendingTransaction *WalletImpl::loadSignedTx(const std::string &signed_filename) {
clearStatus();
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
if (!m_wallet->load_tx(signed_filename, transaction->m_pending_tx)) {
setStatusError(tr("Failed to load unsigned transactions"));
return transaction;
}
return transaction;
}
bool WalletImpl::submitTransaction(const string &fileName) {
clearStatus();
std::unique_ptr<PendingTransactionImpl> transaction(new PendingTransactionImpl(*this));
@ -1175,7 +1262,7 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all)
try
{
if (!m_wallet->export_key_images(filename), all)
if (!m_wallet->export_key_images(filename, all))
{
setStatusError(tr("failed to save file ") + filename);
return false;
@ -1275,6 +1362,91 @@ bool WalletImpl::importOutputs(const string &filename)
return true;
}
bool WalletImpl::importTransaction(const std::string &txid, std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen)
{
try
{
m_wallet->import_tx(txid, o_indices, height, block_version, ts, miner_tx, pool, double_spend_seen);
}
catch (const std::exception &e)
{
LOG_ERROR("Failed to import transaction: " << e.what());
setStatusError(string(tr("Failed to import transaction: ")) + e.what());
return false;
}
return true;
}
std::string WalletImpl::printBlockchain()
{
return m_wallet->printBlockchain();
}
std::string WalletImpl::printTransfers()
{
return m_wallet->printTransfers();
}
std::string WalletImpl::printPayments()
{
return m_wallet->printPayments();
}
std::string WalletImpl::printUnconfirmedPayments()
{
return m_wallet->printUnconfirmedPayments();
}
std::string WalletImpl::printConfirmedTransferDetails()
{
return m_wallet->printConfirmedTransferDetails();
}
std::string WalletImpl::printUnconfirmedTransferDetails()
{
return m_wallet->printUnconfirmedTransferDetails();
}
std::string WalletImpl::printPubKeys()
{
return m_wallet->printPubKeys();
}
std::string WalletImpl::printTxNotes()
{
return m_wallet->printTxNotes();
}
std::string WalletImpl::printSubaddresses()
{
return m_wallet->printSubaddresses();
}
std::string WalletImpl::printSubaddressLabels()
{
return m_wallet->printSubaddressLabels();
}
std::string WalletImpl::printAdditionalTxKeys()
{
return m_wallet->printAdditionalTxKeys();
}
std::string WalletImpl::printAttributes()
{
return m_wallet->printAttributes();
}
std::string WalletImpl::printKeyImages()
{
return m_wallet->printKeyImages();
}
std::string WalletImpl::printAccountTags()
{
return m_wallet->printAccountTags();
}
std::string WalletImpl::printTxKeys()
{
return m_wallet->printTxKeys();
}
std::string WalletImpl::printAddressBook()
{
return m_wallet->printAddressBook();
}
std::string WalletImpl::printScannedPoolTxs()
{
return m_wallet->printScannedPoolTxs();
}
void WalletImpl::addSubaddressAccount(const std::string& label)
{
m_wallet->add_subaddress_account(label);
@ -1652,6 +1824,137 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
return createTransactionMultDest(std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, priority, subaddr_account, subaddr_indices);
}
PendingTransaction *WalletImpl::createTransactionSingle(const string &key_image, const string &dst_addr,
const size_t outputs, PendingTransaction::Priority priority)
{
clearStatus();
// Pause refresh thread while creating transaction
pauseRefresh();
cryptonote::address_parse_info info;
size_t fake_outs_count = m_wallet->adjust_mixin(m_wallet->default_mixin());
//uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast<uint32_t>(priority));
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
do {
std::vector<uint8_t> extra;
std::string extra_nonce;
vector<cryptonote::tx_destination_entry> dsts;
bool error = false;
crypto::key_image ki;
if (!epee::string_tools::hex_to_pod(key_image, ki))
{
setStatusError(tr("failed to parse key image"));
error = true;
break;
}
if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), dst_addr))
{
setStatusError(tr("Invalid destination address"));
error = true;
break;
}
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);
}
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 {
transaction->m_pending_tx = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress,
outputs, fake_outs_count, 0 /* unlock time */, priority, extra);
pendingTxPostProcess(transaction);
if (multisig().isMultisig) {
auto tx_set = m_wallet->make_multisig_tx_set(transaction->m_pending_tx);
transaction->m_pending_tx = tx_set.m_ptx;
transaction->m_signers = tx_set.m_signers;
}
} 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 &e) {
setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str());
} catch (const tools::error::not_enough_unlocked_money& e) {
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) {
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) {
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;
}
writer << "\n" << tr("Please sweep unmixable outputs.");
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(string(tr("unknown transfer error: ")) + e.what());
} catch (const tools::error::wallet_internal_error& e) {
setStatusError(string(tr("internal error: ")) + e.what());
} catch (const std::exception& e) {
setStatusError(string(tr("unexpected error: ")) + e.what());
} catch (...) {
setStatusError(tr("unknown error"));
}
} while (false);
statusWithErrorString(transaction->m_status, transaction->m_errorString);
// Resume refresh thread
startRefresh();
return transaction;
}
PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
{
@ -1755,6 +2058,7 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::str
extra_size,
m_wallet->use_fork_rules(8, 0),
m_wallet->use_fork_rules(HF_VERSION_CLSAG, 0),
true,
m_wallet->get_base_fee(),
m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))),
m_wallet->get_fee_quantization_mask());
@ -1770,6 +2074,11 @@ AddressBook *WalletImpl::addressBook()
return m_addressBook.get();
}
Coins *WalletImpl::coins()
{
return m_coins.get();
}
Subaddress *WalletImpl::subaddress()
{
return m_subaddress.get();
@ -2238,37 +2547,34 @@ void WalletImpl::refreshThreadFunc()
void WalletImpl::doRefresh()
{
bool success = true;
bool rescan = m_refreshShouldRescan.exchange(false);
// synchronizing async and sync refresh calls
boost::lock_guard<boost::mutex> guarg(m_refreshMutex2);
do try {
LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<<rescan);
// Syncing daemon and refreshing wallet simultaneously is very resource intensive.
// Disable refresh if wallet is disconnected or daemon isn't synced.
if (m_wallet->light_wallet() || daemonSynced()) {
if(rescan)
m_wallet->rescan_blockchain(false);
m_wallet->refresh(trustedDaemon());
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();
}
m_wallet->find_and_save_rings(false);
} else {
LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced");
if(rescan)
m_wallet->rescan_blockchain(false);
m_wallet->refresh(trustedDaemon());
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();
}
m_wallet->find_and_save_rings(false);
} catch (const std::exception &e) {
success = false;
setStatusError(e.what());
break;
}while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested
m_is_connected = success;
if (m_wallet2Callback->getListener()) {
m_wallet2Callback->getListener()->refreshed();
m_wallet2Callback->getListener()->refreshed(success);
}
}
@ -2331,8 +2637,14 @@ void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending)
bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit, bool ssl)
{
if (!m_wallet->init(daemon_address, m_daemon_login, proxy_address, upper_transaction_size_limit))
return false;
if (!m_wallet->init(daemon_address,
m_daemon_login,
proxy_address,
upper_transaction_size_limit,
trustedDaemon(),
ssl ? epee::net_utils::ssl_support_t::e_ssl_support_autodetect : epee::net_utils::ssl_support_t::e_ssl_support_disabled)) {
return false;
}
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
// If daemon isn't synced a calculated block height will be used instead

@ -46,8 +46,11 @@ class PendingTransactionImpl;
class UnsignedTransactionImpl;
class AddressBookImpl;
class SubaddressImpl;
class CoinsImpl;
class SubaddressAccountImpl;
struct Wallet2CallbackImpl;
class PendingTransactionInfoImpl;
class TransactionConstructionInfoImpl;
class WalletImpl : public Wallet
{
@ -76,6 +79,10 @@ public:
const std::string &address_string,
const std::string &viewkey_string,
const std::string &spendkey_string = "");
bool recoverDeterministicWalletFromSpendKey(const std::string &path,
const std::string &password,
const std::string &language,
const std::string &spendkey_string);
bool recoverFromDevice(const std::string &path,
const std::string &password,
const std::string &device_name);
@ -89,9 +96,11 @@ public:
std::string errorString() const override;
void statusWithErrorString(int& status, std::string& errorString) const override;
bool setPassword(const std::string &password) override;
std::string getPassword() const override;
bool setDevicePin(const std::string &password) override;
bool setDevicePassphrase(const std::string &password) override;
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
bool subaddressIndex(std::string address, std::pair<uint32_t, uint32_t> &index) const override;
std::string integratedAddress(const std::string &payment_id) const override;
std::string secretViewKey() const override;
std::string publicViewKey() const override;
@ -162,19 +171,46 @@ public:
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) override;
PendingTransaction * createTransactionSingle(const std::string &key_image, const std::string &dst_addr,
size_t outputs = 1, PendingTransaction::Priority priority = PendingTransaction::Priority_Low) override;
virtual PendingTransaction * createSweepUnmixableTransaction() override;
bool submitTransaction(const std::string &fileName) override;
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override;
virtual UnsignedTransaction * loadUnsignedTxFromStr(const std::string &unsigned_tx) override;
virtual UnsignedTransaction * loadUnsignedTxFromBase64Str(const std::string &unsigned_tx) override;
virtual PendingTransaction * loadSignedTx(const std::string &signed_filename) override;
bool exportKeyImages(const std::string &filename, bool all = false) override;
bool importKeyImages(const std::string &filename) override;
bool exportOutputs(const std::string &filename, bool all = false) override;
bool importOutputs(const std::string &filename) override;
bool importTransaction(const std::string &txid, std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) override;
virtual std::string printBlockchain() override;
virtual std::string printTransfers() override;
virtual std::string printPayments() override;
virtual std::string printUnconfirmedPayments() override;
virtual std::string printConfirmedTransferDetails() override;
virtual std::string printUnconfirmedTransferDetails() override;
virtual std::string printPubKeys() override;
virtual std::string printTxNotes() override;
virtual std::string printSubaddresses() override;
virtual std::string printSubaddressLabels() override;
virtual std::string printAdditionalTxKeys() override;
virtual std::string printAttributes() override;
virtual std::string printKeyImages() override;
virtual std::string printAccountTags() override;
virtual std::string printTxKeys() override;
virtual std::string printAddressBook() override;
virtual std::string printScannedPoolTxs() override;
virtual void disposeTransaction(PendingTransaction * t) override;
virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
PendingTransaction::Priority priority) const override;
virtual TransactionHistory * history() override;
virtual AddressBook * addressBook() override;
virtual Coins * coins() override;
virtual Subaddress * subaddress() override;
virtual SubaddressAccount * subaddressAccount() override;
virtual void setListener(WalletListener * l) override;
@ -244,7 +280,10 @@ private:
friend struct Wallet2CallbackImpl;
friend class AddressBookImpl;
friend class SubaddressImpl;
friend class CoinsImpl;
friend class SubaddressAccountImpl;
friend class PendingTransactionInfoImpl;
friend class TransactionConstructionInfoImpl;
std::unique_ptr<tools::wallet2> m_wallet;
mutable boost::mutex m_statusMutex;
@ -255,6 +294,7 @@ private:
std::unique_ptr<Wallet2CallbackImpl> m_wallet2Callback;
std::unique_ptr<AddressBookImpl> m_addressBook;
std::unique_ptr<SubaddressImpl> m_subaddress;
std::unique_ptr<CoinsImpl> m_coins;
std::unique_ptr<SubaddressAccountImpl> m_subaddressAccount;
// multi-threaded refresh stuff

@ -66,6 +66,47 @@ enum NetworkType : uint8_t {
bool set;
};
/*
* @brief Transaction construction data
*/
struct TransactionConstructionInfo
{
struct Input {
Input(uint64_t _amount, const std::string &_pubkey);
const uint64_t amount;
const std::string pubkey;
};
struct Output {
Output(uint64_t _amount, const std::string &_address);
const uint64_t amount;
const std::string address;
};
virtual ~TransactionConstructionInfo() = 0;
virtual uint64_t unlockTime() const = 0;
virtual std::set<std::uint32_t> subaddressIndices() const = 0;
virtual std::vector<std::string> subaddresses() const = 0;
virtual uint64_t minMixinCount() const = 0;
virtual std::vector<Input> inputs() const = 0;
virtual std::vector<Output> outputs() const = 0;
};
/*
* @brief Detailed pending transaction information
*/
struct PendingTransactionInfo
{
virtual ~PendingTransactionInfo() = 0;
virtual uint64_t fee() const = 0;
virtual uint64_t dust() const = 0;
virtual bool dustAddedToFee() const = 0;
virtual std::string txKey() const = 0;
virtual TransactionConstructionInfo * constructionData() const = 0;
};
/**
* @brief Transaction-like interface for sending money
*/
@ -101,6 +142,13 @@ struct PendingTransaction
virtual uint64_t txCount() const = 0;
virtual std::vector<uint32_t> subaddrAccount() const = 0;
virtual std::vector<std::set<uint32_t>> subaddrIndices() const = 0;
virtual std::string unsignedTxToBin() const = 0;
virtual std::string unsignedTxToBase64() const = 0;
virtual std::string signedTxToHex(int index) const = 0;
virtual size_t signedTxSize(int index) const = 0;
virtual PendingTransactionInfo * transaction(int index) const = 0;
virtual void refresh() = 0;
virtual std::vector<PendingTransactionInfo*> getAll() const = 0;
/**
* @brief multisigSignData
@ -160,6 +208,9 @@ struct UnsignedTransaction
* return - true on success
*/
virtual bool sign(const std::string &signedFileName) = 0;
virtual void refresh() = 0;
virtual std::vector<TransactionConstructionInfo*> getAll() const = 0;
virtual TransactionConstructionInfo * transaction(int index) const = 0;
};
/**
@ -198,6 +249,7 @@ struct TransactionInfo
virtual std::string paymentId() const = 0;
//! only applicable for output transactions
virtual const std::vector<Transfer> & transfers() const = 0;
virtual const std::vector<std::pair<std::string, std::vector<uint64_t>>> & rings() const = 0;
};
/**
* @brief The TransactionHistory - interface for displaying transaction history
@ -260,22 +312,66 @@ struct AddressBook
virtual int lookupPaymentID(const std::string &payment_id) const = 0;
};
/**
* @brief The CoinsInfo - interface for displaying coins information
*/
struct CoinsInfo
{
virtual ~CoinsInfo() = 0;
virtual uint64_t blockHeight() const = 0;
virtual std::string hash() const = 0;
virtual size_t internalOutputIndex() const = 0;
virtual uint64_t globalOutputIndex() const = 0;
virtual bool spent() const = 0;
virtual bool frozen() const = 0;
virtual uint64_t spentHeight() const = 0;
virtual uint64_t amount() const = 0;
virtual bool rct() const = 0;
virtual bool keyImageKnown() const = 0;
virtual size_t pkIndex() const = 0;
virtual uint32_t subaddrIndex() const = 0;
virtual uint32_t subaddrAccount() const = 0;
virtual std::string address() const = 0;
virtual std::string addressLabel() const = 0;
virtual std::string keyImage() const = 0;
virtual uint64_t unlockTime() const = 0;
virtual bool unlocked() const = 0;
virtual std::string pubKey() const = 0;
virtual bool coinbase() const = 0;
};
struct Coins
{
virtual ~Coins() = 0;
virtual int count() const = 0;
virtual CoinsInfo * coin(int index) const = 0;
virtual std::vector<CoinsInfo*> getAll() const = 0;
virtual void refresh() = 0;
virtual void setFrozen(int index) = 0;
virtual void thaw(int index) = 0;
virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0;
};
struct SubaddressRow {
public:
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label):
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label, bool _used):
m_rowId(_rowId),
m_address(_address),
m_label(_label) {}
m_label(_label),
m_used(_used) {}
private:
std::size_t m_rowId;
std::string m_address;
std::string m_label;
bool m_used;
public:
std::string extra;
std::string getAddress() const {return m_address;}
std::string getLabel() const {return m_label;}
std::size_t getRowId() const {return m_rowId;}
bool isUsed() const {return m_used;}
};
struct Subaddress
@ -382,7 +478,7 @@ struct WalletListener
/**
* @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously
*/
virtual void refreshed() = 0;
virtual void refreshed(bool success) = 0;
/**
* @brief called by device if the action is required
@ -456,9 +552,11 @@ struct Wallet
//! returns both error and error string atomically. suggested to use in instead of status() and errorString()
virtual void statusWithErrorString(int& status, std::string& errorString) const = 0;
virtual bool setPassword(const std::string &password) = 0;
virtual std::string getPassword() const = 0;
virtual bool setDevicePin(const std::string &pin) { (void)pin; return false; };
virtual bool setDevicePassphrase(const std::string &passphrase) { (void)passphrase; return false; };
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
virtual bool subaddressIndex(std::string address, std::pair<uint32_t, uint32_t> &index) const = 0;
std::string mainAddress() const { return address(0, 0); }
virtual std::string path() const = 0;
virtual NetworkType nettype() const = 0;
@ -868,6 +966,18 @@ struct Wallet
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0;
/*!
* \brief createTransactionSingle creates transaction with single input
* \param key_image key image as string
* \param dst_addr destination address as string
* \param priority
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
* after object returned
*/
virtual PendingTransaction * createTransactionSingle(const std::string &key_image, const std::string &dst_addr,
size_t outputs = 1, PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0;
/*!
* \brief createSweepUnmixableTransaction creates transaction with unmixable outputs.
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
@ -882,7 +992,27 @@ struct Wallet
* after object returned
*/
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0;
/*!
* \brief loadUnsignedTx - creates transaction from unsigned tx string
* \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status()
* after object returned
*/
virtual UnsignedTransaction * loadUnsignedTxFromStr(const std::string &unsigned_tx) = 0;
/*!
* \brief loadUnsignedTx - creates transaction from unsigned base64 encoded tx string
* \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status()
* after object returned
*/
virtual UnsignedTransaction * loadUnsignedTxFromBase64Str(const std::string &unsigned_tx_base64) = 0;
/*!
* \brief loadSignedTx - creates transaction from signed tx file
* \return - PendingTransaction object.
*/
virtual PendingTransaction * loadSignedTx(const std::string &signed_filename) = 0;
/*!
* \brief submitTransaction - submits transaction in signed tx file
* \return - true on success
@ -933,8 +1063,29 @@ struct Wallet
*/
virtual bool importOutputs(const std::string &filename) = 0;
virtual bool importTransaction(const std::string &txid, std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) = 0;
virtual std::string printBlockchain() = 0;
virtual std::string printTransfers() = 0;
virtual std::string printPayments() = 0;
virtual std::string printUnconfirmedPayments() = 0;
virtual std::string printConfirmedTransferDetails() = 0;
virtual std::string printUnconfirmedTransferDetails() = 0;
virtual std::string printPubKeys() = 0;
virtual std::string printTxNotes() = 0;
virtual std::string printSubaddresses() = 0;
virtual std::string printSubaddressLabels() = 0;
virtual std::string printAdditionalTxKeys() = 0;
virtual std::string printAttributes() = 0;
virtual std::string printKeyImages() = 0;
virtual std::string printAccountTags() = 0;
virtual std::string printTxKeys() = 0;
virtual std::string printAddressBook() = 0;
virtual std::string printScannedPoolTxs() = 0;
virtual TransactionHistory * history() = 0;
virtual AddressBook * addressBook() = 0;
virtual Coins * coins() = 0;
virtual Subaddress * subaddress() = 0;
virtual SubaddressAccount * subaddressAccount() = 0;
virtual void setListener(WalletListener *) = 0;
@ -991,7 +1142,8 @@ struct Wallet
/*
* \brief signMessage - sign a message with the spend private key
* \param message - the message to sign (arbitrary byte data)
* \return the signature
* \param address - the address to make the signature with, defaults to primary address (optional)
* \return the signature, empty string if the address is invalid or does not belong to the wallet
*/
virtual std::string signMessage(const std::string &message, const std::string &address = "") = 0;
/*!
@ -1200,6 +1352,25 @@ struct WalletManager
return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString);
}
/*!
* \brief recover deterministic wallet from spend key.
* \param path Name of wallet file to be created
* \param password Password of wallet file
* \param language language
* \param nettype Network type
* \param restoreHeight restore from start height
* \param spendKeyString spend key
* \param kdf_rounds Number of rounds for key derivation function
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path,
const std::string &password,
const std::string &language,
NetworkType nettype,
uint64_t restoreHeight,
const std::string &spendKeyString,
uint64_t kdf_rounds = 1) = 0;
/*!
* \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead
* \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted

@ -122,6 +122,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
return wallet;
}
Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path,
const std::string &password,
const std::string &language,
NetworkType nettype,
uint64_t restoreHeight,
const std::string &spendkey_string,
uint64_t kdf_rounds)
{
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
}
wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string);
return wallet;
}
Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
const std::string &password,
NetworkType nettype,

@ -67,6 +67,13 @@ public:
const std::string &addressString,
const std::string &viewKeyString,
const std::string &spendKeyString = "") override;
virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path,
const std::string &password,
const std::string &language,
NetworkType nettype,
uint64_t restoreHeight,
const std::string &spendkey_string,
uint64_t kdf_rounds) override;
virtual Wallet * createWalletFromDevice(const std::string &path,
const std::string &password,
NetworkType nettype,

@ -51,7 +51,7 @@ using namespace epee;
namespace tools
{
static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
static const std::chrono::seconds rpc_timeout = std::chrono::seconds(10);
NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::abstract_http_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex)
: m_http_client(http_client)
@ -162,7 +162,7 @@ boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height)
auto res = get_info();
if (res)
return res;
height = m_target_height;
height = m_target_height > m_height ? m_target_height : m_height;
return boost::optional<std::string>();
}

@ -1537,6 +1537,14 @@ void wallet2::add_subaddress_account(const std::string& label)
m_subaddress_labels[index_major][0] = label;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::get_subaddress_used(const cryptonote::subaddress_index& index)
{
return std::find_if(m_transfers.begin(), m_transfers.end(),
[this, index](const transfer_details &td) {
return td.m_subaddr_index == index;
}) != m_transfers.end();
}
//----------------------------------------------------------------------------------------------------
void wallet2::add_subaddress(uint32_t index_major, const std::string& label)
{
THROW_WALLET_EXCEPTION_IF(index_major >= m_subaddress_labels.size(), error::account_index_outofbound);
@ -2169,13 +2177,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
if (!pool)
{
m_transfers.push_back(transfer_details{});
transfer_details& td = m_transfers.back();
td.m_block_height = height;
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
m_transfers.push_back(transfer_details{});
transfer_details& td = m_transfers.back();
td.m_block_height = height;
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
td.m_key_image = tx_scan_info[o].ki;
td.m_key_image_known = !m_watch_only && !m_multisig;
if (!td.m_key_image_known)
@ -2233,6 +2242,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1);
}
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
lock.unlock();
if (0 != m_callback)
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time);
}
@ -2271,12 +2282,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t extra_amount = amount - m_transfers[kit->second].amount();
if (!pool)
{
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
transfer_details &td = m_transfers[kit->second];
td.m_block_height = height;
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
td.m_block_height = height;
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
td.m_amount = amount;
td.m_pk_index = pk_index - 1;
td.m_subaddr_index = tx_scan_info[o].received->index;
@ -3212,6 +3224,54 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
}
MTRACE("update_pool_state end");
}
//----------------------------------------------------------------------------------------------------
void wallet2::import_tx(const std::string &txid, std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen)
{
crypto::hash hash;
epee::string_tools::hex_to_pod(txid, hash);
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(hash));
req.decode_as_json = false;
req.prune = true;
bool r;
{
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
if (r && res.status == CORE_RPC_STATUS_OK)
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
}
MDEBUG("Got " << r << " and " << res.status);
if (!(r && res.status == CORE_RPC_STATUS_OK)) {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Error calling gettransactions daemon RPC: r " + std::to_string(r) + ", status " + get_rpc_status(res.status));
}
if (res.txs.size() != 1) {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Expected 1 tx, got " + std::to_string(res.txs.size()));
}
const auto &tx_entry = res.txs[0];
cryptonote::transaction tx;
cryptonote::blobdata bd;
crypto::hash tx_hash;
if (!get_pruned_tx(tx_entry, tx, tx_hash)) {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to parse transaction from daemon");
}
if (tx_hash != hash) {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Got txid " + epee::string_tools::pod_to_hex(tx_hash) + " which we did not ask for");
}
process_new_transaction(tx_hash, tx, o_indices, height, block_version, ts, miner_tx, pool, double_spend_seen, {});
}
//----------------------------------------------------------------------------------------------------
void wallet2::process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs)
{
@ -3235,11 +3295,19 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
{
std::vector<crypto::hash> hashes;
const uint64_t checkpoint_height = m_checkpoints.get_max_height();
// Get highest checkpoint that is lower than stop_height
uint64_t checkpoint_height = 0;
for (auto i : m_checkpoints.get_points()) {
if (i.first > stop_height) {
break;
}
checkpoint_height = i.first;
}
if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force)
{
// we will drop all these, so don't bother getting them
uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size();
uint64_t missing_blocks = checkpoint_height - m_blockchain.size();
while (missing_blocks-- > 0)
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
@ -5504,6 +5572,315 @@ void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee
bool r = store_keys(new_keys_filename, password, true);
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, new_keys_filename);
}
std::string wallet2::printBlockchain()
{
std::string blstr;
blstr += "offset: " + std::to_string(m_blockchain.offset()) + "\n";
blstr += "genesis: " + string_tools::pod_to_hex(m_blockchain.genesis()) + "\n";
for (size_t i = m_blockchain.offset(); i < m_blockchain.size(); i++) {
blstr += std::to_string(i) + " : " + string_tools::pod_to_hex(m_blockchain[i]) + "\n";
}
return blstr;
}
std::string wallet2::printTransfers()
{
std::string str;
for (const auto &td : m_transfers) {
str += "block_height: " + std::to_string(td.m_block_height) + "\n";
str += printTxPrefix(td.m_tx);
str += "txid: " + string_tools::pod_to_hex(td.m_txid) + "\n";
str += "internal_output_index: " + std::to_string(td.m_internal_output_index) + "\n";
str += "global_output_index: " + std::to_string(td.m_global_output_index) + "\n";
str += "spent: " + std::to_string(td.m_spent) + "\n";
str += "frozen: " + std::to_string(td.m_frozen) + "\n";
str += "spent_height: " + std::to_string(td.m_spent_height) + "\n";
str += "key_image: " + string_tools::pod_to_hex(td.m_key_image) + "\n";
str += "mask: " + string_tools::pod_to_hex(td.m_mask) + "\n";
str += "amount: " + std::to_string(td.m_amount) + "\n";
str += "rct: " + std::to_string(td.m_rct) + "\n";
str += "key_image_known: " + std::to_string(td.m_key_image_known) + "\n";
str += "key_image_request: " + std::to_string(td.m_key_image_request) + "\n";
str += "pk_index: " + std::to_string(td.m_pk_index) + "\n";
str += "subaddr_index: " + std::to_string(td.m_subaddr_index.major) + "," + std::to_string(td.m_subaddr_index.minor) + "\n";
str += "key_image_partial: " + std::to_string(td.m_key_image_partial) + "\n";
str += "multisig_k:\n";
for (const auto &el : td.m_multisig_k) {
str += " " + string_tools::pod_to_hex(el) + "\n";
}
str += "multisig_info:\n";
for (const auto &el : td.m_multisig_info) {
str += " signer: " + string_tools::pod_to_hex(el.m_signer) + "\n";
str += " LR:\n";
for (const auto &em : el.m_LR) {
str += " L: " + string_tools::pod_to_hex(em.m_L) + "\n";
str += " R: " + string_tools::pod_to_hex(em.m_R) + "\n";
}
str += "\n";
str += " partial_key_images:\n";
for (const auto &em : el.m_partial_key_images) {
str += " " + string_tools::pod_to_hex(em) + "\n";
}
str += "\n";
}
str += "uses:\n";
for (const auto &el : td.m_uses) {
str += " " + std::to_string(el.first) + " : " + string_tools::pod_to_hex(el.second) + "\n";
}
str += "\n";
}
return str;
}
std::string wallet2::printUnconfirmedPayments()
{
std::string str;
for (const auto &el : m_unconfirmed_payments) {
auto ppd = el.second;
str += "double_spend_seen: " + std::to_string(ppd.m_double_spend_seen) + "\n";
str += printPaymentDetails(ppd.m_pd);
str += "\n";
}
return str;
}
std::string wallet2::printConfirmedTransferDetails()
{
std::string str;
for (const auto &el : m_confirmed_txs) {
auto ctd = el.second;
str += "amount_in: " + std::to_string(ctd.m_amount_in) + "\n";
str += "amount_out: " + std::to_string(ctd.m_amount_out) + "\n";
str += "change: " + std::to_string(ctd.m_change) + "\n";
str += "block_height: " + std::to_string(ctd.m_block_height) + "\n";
str += "dests:\n";
for (const auto &em : ctd.m_dests) {
str += printTxDestinationEntry(em);
}
str += "payment_id: " + string_tools::pod_to_hex(ctd.m_payment_id) + "\n";
str += "timestamp: " + std::to_string(ctd.m_timestamp) + "\n";
str += "unlock_time: " + std::to_string(ctd.m_unlock_time) + "\n";
str += "subaddr_account: " + std::to_string(ctd.m_subaddr_account) + "\n";
str += "subaddr_indices: ";
for (auto em : ctd.m_subaddr_indices) {
str += std::to_string(em);
}
str += "\n\n";
}
return str;
}
std::string wallet2::printUnconfirmedTransferDetails()
{
std::string str;
for (const auto &el : m_unconfirmed_txs) {
auto utd = el.second;
str += printTxPrefix(utd.m_tx);
str += "amount_in: " + std::to_string(utd.m_amount_in) + "\n";
str += "amount_out: " + std::to_string(utd.m_amount_out) + "\n";
str += "change: " + std::to_string(utd.m_change) + "\n";
str += "sent_time: " + std::to_string(utd.m_sent_time) + "\n";
str += "dests:\n";
for (const auto &em : utd.m_dests) {
str += printTxDestinationEntry(em);
}
str += "payment_id: " + string_tools::pod_to_hex(utd.m_payment_id) + "\n";
str += "timestamp: " + std::to_string(utd.m_timestamp) + "\n";
str += "subaddr_account: " + std::to_string(utd.m_subaddr_account) + "\n";
str += "subaddr_indices: ";
for (auto em : utd.m_subaddr_indices) {
str += std::to_string(em);
}
str += "\n\n";
}
return str;
}
std::string wallet2::printPayments()
{
std::string str;
for (const auto &el : m_payments) {
str += printPaymentDetails(el.second) + "\n";
}
return str;
}
std::string wallet2::printPubKeys()
{
std::string str;
vector<std::pair<crypto::public_key, size_t>> v;
for (const auto &el : m_pub_keys) {
v.push_back(el);
}
std::sort(v.begin(), v.end(),
[](std::pair<crypto::public_key, size_t> a, std::pair<crypto::public_key, size_t> b){return a.second < b.second;});
for (const auto &el : v){
str += string_tools::pod_to_hex(el.first) + " : " + boost::to_string(el.second) + "\n";
}
return str;
}
std::string wallet2::printTxNotes()
{
std::string str;
for (std::pair<crypto::hash, std::string> el : m_tx_notes) {
str += string_tools::pod_to_hex(el.first) + " : " + el.second + "\n";
}
return str;
}
std::string wallet2::printSubaddresses()
{
std::string str;
vector<std::pair<crypto::public_key, cryptonote::subaddress_index>> v;
for (const auto &el : m_subaddresses) {
v.push_back(el);
}
std::sort(v.begin(), v.end(), [](std::pair<crypto::public_key, cryptonote::subaddress_index> a, std::pair<crypto::public_key, cryptonote::subaddress_index> b) {
if (a.second.major == b.second.major) {
return a.second.minor < b.second.minor;
}
return a.second.major < b.second.major;
});
for (const auto &el : v) {
str += string_tools::pod_to_hex(el.first) + " : " + std::to_string(el.second.major) + "," + std::to_string(el.second.minor) + "\n";
}
return str;
}
std::string wallet2::printSubaddressLabels()
{
std::string str;
for (size_t i = 0; i < m_subaddress_labels.size(); i++) {
for (size_t j = 0; j < m_subaddress_labels[i].size(); j++) {
str += std::to_string(i) + "," + std::to_string(j) + " : " + m_subaddress_labels[i][j] + "\n";
}
}
return str;
}
std::string wallet2::printAdditionalTxKeys()
{
std::string str;
for (std::pair<crypto::hash, std::vector<crypto::secret_key>> el : m_additional_tx_keys) {
str += "Txid: " + string_tools::pod_to_hex(el.first) + " (" + std::to_string(el.second.size()) + ")\n";
for (auto em : el.second) {
str += " " + string_tools::pod_to_hex(em) + "\n";
}
}
return str;
}
std::string wallet2::printAttributes()
{
std::string str;
for (auto el : m_attributes) {
str += el.first + " : " + el.second + "\n";
}
return str;
}
std::string wallet2::printKeyImages()
{
std::string str;
vector<std::pair<crypto::key_image, size_t>> v;
for (const auto &el : m_key_images) {
v.push_back(el);
}
std::sort(v.begin(), v.end(), [](std::pair<crypto::key_image, size_t> a, std::pair<crypto::key_image, size_t> b){return a.second < b.second;});
for (const auto &el: v) {
str += string_tools::pod_to_hex(el.first) + " : " + boost::to_string(el.second) + "\n";
}
return str;
}
std::string wallet2::printAccountTags()
{
std::string str;
for (size_t i = 0; i < m_account_tags.second.size(); i++) {
str += std::to_string(i) + " : " + m_account_tags.second[i] + "\n";
}
return str;
}
std::string wallet2::printTxKeys()
{
std::string str;
for (std::pair<crypto::hash, crypto::secret_key> el : m_tx_keys) {
str += string_tools::pod_to_hex(el.first) + " : " + string_tools::pod_to_hex(el.second) + "\n";
}
return str;
}
std::string wallet2::printAddressBook()
{
std::string str;
for (auto el : m_address_book) {
str += "address: " + string_tools::pod_to_hex(el.m_address) + "\n";
str += "payment_id: " + string_tools::pod_to_hex(el.m_payment_id) + "\n";
str += "description: " + el.m_description + "\n";
str += "is_subaddress: " + std::to_string(el.m_is_subaddress) + "\n";
str += "has_payment_id: " + std::to_string(el.m_has_payment_id) + "\n";
str += "\n";
}
return str;
}
std::string wallet2::printScannedPoolTxs()
{
std::string str;
for (size_t i = 0; i < 2; i++) {
str += "scanned_pool_txs[" + std::to_string(i) + "]\n";
for (auto el : m_scanned_pool_txs[i]) {
str += string_tools::pod_to_hex(el) + "\n";
}
str += "\n";
}
return str;
}
std::string wallet2::printTxPrefix(const cryptonote::transaction_prefix &tx)
{
std::string str;
str += "tx.version: " + std::to_string(tx.version) + "\n";
str += "tx.unlock_time: " + std::to_string(tx.unlock_time) + "\n";
return str;
}
std::string wallet2::printPaymentDetails(const payment_details &pd)
{
std::string str;
str += "tx_hash: " + string_tools::pod_to_hex(pd.m_tx_hash) + "\n";
str += "amount: " + std::to_string(pd.m_amount) + "\n";
str += "amounts: ";
for (auto em : pd.m_amounts) {
str += std::to_string(em);
}
str += "\n";
str += "fee: " + std::to_string(pd.m_fee) + "\n";
str += "block_height: " + std::to_string(pd.m_block_height) + "\n";
str += "unlock_time: " + std::to_string(pd.m_unlock_time) + "\n";
str += "timestamp: " + std::to_string(pd.m_timestamp) + "\n";
str += "coinbase: " + std::to_string(pd.m_coinbase) + "\n";
str += "subaddr_index: " + std::to_string(pd.m_subaddr_index.major) + "," + std::to_string(pd.m_subaddr_index.minor) + "\n";
return str;
}
std::string wallet2::printTxDestinationEntry(const cryptonote::tx_destination_entry &tx)
{
std::string str;
str += " original: " + tx.original + "\n";
str += " amount: " + std::to_string(tx.amount) + "\n";
str += " addr: " + string_tools::pod_to_hex(tx.addr) + "\n";
str += " is_subaddress: " + std::to_string(tx.is_subaddress) + "\n";
str += " is_integrated: " + std::to_string(tx.is_integrated) + "\n";
return str;
}
//----------------------------------------------------------------------------------------------------
void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists)
{

@ -232,7 +232,7 @@ private:
friend class wallet_keys_unlocker;
friend class wallet_device_callback;
public:
static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
static constexpr const std::chrono::seconds rpc_timeout = std::chrono::seconds(10);
enum RefreshType {
RefreshFull,
@ -910,6 +910,7 @@ private:
std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); }
std::string get_integrated_address_as_str(const crypto::hash8& payment_id) const;
bool get_subaddress_used(const cryptonote::subaddress_index& index);
void add_subaddress_account(const std::string& label);
size_t get_num_subaddress_accounts() const { return m_subaddress_labels.size(); }
size_t get_num_subaddresses(uint32_t index_major) const { return index_major < m_subaddress_labels.size() ? m_subaddress_labels[index_major].size() : 0; }
@ -1388,6 +1389,7 @@ private:
bool import_key_images(std::vector<crypto::key_image> key_images, size_t offset=0, boost::optional<std::unordered_set<size_t>> selected_transfers=boost::none);
bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
void import_tx(const std::string &txid, std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen);
void update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false);
void process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs);
@ -1532,6 +1534,27 @@ private:
uint64_t get_bytes_sent() const;
uint64_t get_bytes_received() const;
std::string printBlockchain();
std::string printTransfers();
std::string printKeyImages();
std::string printUnconfirmedTransferDetails();
std::string printPayments();
std::string printUnconfirmedPayments();
std::string printConfirmedTransferDetails();
std::string printPubKeys();
std::string printTxNotes();
std::string printSubaddresses();
std::string printSubaddressLabels();
std::string printAdditionalTxKeys();
std::string printAttributes();
std::string printAccountTags();
std::string printTxKeys();
std::string printAddressBook();
std::string printScannedPoolTxs();
std::string printTxPrefix(const cryptonote::transaction_prefix &tx);
std::string printPaymentDetails(const payment_details &pd);
std::string printTxDestinationEntry(const cryptonote::tx_destination_entry &tx);
// MMS -------------------------------------------------------------------------------------------------
mms::message_store& get_message_store() { return m_message_store; };
const mms::message_store& get_message_store() const { return m_message_store; };
@ -1558,6 +1581,7 @@ private:
static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
boost::shared_mutex m_transfers_mutex;
private:
/*!
* \brief Stores wallet information to wallet file.

Loading…
Cancel
Save