aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
authorDusan Klinec <dusan.klinec@gmail.com>2018-05-07 23:23:47 +0200
committerDusan Klinec <dusan.klinec@gmail.com>2018-06-03 13:21:00 +0200
commit9c2a7b46381f577ca1398a1d587d176b3b72a366 (patch)
tree9ef329cdaaec7499171460580bd9c457e03ec24d /src/wallet/wallet2.cpp
parentMerge pull request #3525 (diff)
downloadmonero-9c2a7b46381f577ca1398a1d587d176b3b72a366.tar.xz
wallet-rpc: watch-only and cold wallet features added
- unsigned_txset, signed_txset in transfer / submit_transfer / sign_transfer - export_outputs, import_outputs Squashed commits: [f4d9f3d4] wallet-rpc: do_not_relay removed from submit_transfer [5b16a86f] wallet-rpc: review-fix - method signature changes, renaming [b7fbb10a] wallet-rpc: naming fixes (unsigned vs signed), consts renamed [8c7d2727] wallet-rpc: sign_transfer added [481d024a] wallet2: sign_tx splitted to work with strings and structs, more granular [2a474db9] wallet-rpc: wallet2::load_unsigned_tx split to load from str, file [b1e3a018] wallet-rpc: review fix, load_tx_from_str variable rename [1f6373be] wallet-rpc: review fix: save_tx_to_{str,file} [2a08eafc] wallet-rpc: review comments fixes - redundant this removed from wallet2.cpp - load_tx_from_str, load_tx_from_file [43498052] wallet-rpc: submit_transfer added [9c45d1ad] wallet-rpc: watch_only check, return unsigned_txset [62831396] wallet2: added string variants to load_tx, save_tx - analogously to save_multisig_tx - required for monero-wallet-rpc to support watch-only wallet
Diffstat (limited to '')
-rw-r--r--src/wallet/wallet2.cpp183
1 files changed, 157 insertions, 26 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index d11c99378..128d9780a 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -110,6 +110,8 @@ using namespace cryptonote;
#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
+#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
+
#define SEGREGATION_FORK_HEIGHT 1546000
#define TESTNET_SEGREGATION_FORK_HEIGHT 1000000
#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000
@@ -4661,6 +4663,15 @@ void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const
{
LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
+ std::string ciphertext = dump_tx_to_str(ptx_vector);
+ if (ciphertext.empty())
+ return false;
+ return epee::file_io_utils::save_string_to_file(filename, ciphertext);
+}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) const
+{
+ LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
unsigned_tx_set txs;
for (auto &tx: ptx_vector)
{
@@ -4680,11 +4691,11 @@ bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::stri
}
catch (...)
{
- return false;
+ return std::string();
}
LOG_PRINT_L2("Saving unsigned tx data: " << oss.str());
std::string ciphertext = encrypt_with_view_secret_key(oss.str());
- return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + ciphertext);
+ return std::string(UNSIGNED_TX_PREFIX) + ciphertext;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const
@@ -4702,10 +4713,17 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
LOG_PRINT_L0("Failed to load from " << unsigned_filename);
return false;
}
+
+ return parse_unsigned_tx_from_str(s, exported_txs);
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const
+{
+ std::string s = unsigned_tx_st;
const size_t magiclen = strlen(UNSIGNED_TX_PREFIX) - 1;
if (strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen))
{
- LOG_PRINT_L0("Bad magic from " << unsigned_filename);
+ LOG_PRINT_L0("Bad magic from unsigned tx");
return false;
}
s = s.substr(magiclen);
@@ -4721,7 +4739,7 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << unsigned_filename);
+ LOG_PRINT_L0("Failed to parse data from unsigned tx");
return false;
}
}
@@ -4738,19 +4756,19 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << unsigned_filename);
+ LOG_PRINT_L0("Failed to parse data from unsigned tx");
return false;
}
}
catch (const std::exception &e)
{
- LOG_PRINT_L0("Failed to decrypt " << unsigned_filename << ": " << e.what());
+ LOG_PRINT_L0("Failed to decrypt unsigned tx: " << e.what());
return false;
}
}
else
{
- LOG_PRINT_L0("Unsupported version in " << unsigned_filename);
+ LOG_PRINT_L0("Unsupported version in unsigned tx");
return false;
}
LOG_PRINT_L1("Loaded tx unsigned data from binary: " << exported_txs.txes.size() << " transactions");
@@ -4771,14 +4789,12 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
}
return sign_tx(exported_txs, signed_filename, txs, export_raw);
}
-
//----------------------------------------------------------------------------------------------------
-bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
+bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
{
import_outputs(exported_txs.transfers);
// sign the transactions
- signed_tx_set signed_txes;
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
{
tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
@@ -4840,19 +4856,20 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
signed_txes.key_images[i] = m_transfers[i].m_key_image;
}
- // save as binary
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << signed_txes;
- }
- catch(...)
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
+{
+ // sign the transactions
+ signed_tx_set signed_txes;
+ std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes);
+ if (ciphertext.empty())
{
+ LOG_PRINT_L0("Failed to sign unsigned_tx_set");
return false;
}
- LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
- std::string ciphertext = encrypt_with_view_secret_key(oss.str());
+
if (!epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + ciphertext))
{
LOG_PRINT_L0("Failed to save file to " << signed_filename);
@@ -4875,6 +4892,32 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
return true;
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &ptx, signed_tx_set &signed_txes)
+{
+ // sign the transactions
+ bool r = sign_tx(exported_txs, ptx, signed_txes);
+ if (!r)
+ {
+ LOG_PRINT_L0("Failed to sign unsigned_tx_set");
+ return std::string();
+ }
+
+ // save as binary
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ try
+ {
+ ar << signed_txes;
+ }
+ catch(...)
+ {
+ return std::string();
+ }
+ LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
+ std::string ciphertext = encrypt_with_view_secret_key(oss.str());
+ return std::string(SIGNED_TX_PREFIX) + ciphertext;
+}
+//----------------------------------------------------------------------------------------------------
bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func)
{
std::string s;
@@ -4892,10 +4935,20 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
LOG_PRINT_L0("Failed to load from " << signed_filename);
return false;
}
+
+ return parse_tx_from_str(s, ptx, accept_func);
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func)
+{
+ std::string s = signed_tx_st;
+ boost::system::error_code errcode;
+ signed_tx_set signed_txs;
+
const size_t magiclen = strlen(SIGNED_TX_PREFIX) - 1;
if (strncmp(s.c_str(), SIGNED_TX_PREFIX, magiclen))
{
- LOG_PRINT_L0("Bad magic from " << signed_filename);
+ LOG_PRINT_L0("Bad magic from signed transaction");
return false;
}
s = s.substr(magiclen);
@@ -4911,7 +4964,7 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << signed_filename);
+ LOG_PRINT_L0("Failed to parse data from signed transaction");
return false;
}
}
@@ -4928,23 +4981,23 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse decrypted data from " << signed_filename);
+ LOG_PRINT_L0("Failed to parse decrypted data from signed transaction");
return false;
}
}
catch (const std::exception &e)
{
- LOG_PRINT_L0("Failed to decrypt " << signed_filename << ": " << e.what());
+ LOG_PRINT_L0("Failed to decrypt signed transaction: " << e.what());
return false;
}
}
else
{
- LOG_PRINT_L0("Unsupported version in " << signed_filename);
+ LOG_PRINT_L0("Unsupported version in signed transaction");
return false;
}
LOG_PRINT_L0("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions");
- for (auto &ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx));
+ for (auto &c_ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
if (accept_func && !accept_func(signed_txs))
{
@@ -9772,6 +9825,23 @@ std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
return outs;
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::export_outputs_to_str() const
+{
+ std::vector<tools::wallet2::transfer_details> outs = export_outputs();
+
+ std::stringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ ar << outs;
+
+ std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
+ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
+ std::string header;
+ header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
+ header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
+ std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
+ return magic + ciphertext;
+}
+//----------------------------------------------------------------------------------------------------
size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs)
{
m_transfers.clear();
@@ -9804,6 +9874,67 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
return m_transfers.size();
}
//----------------------------------------------------------------------------------------------------
+size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
+{
+ std::string data = outputs_st;
+ const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
+ if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad magic from outputs"));
+ }
+
+ try
+ {
+ data = decrypt_with_view_secret_key(std::string(data, magiclen));
+ }
+ catch (const std::exception &e)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt outputs: ") + e.what());
+ }
+
+ const size_t headerlen = 2 * sizeof(crypto::public_key);
+ if (data.size() < headerlen)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad data size for outputs"));
+ }
+ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
+ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
+ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
+ if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Outputs from are for a different account"));
+ }
+
+ size_t imported_outputs = 0;
+ try
+ {
+ std::string body(data, headerlen);
+ std::stringstream iss;
+ iss << body;
+ std::vector<tools::wallet2::transfer_details> outputs;
+ try
+ {
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> outputs;
+ }
+ catch (...)
+ {
+ iss.str("");
+ iss << body;
+ boost::archive::binary_iarchive ar(iss);
+ ar >> outputs;
+ }
+
+ imported_outputs = import_outputs(outputs);
+ }
+ catch (const std::exception &e)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs") + e.what());
+ }
+
+ return imported_outputs;
+}
+//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
{
crypto::public_key pkey;