aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/api')
-rw-r--r--src/wallet/api/pending_transaction.cpp30
-rw-r--r--src/wallet/api/pending_transaction.h2
-rw-r--r--src/wallet/api/unsigned_transaction.cpp279
-rw-r--r--src/wallet/api/unsigned_transaction.h76
-rw-r--r--src/wallet/api/wallet.cpp47
-rw-r--r--src/wallet/api/wallet.h4
6 files changed, 434 insertions, 4 deletions
diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp
index 6586d0c48..760c84f4f 100644
--- a/src/wallet/api/pending_transaction.cpp
+++ b/src/wallet/api/pending_transaction.cpp
@@ -51,7 +51,7 @@ PendingTransaction::~PendingTransaction() {}
PendingTransactionImpl::PendingTransactionImpl(WalletImpl &wallet)
: m_wallet(wallet)
{
-
+ m_status = Status_Ok;
}
PendingTransactionImpl::~PendingTransactionImpl()
@@ -77,19 +77,39 @@ std::vector<std::string> PendingTransactionImpl::txid() const
return txid;
}
-bool PendingTransactionImpl::commit()
+bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
{
LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size());
try {
+ // Save tx to file
+ if (!filename.empty()) {
+ boost::system::error_code ignore;
+ bool tx_file_exists = boost::filesystem::exists(filename, ignore);
+ if(tx_file_exists && !overwrite){
+ m_errorString = string(tr("Attempting to save transaction to file, but specified file(s) exist. Exiting to not risk overwriting. File:")) + filename;
+ m_status = Status_Error;
+ LOG_ERROR(m_errorString);
+ return false;
+ }
+ bool r = m_wallet.m_wallet->save_tx(m_pending_tx, filename);
+ if (!r) {
+ m_errorString = tr("Failed to write transaction(s) to file");
+ m_status = Status_Error;
+ } else {
+ m_status = Status_Ok;
+ }
+ }
+ // Commit tx
+ else {
while (!m_pending_tx.empty()) {
auto & ptx = m_pending_tx.back();
m_wallet.m_wallet->commit_tx(ptx);
- // success_msg_writer(true) << tr("Money successfully sent, transaction ") << get_transaction_hash(ptx.tx);
// if no exception, remove element from vector
m_pending_tx.pop_back();
} // TODO: extract method;
+ }
} catch (const tools::error::daemon_busy&) {
// TODO: make it translatable with "tr"?
m_errorString = tr("daemon is busy. Please try again later.");
@@ -100,7 +120,11 @@ bool PendingTransactionImpl::commit()
} catch (const tools::error::tx_rejected& e) {
std::ostringstream writer(m_errorString);
writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
+ std::string reason = e.reason();
m_status = Status_Error;
+ m_errorString = writer.str();
+ if (!reason.empty())
+ m_errorString += string(tr(". Reason: ")) + reason;
} catch (std::exception &e) {
m_errorString = string(tr("Unknown exception: ")) + e.what();
m_status = Status_Error;
diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h
index 47ccdec76..d85a686fd 100644
--- a/src/wallet/api/pending_transaction.h
+++ b/src/wallet/api/pending_transaction.h
@@ -45,7 +45,7 @@ public:
~PendingTransactionImpl();
int status() const;
std::string errorString() const;
- bool commit();
+ bool commit(const std::string &filename = "", bool overwrite = false);
uint64_t amount() const;
uint64_t dust() const;
uint64_t fee() const;
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
new file mode 100644
index 000000000..84ec2d9d2
--- /dev/null
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -0,0 +1,279 @@
+// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 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.
+//
+// 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.
+//
+// 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.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include "unsigned_transaction.h"
+#include "wallet.h"
+#include "common_defines.h"
+
+#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_core/cryptonote_basic_impl.h"
+#include "cryptonote_core/cryptonote_format_utils.h"
+
+#include <memory>
+#include <vector>
+#include <sstream>
+#include <boost/format.hpp>
+
+using namespace std;
+
+namespace Monero {
+
+UnsignedTransaction::~UnsignedTransaction() {}
+
+
+UnsignedTransactionImpl::UnsignedTransactionImpl(WalletImpl &wallet)
+ : m_wallet(wallet)
+{
+ m_status = Status_Ok;
+}
+
+UnsignedTransactionImpl::~UnsignedTransactionImpl()
+{
+ LOG_PRINT_L3("Unsigned tx deleted");
+}
+
+int UnsignedTransactionImpl::status() const
+{
+ return m_status;
+}
+
+string UnsignedTransactionImpl::errorString() const
+{
+ return m_errorString;
+}
+
+bool UnsignedTransactionImpl::sign(const std::string &signedFileName)
+{
+ if(m_wallet.watchOnly())
+ {
+ m_errorString = tr("This is a watch only wallet");
+ m_status = Status_Error;
+ return false;
+ }
+ std::vector<tools::wallet2::pending_tx> ptx;
+ try
+ {
+ bool r = m_wallet.m_wallet->sign_tx(m_unsigned_tx_set, signedFileName, ptx);
+ if (!r)
+ {
+ m_errorString = tr("Failed to sign transaction");
+ m_status = Status_Error;
+ return false;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ m_errorString = string(tr("Failed to sign transaction")) + e.what();
+ m_status = Status_Error;
+ return false;
+ }
+ return true;
+}
+
+//----------------------------------------------------------------------------------------------------
+bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message)
+{
+ // gather info to ask the user
+ uint64_t amount = 0, amount_to_dests = 0, change = 0;
+ size_t min_mixin = ~0;
+ std::unordered_map<std::string, uint64_t> dests;
+ const std::string wallet_address = m_wallet.m_wallet->get_account().get_public_address_str(m_wallet.m_wallet->testnet());
+ for (size_t n = 0; n < get_num_txes(); ++n)
+ {
+ const tools::wallet2::tx_construction_data &cd = get_tx(n);
+ for (size_t s = 0; s < cd.sources.size(); ++s)
+ {
+ amount += cd.sources[s].amount;
+ size_t mixin = cd.sources[s].outputs.size() - 1;
+ if (mixin < min_mixin)
+ min_mixin = mixin;
+ }
+ for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
+ {
+ const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
+ std::string address = get_account_address_as_str(m_wallet.m_wallet->testnet(), entry.addr);
+ std::unordered_map<std::string,uint64_t>::iterator i = dests.find(address);
+ if (i == dests.end())
+ dests.insert(std::make_pair(address, entry.amount));
+ else
+ i->second += entry.amount;
+ amount_to_dests += entry.amount;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ std::unordered_map<std::string, uint64_t>::iterator it = dests.find(get_account_address_as_str(m_wallet.m_wallet->testnet(), cd.change_dts.addr));
+ if (it == dests.end())
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Claimed change does not go to a paid address");
+ return false;
+ }
+ if (it->second < cd.change_dts.amount)
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Claimed change is larger than payment to the change address");
+ return false;
+ }
+ if (memcmp(&cd.change_dts.addr, &get_tx(0).change_dts.addr, sizeof(cd.change_dts.addr)))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Change does to more than one address");
+ return false;
+ }
+ change += cd.change_dts.amount;
+ it->second -= cd.change_dts.amount;
+ if (it->second == 0)
+ dests.erase(get_account_address_as_str(m_wallet.m_wallet->testnet(), cd.change_dts.addr));
+ }
+ }
+ std::string dest_string;
+ for (std::unordered_map<std::string, uint64_t>::const_iterator i = dests.begin(); i != dests.end(); )
+ {
+ dest_string += (boost::format(tr("sending %s to %s")) % cryptonote::print_money(i->second) % i->first).str();
+ ++i;
+ if (i != dests.end())
+ dest_string += ", ";
+ }
+ if (dest_string.empty())
+ dest_string = tr("with no destinations");
+
+ std::string change_string;
+ if (change > 0)
+ {
+ std::string address = get_account_address_as_str(m_wallet.m_wallet->testnet(), get_tx(0).change_dts.addr);
+ change_string += (boost::format(tr("%s change to %s")) % cryptonote::print_money(change) % address).str();
+ }
+ else
+ change_string += tr("no change");
+ uint64_t fee = amount - amount_to_dests;
+ m_confirmationMessage = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %s")) % (unsigned long)get_num_txes() % cryptonote::print_money(amount) % cryptonote::print_money(fee) % dest_string % change_string % (unsigned long)min_mixin % extra_message).str();
+ return true;
+}
+
+std::vector<uint64_t> UnsignedTransactionImpl::amount() const
+{
+ std::vector<uint64_t> result;
+ for (const auto &utx : m_unsigned_tx_set.txes) {
+ for (const auto &unsigned_dest : utx.dests) {
+ result.push_back(unsigned_dest.amount);
+ }
+ }
+ return result;
+}
+
+std::vector<uint64_t> UnsignedTransactionImpl::fee() const
+{
+ std::vector<uint64_t> result;
+ for (const auto &utx : m_unsigned_tx_set.txes) {
+ uint64_t fee = 0;
+ for (const auto &i: utx.sources) fee += i.amount;
+ for (const auto &i: utx.splitted_dsts) fee -= i.amount;
+ result.push_back(fee);
+ }
+ return result;
+}
+
+std::vector<uint64_t> UnsignedTransactionImpl::mixin() const
+{
+ std::vector<uint64_t> result;
+ for (const auto &utx: m_unsigned_tx_set.txes) {
+ size_t min_mixin = ~0;
+ // TODO: Is this loop needed or is sources[0] ?
+ for (size_t s = 0; s < utx.sources.size(); ++s) {
+ size_t mixin = utx.sources[s].outputs.size() - 1;
+ if (mixin < min_mixin)
+ min_mixin = mixin;
+ }
+ result.push_back(min_mixin);
+ }
+ return result;
+}
+
+uint64_t UnsignedTransactionImpl::txCount() const
+{
+ return m_unsigned_tx_set.txes.size();
+}
+
+std::vector<std::string> UnsignedTransactionImpl::paymentId() const
+{
+ std::vector<string> result;
+ for (const auto &utx: m_unsigned_tx_set.txes) {
+ crypto::hash payment_id = cryptonote::null_hash;
+ cryptonote::tx_extra_nonce extra_nonce;
+ std::vector<cryptonote::tx_extra_field> tx_extra_fields;
+ cryptonote::parse_tx_extra(utx.extra, tx_extra_fields);
+ if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ crypto::hash8 payment_id8 = cryptonote::null_hash8;
+ if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ // We can't decrypt short pid without recipient key.
+ memcpy(payment_id.data, payment_id8.data, 8);
+ }
+ else if (!cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ {
+ payment_id = cryptonote::null_hash;
+ }
+ }
+ if(payment_id != cryptonote::null_hash)
+ result.push_back(epee::string_tools::pod_to_hex(payment_id));
+ else
+ result.push_back("");
+ }
+ return result;
+}
+
+std::vector<std::string> UnsignedTransactionImpl::recipientAddress() const
+{
+ // TODO: return integrated address if short payment ID exists
+ std::vector<string> result;
+ for (const auto &utx: m_unsigned_tx_set.txes) {
+ result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->testnet(), utx.dests[0].addr));
+ }
+ return result;
+}
+
+uint64_t UnsignedTransactionImpl::minMixinCount() const
+{
+ uint64_t min_mixin = ~0;
+ for (const auto &utx: m_unsigned_tx_set.txes) {
+ for (size_t s = 0; s < utx.sources.size(); ++s) {
+ size_t mixin = utx.sources[s].outputs.size() - 1;
+ if (mixin < min_mixin)
+ min_mixin = mixin;
+ }
+ }
+ return min_mixin;
+}
+
+} // namespace
+
+namespace Bitmonero = Monero;
+
diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h
new file mode 100644
index 000000000..8038334e4
--- /dev/null
+++ b/src/wallet/api/unsigned_transaction.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2014-2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 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.
+//
+// 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.
+//
+// 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.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include "wallet/wallet2_api.h"
+#include "wallet/wallet2.h"
+
+#include <string>
+#include <vector>
+
+
+namespace Monero {
+
+class WalletImpl;
+class UnsignedTransactionImpl : public UnsignedTransaction
+{
+public:
+ UnsignedTransactionImpl(WalletImpl &wallet);
+ ~UnsignedTransactionImpl();
+ int status() const;
+ std::string errorString() const;
+ std::vector<uint64_t> amount() const;
+ std::vector<uint64_t> dust() const;
+ std::vector<uint64_t> fee() const;
+ std::vector<uint64_t> mixin() const;
+ std::vector<std::string> paymentId() const;
+ std::vector<std::string> recipientAddress() const;
+ uint64_t txCount() const;
+ // sign txs and save to file
+ bool sign(const std::string &signedFileName);
+ std::string confirmationMessage() const {return m_confirmationMessage;}
+ uint64_t minMixinCount() const;
+
+private:
+ // Callback function to check all loaded tx's and generate confirmationMessage
+ bool checkLoadedTx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message);
+
+ friend class WalletImpl;
+ WalletImpl &m_wallet;
+
+ int m_status;
+ std::string m_errorString;
+ tools::wallet2::unsigned_tx_set m_unsigned_tx_set;
+ std::string m_confirmationMessage;
+};
+
+
+}
+
+namespace Bitmonero = Monero;
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 0fb832d65..2159abd5c 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -31,6 +31,7 @@
#include "wallet.h"
#include "pending_transaction.h"
+#include "unsigned_transaction.h"
#include "transaction_history.h"
#include "address_book.h"
#include "common_defines.h"
@@ -623,6 +624,50 @@ int WalletImpl::autoRefreshInterval() const
return m_refreshIntervalMillis;
}
+UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) {
+ clearStatus();
+ UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this);
+ if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){
+ m_errorString = tr("Failed to load unsigned transactions");
+ m_status = Status_Error;
+ }
+
+ // Check tx data and construct confirmation message
+ std::string extra_message;
+ if (!transaction->m_unsigned_tx_set.transfers.empty())
+ extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.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);
+ m_status = transaction->status();
+ m_errorString = transaction->errorString();
+
+ return transaction;
+}
+
+bool WalletImpl::submitTransaction(const string &fileName) {
+ clearStatus();
+ PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
+
+// bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); });
+ bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx);
+ if (!r) {
+ m_errorString = tr("Failed to load transaction from file");
+ m_status = Status_Ok;
+ delete transaction;
+ return false;
+ }
+
+ if(!transaction->commit()) {
+ m_errorString = transaction->m_errorString;
+ m_status = Status_Error;
+ delete transaction;
+ return false;
+ }
+
+ delete transaction;
+
+ return true;
+}
+
// TODO:
// 1 - properly handle payment id (add another menthod with explicit 'payment_id' param)
// 2 - check / design how "Transaction" can be single interface
@@ -640,6 +685,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
clearStatus();
// Pause refresh thread while creating transaction
pauseRefresh();
+
cryptonote::account_public_address addr;
// indicates if dst_addr is integrated address (address + payment_id)
@@ -1141,6 +1187,7 @@ void WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction
if (Utils::isAddressLocal(daemon_address)) {
this->setTrustedDaemon(true);
}
+
}
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 41b3b22f3..5b4064e8e 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -43,6 +43,7 @@
namespace Monero {
class TransactionHistoryImpl;
class PendingTransactionImpl;
+class UnsignedTransactionImpl;
class AddressBookImpl;
struct Wallet2CallbackImpl;
@@ -99,6 +100,8 @@ public:
optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low);
virtual PendingTransaction * createSweepUnmixableTransaction();
+ bool submitTransaction(const std::string &fileName);
+ virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename);
virtual void disposeTransaction(PendingTransaction * t);
virtual TransactionHistory * history() const;
@@ -126,6 +129,7 @@ private:
private:
friend class PendingTransactionImpl;
+ friend class UnsignedTransactionImpl;
friend class TransactionHistoryImpl;
friend class Wallet2CallbackImpl;
friend class AddressBookImpl;