aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--Makefile4
-rw-r--r--README.md56
-rw-r--r--contrib/epee/include/storages/levin_abstract_invoke2.h2
-rw-r--r--contrib/epee/include/string_tools.h4
-rw-r--r--src/blockchain_utilities/CMakeLists.txt22
-rw-r--r--src/blockchain_utilities/cn_deserialize.cpp190
-rw-r--r--src/crypto/skein.c4
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.cpp2
-rw-r--r--src/daemon/rpc_command_executor.cpp8
-rw-r--r--src/ringct/rctOps.cpp59
-rw-r--r--src/ringct/rctOps.h24
-rw-r--r--src/simplewallet/simplewallet.cpp144
-rw-r--r--src/simplewallet/simplewallet.h4
-rw-r--r--src/wallet/api/wallet_manager.cpp13
-rw-r--r--src/wallet/wallet2.cpp114
-rw-r--r--src/wallet/wallet2.h15
-rw-r--r--tests/performance_tests/CMakeLists.txt1
-rw-r--r--tests/performance_tests/generate_keypair.h51
-rw-r--r--tests/performance_tests/main.cpp4
-rw-r--r--utils/gpg_keys/anonimal.asc64
-rw-r--r--utils/gpg_keys/guzzi.asc30
22 files changed, 705 insertions, 112 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0d82324a3..2d4f4f24b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -572,6 +572,8 @@ find_package(Boost 1.58 QUIET REQUIRED COMPONENTS system filesystem thread date_
set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES})
if(NOT Boost_FOUND)
die("Could not find Boost libraries, please make sure you have installed Boost or libboost-all-dev (1.58) or the equivalent")
+elseif(Boost_FOUND)
+ message(STATUS "Found Boost Version: ${Boost_VERSION}")
endif()
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
diff --git a/Makefile b/Makefile
index fc79958f6..777769f13 100644
--- a/Makefile
+++ b/Makefile
@@ -62,11 +62,11 @@ coverage:
mkdir -p build/debug
cd build/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug -D COVERAGE=ON ../.. && $(MAKE) && $(MAKE) test
-release-static-arm6:
+release-static-armv6:
mkdir -p build/release
cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE)
-release-static-arm7:
+release-static-armv7:
mkdir -p build/release
cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE)
diff --git a/README.md b/README.md
index 2245e6920..61f7da79d 100644
--- a/README.md
+++ b/README.md
@@ -145,7 +145,11 @@ invokes cmake commands as needed.
this to be worthwhile, the machine should have one core and about 2GB of RAM
available per thread.
-* The resulting executables can be found in `build/release/bin`.
+* The resulting executables can be found in `build/release/bin`
+
+* Add `PATH="$PATH:$HOME/monero/build/release/bin"` to `.profile`
+
+* Run Monero with `monerod --detach`
* **Optional**: build and run the test suite to verify the binaries:
@@ -165,6 +169,50 @@ invokes cmake commands as needed.
HAVE_DOT=YES doxygen Doxyfile
+#### On the Raspberry Pi
+
+Tested on a Raspberry Pi 2 with a clean install of minimal Debian Jessie from https://www.raspberrypi.org/downloads/raspbian/
+
+* `apt-get update` and `apt-get upgrade` to install all of the latest software
+
+* Install the dependencies for Monero except libunwind and libboost-all-dev
+
+* Increase the system swap size
+
+ sudo /etc/init.d/dphys-swapfile stop
+ sudo nano /etc/dphys-swapfile
+ CONF_SWAPSIZE=1024
+ sudo /etc/init.d/dphys-swapfile start
+
+* Install the latest version of boost (this may first require invoking `apt-get remove --purge libboost*` to remove a previous version if you're not using a clean install)
+
+ cd
+ wget https://sourceforge.net/projects/boost/files/boost/1.62.0/boost_1_62_0.tar.bz2
+ tar xvfo boost_1_62_0.tar.bz2
+ cd boost_1_62_0
+ ./bootstrap.sh
+ sudo ./b2
+
+* Wait ~8 hours
+
+ sudo ./bjam install
+
+* Wait ~4 hours
+
+* Change to the root of the source code directory and build:
+
+ cd monero
+ make release
+
+* Wait ~4 hours
+
+* The resulting executables can be found in `build/release/bin`
+
+* Add `PATH="$PATH:$HOME/monero/build/release/bin"` to `.profile`
+
+* Run Monero with `monerod --detach`
+
+* You may wish to reduce the size of the swap file after the build has finished, and delete the boost directory from your home directory
#### On Windows:
@@ -242,9 +290,9 @@ By default, in either dynamically or statically linked builds, binaries target t
* ```make release-static-64``` builds binaries on Linux on x86_64 portable across POSIX systems on x86_64 processors
* ```make release-static-32``` builds binaries on Linux on x86_64 or i686 portable across POSIX systems on i686 processors
-* ```make release-static-arm8``` builds binaries on Linux on armv8 portable across POSIX systems on armv8 processors
-* ```make release-static-arm7``` builds binaries on Linux on armv7 portable across POSIX systems on armv7 processors
-* ```make release-static-arm6``` builds binaries on Linux on armv7 or armv6 portable across POSIX systems on armv6 processors, such as the Raspberry Pi
+* ```make release-static-armv8``` builds binaries on Linux portable across POSIX systems on armv8 processors
+* ```make release-static-armv7``` builds binaries on Linux portable across POSIX systems on armv7 processors
+* ```make release-static-armv6``` builds binaries on Linux portable across POSIX systems on armv6 processors
* ```make release-static-win64``` builds binaries on 64-bit Windows portable across 64-bit Windows systems
* ```make release-static-win32``` builds binaries on 64-bit or 32-bit Windows portable across 32-bit Windows systems
diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h
index 73ede1b12..8c4fb9ccd 100644
--- a/contrib/epee/include/storages/levin_abstract_invoke2.h
+++ b/contrib/epee/include/storages/levin_abstract_invoke2.h
@@ -281,7 +281,7 @@ namespace epee
#define END_INVOKE_MAP2() \
- LOG_ERROR("Unkonown command:" << command); \
+ LOG_ERROR("Unknown command:" << command); \
return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \
}
}
diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h
index b3623298c..6292e471c 100644
--- a/contrib/epee/include/string_tools.h
+++ b/contrib/epee/include/string_tools.h
@@ -33,6 +33,7 @@
#include <locale>
#include <cstdlib>
#include <iomanip>
+#include <type_traits>
//#include <strsafe.h>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
@@ -171,6 +172,7 @@ namespace string_tools
template<class t_pod_type>
bool parse_tpod_from_hex_string(const std::string& str_hash, t_pod_type& t_pod)
{
+ static_assert(std::is_pod<t_pod_type>::value, "expected pod type");
std::string buf;
bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf);
if (!res || buf.size() != sizeof(t_pod_type))
@@ -570,6 +572,7 @@ POP_WARNINGS
template<class t_pod_type>
std::string pod_to_hex(const t_pod_type& s)
{
+ static_assert(std::is_pod<t_pod_type>::value, "expected pod type");
std::string buff;
buff.assign(reinterpret_cast<const char*>(&s), sizeof(s));
return buff_to_hex_nodelimer(buff);
@@ -578,6 +581,7 @@ POP_WARNINGS
template<class t_pod_type>
bool hex_to_pod(const std::string& hex_str, t_pod_type& s)
{
+ static_assert(std::is_pod<t_pod_type>::value, "expected pod type");
std::string hex_str_tr = trim(hex_str);
if(sizeof(s)*2 != hex_str.size())
return false;
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index ccfd4a279..198f15ca3 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -58,6 +58,11 @@ monero_private_headers(blockchain_export
${blockchain_export_private_headers})
+set(cn_deserialize_sources
+ cn_deserialize.cpp
+ )
+
+
monero_add_executable(blockchain_import
${blockchain_import_sources}
${blockchain_import_private_headers})
@@ -104,3 +109,20 @@ add_dependencies(blockchain_export
set_property(TARGET blockchain_export
PROPERTY
OUTPUT_NAME "monero-blockchain-export")
+
+monero_add_executable(cn_deserialize
+ ${cn_deserialize_sources}
+ ${cn_deserialize_private_headers})
+
+target_link_libraries(cn_deserialize
+ LINK_PRIVATE
+ cryptonote_core
+ p2p
+ ${CMAKE_THREAD_LIBS_INIT})
+
+add_dependencies(cn_deserialize
+ version)
+set_property(TARGET cn_deserialize
+ PROPERTY
+ OUTPUT_NAME "cn_deserialize")
+
diff --git a/src/blockchain_utilities/cn_deserialize.cpp b/src/blockchain_utilities/cn_deserialize.cpp
new file mode 100644
index 000000000..a8448dcee
--- /dev/null
+++ b/src/blockchain_utilities/cn_deserialize.cpp
@@ -0,0 +1,190 @@
+// 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.
+
+#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_core/tx_extra.h"
+#include "cryptonote_core/blockchain.h"
+#include "blockchain_utilities.h"
+#include "common/command_line.h"
+#include "version.h"
+
+namespace po = boost::program_options;
+using namespace epee; // log_space
+
+using namespace cryptonote;
+
+int main(int argc, char* argv[])
+{
+ uint32_t log_level = 0;
+ std::string input;
+
+ tools::sanitize_locale();
+
+ boost::filesystem::path output_file_path;
+
+ po::options_description desc_cmd_only("Command line options");
+ po::options_description desc_cmd_sett("Command line options and settings options");
+ const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true};
+ const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level};
+ const command_line::arg_descriptor<std::string> arg_input = {"input", "Specify input has a hexadecimal string", ""};
+
+ command_line::add_arg(desc_cmd_sett, arg_output_file);
+ command_line::add_arg(desc_cmd_sett, arg_log_level);
+ command_line::add_arg(desc_cmd_sett, arg_input);
+
+ command_line::add_arg(desc_cmd_only, command_line::arg_help);
+
+ po::options_description desc_options("Allowed options");
+ desc_options.add(desc_cmd_only).add(desc_cmd_sett);
+
+ po::variables_map vm;
+ bool r = command_line::handle_error_helper(desc_options, [&]()
+ {
+ po::store(po::parse_command_line(argc, argv, desc_options), vm);
+ po::notify(vm);
+ return true;
+ });
+ if (! r)
+ return 1;
+
+ if (command_line::get_arg(vm, command_line::arg_help))
+ {
+ std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL;
+ std::cout << desc_options << std::endl;
+ return 1;
+ }
+
+ log_level = command_line::get_arg(vm, arg_log_level);
+ input = command_line::get_arg(vm, arg_input);
+ if (input.empty())
+ {
+ std::cerr << "--input is mandatory" << std::endl;
+ return 1;
+ }
+
+ log_space::get_set_log_detalisation_level(true, log_level);
+ log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
+
+ std::string m_config_folder;
+
+ std::ostream *output;
+ std::ofstream *raw_data_file = NULL;
+ if (command_line::has_arg(vm, arg_output_file))
+ {
+ output_file_path = boost::filesystem::path(command_line::get_arg(vm, arg_output_file));
+
+ const boost::filesystem::path dir_path = output_file_path.parent_path();
+ if (!dir_path.empty())
+ {
+ if (boost::filesystem::exists(dir_path))
+ {
+ if (!boost::filesystem::is_directory(dir_path))
+ {
+ std::cerr << "output directory path is a file: " << dir_path << std::endl;
+ return 1;
+ }
+ }
+ else
+ {
+ if (!boost::filesystem::create_directory(dir_path))
+ {
+ std::cerr << "Failed to create directory " << dir_path << std::endl;
+ return 1;
+ }
+ }
+ }
+
+ raw_data_file = new std::ofstream();
+ raw_data_file->open(output_file_path.string(), std::ios_base::out | std::ios::trunc);
+ if (raw_data_file->fail())
+ return 1;
+ output = raw_data_file;
+ }
+ else
+ {
+ output_file_path = "";
+ output = &std::cout;
+ }
+
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(input, blob))
+ {
+ std::cerr << "Invalid hex input" << std::endl;
+ std::cerr << "Invalid hex input: " << input << std::endl;
+ return 1;
+ }
+
+ cryptonote::block block;
+ cryptonote::transaction tx;
+ std::vector<cryptonote::tx_extra_field> fields;
+ if (cryptonote::parse_and_validate_block_from_blob(blob, block))
+ {
+ std::cout << "Parsed block:" << std::endl;
+ std::cout << cryptonote::obj_to_json_str(block) << std::endl;
+ }
+ else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx))
+ {
+ std::cout << "Parsed transaction:" << std::endl;
+ std::cout << cryptonote::obj_to_json_str(tx) << std::endl;
+
+ if (cryptonote::parse_tx_extra(tx.extra, fields))
+ {
+ std::cout << "tx_extra has " << fields.size() << " field(s)" << std::endl;
+ for (size_t n = 0; n < fields.size(); ++n)
+ {
+ std::cout << "field " << n << ": ";
+ if (typeid(cryptonote::tx_extra_padding) == fields[n].type()) std::cout << "extra padding: " << boost::get<cryptonote::tx_extra_padding>(fields[n]).size << " bytes";
+ else if (typeid(cryptonote::tx_extra_pub_key) == fields[n].type()) std::cout << "extra pub key: " << boost::get<cryptonote::tx_extra_pub_key>(fields[n]).pub_key;
+ else if (typeid(cryptonote::tx_extra_nonce) == fields[n].type()) std::cout << "extra nonce: " << epee::string_tools::buff_to_hex_nodelimer(boost::get<cryptonote::tx_extra_nonce>(fields[n]).nonce);
+ else if (typeid(cryptonote::tx_extra_merge_mining_tag) == fields[n].type()) std::cout << "extra merge mining tag: depth " << boost::get<cryptonote::tx_extra_merge_mining_tag>(fields[n]).depth << ", merkle root " << boost::get<cryptonote::tx_extra_merge_mining_tag>(fields[n]).merkle_root;
+ else if (typeid(cryptonote::tx_extra_mysterious_minergate) == fields[n].type()) std::cout << "extra minergate custom: " << epee::string_tools::buff_to_hex_nodelimer(boost::get<cryptonote::tx_extra_mysterious_minergate>(fields[n]).data);
+ else std::cout << "unknown";
+ std::cout << std::endl;
+ }
+ }
+ else
+ {
+ std::cout << "Failed to parse tx_extra" << std::endl;
+ }
+ }
+ else
+ {
+ std::cerr << "Not a recognized CN type" << std::endl;
+ return 1;
+ }
+
+
+
+ if (output->fail())
+ return 1;
+ output->flush();
+ if (raw_data_file)
+ delete raw_data_file;
+
+ return 0;
+}
diff --git a/src/crypto/skein.c b/src/crypto/skein.c
index 9c8ac288d..65e4525c3 100644
--- a/src/crypto/skein.c
+++ b/src/crypto/skein.c
@@ -77,7 +77,7 @@ typedef struct /* 1024-bit Skein hash context stru
} Skein1024_Ctxt_t;
/* Skein APIs for (incremental) "straight hashing" */
-#if SKEIN_256_NIST_MAX_HASH_BITS
+#if SKEIN_256_NIST_MAX_HASHBITS
static int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen);
#endif
static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen);
@@ -1941,7 +1941,7 @@ static HashReturn Final (hashState *state, BitSequence *hashval);
/* select the context size and init the context */
static HashReturn Init(hashState *state, int hashbitlen)
{
-#if SKEIN_256_NIST_MAX_HASH_BITS
+#if SKEIN_256_NIST_MAX_HASHBITS
if (hashbitlen <= SKEIN_256_NIST_MAX_HASHBITS)
{
Skein_Assert(hashbitlen > 0,BAD_HASHLEN);
diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp
index 870e8f0d8..6d64a43cb 100644
--- a/src/cryptonote_core/cryptonote_format_utils.cpp
+++ b/src/cryptonote_core/cryptonote_format_utils.cpp
@@ -509,7 +509,7 @@ namespace cryptonote
std::string extra_nonce;
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
- remove_field_from_tx_extra(tx.extra, typeid(tx_extra_fields));
+ remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce));
if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
{
LOG_ERROR("Failed to add encrypted payment id to tx extra");
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index d8d8aac99..7d50ae76b 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -859,10 +859,10 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
for (const auto &tx_info: res.transactions)
{
bytes += tx_info.blob_size;
- if (min_bytes == 0 || bytes < min_bytes)
- min_bytes = bytes;
- if (bytes > max_bytes)
- max_bytes = bytes;
+ if (min_bytes == 0 || tx_info.blob_size < min_bytes)
+ min_bytes = tx_info.blob_size;
+ if (tx_info.blob_size > max_bytes)
+ max_bytes = tx_info.blob_size;
if (!tx_info.relayed)
n_not_relayed++;
fee += tx_info.fee;
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
index 239168388..cf55897a7 100644
--- a/src/ringct/rctOps.cpp
+++ b/src/ringct/rctOps.cpp
@@ -37,50 +37,12 @@ namespace rct {
//Various key initialization functions
- //Creates a zero scalar
- void zero(key &zero) {
- memset(&zero, 0, 32);
- }
-
- //Creates a zero scalar
- key zero() {
- static const key z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
- return z;
- }
-
- //Creates a zero elliptic curve point
- void identity(key &Id) {
- Id[0] = (unsigned char)(0x01);
- memset(Id.bytes+1, 0, 31);
- }
-
- //Creates a zero elliptic curve point
- key identity() {
- key Id;
- Id[0] = (unsigned char)(0x01);
- memset(Id.bytes+1, 0, 31);
- return Id;
- }
-
- //copies a scalar or point
- void copy(key &AA, const key &A) {
- memcpy(&AA, &A, 32);
- }
-
- //copies a scalar or point
- key copy(const key &A) {
- key AA;
- memcpy(&AA, &A, 32);
- return AA;
- }
-
-
//initializes a key matrix;
//first parameter is rows,
//second is columns
- keyM keyMInit(int rows, int cols) {
+ keyM keyMInit(size_t rows, size_t cols) {
keyM rv(cols);
- int i = 0;
+ size_t i = 0;
for (i = 0 ; i < cols ; i++) {
rv[i] = keyV(rows);
}
@@ -107,11 +69,12 @@ namespace rct {
//Generates a vector of secret key
//Mainly used in testing
- keyV skvGen(int rows ) {
+ keyV skvGen(size_t rows ) {
keyV rv(rows);
- int i = 0;
+ size_t i = 0;
+ crypto::rand(rows * sizeof(key), (uint8_t*)&rv[0]);
for (i = 0 ; i < rows ; i++) {
- skGen(rv[i]);
+ sc_reduce32(rv[i].bytes);
}
return rv;
}
@@ -155,7 +118,7 @@ namespace rct {
//generates a <secret , public> / Pedersen commitment but takes bH as input
- tuple<ctkey, ctkey> ctskpkGen(key bH) {
+ tuple<ctkey, ctkey> ctskpkGen(const key &bH) {
ctkey sk, pk;
skpkGen(sk.dest, pk.dest);
skpkGen(sk.mask, pk.mask);
@@ -172,12 +135,12 @@ namespace rct {
return mask;
}
- key commit(xmr_amount amount, key mask) {
- mask = scalarmultBase(mask);
+ key commit(xmr_amount amount, const key &mask) {
+ key c = scalarmultBase(mask);
key am = d2h(amount);
key bH = scalarmultH(am);
- addKeys(mask, mask, bH);
- return mask;
+ addKeys(c, c, bH);
+ return c;
}
//generates a random uint long long (for testing)
diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h
index a7e13eefa..cd3a6dc0d 100644
--- a/src/ringct/rctOps.h
+++ b/src/ringct/rctOps.h
@@ -64,19 +64,23 @@ namespace rct {
//Various key initialization functions
+ static const key Z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
+ static const key I = { {0x01, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
+
//Creates a zero scalar
- key zero();
- void zero(key &z);
+ inline key zero() { return Z; }
+ inline void zero(key &z) { memset(&z, 0, 32); }
//Creates a zero elliptic curve point
- key identity();
- void identity(key &Id);
+ inline key identity() { return I; }
+ inline void identity(key &Id) { memcpy(&Id, &I, 32); }
//copies a scalar or point
- void copy(key &AA, const key &A);
- key copy(const key & AA);
+ inline void copy(key &AA, const key &A) { memcpy(&AA, &A, 32); }
+ inline key copy(const key & A) { key AA; memcpy(&AA, &A, 32); return AA; }
+
//initializes a key matrix;
//first parameter is rows,
//second is columns
- keyM keyMInit(int, int);
+ keyM keyMInit(size_t rows, size_t cols);
//Various key generation functions
@@ -85,7 +89,7 @@ namespace rct {
void skGen(key &);
//generates a vector of secret keys of size "int"
- keyV skvGen(int );
+ keyV skvGen(size_t rows );
//generates a random curve point (for testing)
key pkGen();
@@ -97,9 +101,9 @@ namespace rct {
//generates C =aG + bH from b, a is random
void genC(key & C, const key & a, xmr_amount amount);
//this one is mainly for testing, can take arbitrary amounts..
- tuple<ctkey, ctkey> ctskpkGen(key bH);
+ tuple<ctkey, ctkey> ctskpkGen(const key &bH);
// make a pedersen commitment with given key
- key commit(xmr_amount amount, key mask);
+ key commit(xmr_amount amount, const key &mask);
// make a pedersen commitment with zero key
key zeroCommit(xmr_amount amount);
//generates a random uint long long
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 3f494f512..b1049265d 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -78,6 +78,7 @@ typedef cryptonote::simple_wallet sw;
#define DEFAULT_MIX 4
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\001"
+#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\001"
// workaround for a suspected bug in pthread/kernel on MacOS X
#ifdef __APPLE__
@@ -703,6 +704,8 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::verify, this, _1), tr("Verify a signature on the contents of a file"));
m_cmd_binder.set_handler("export_key_images", boost::bind(&simple_wallet::export_key_images, this, _1), tr("Export a signed set of key images"));
m_cmd_binder.set_handler("import_key_images", boost::bind(&simple_wallet::import_key_images, this, _1), tr("Import signed key images list and verify their spent status"));
+ m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), tr("Export a set of outputs owned by this wallet"));
+ m_cmd_binder.set_handler("import_outputs", boost::bind(&simple_wallet::import_outputs, this, _1), tr("Import set of outputs owned by this wallet"));
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help"));
}
//----------------------------------------------------------------------------------------------------
@@ -3111,16 +3114,16 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
return true;
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
+bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx)
{
// 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->get_account().get_public_address_str(m_wallet->testnet());
- for (size_t n = 0; n < txs.txes.size(); ++n)
+ for (size_t n = 0; n < get_num_txes(); ++n)
{
- const tools::wallet2::tx_construction_data &cd = txs.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;
@@ -3128,9 +3131,9 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
if (mixin < min_mixin)
min_mixin = mixin;
}
- for (size_t d = 0; d < cd.destinations.size(); ++d)
+ for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
{
- const tx_destination_entry &entry = cd.destinations[d];
+ const tx_destination_entry &entry = cd.splitted_dsts[d];
std::string address = get_account_address_as_str(m_wallet->testnet(), entry.addr);
std::unordered_map<std::string,uint64_t>::iterator i = dests.find(address);
if (i == dests.end())
@@ -3141,9 +3144,21 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
}
if (cd.change_dts.amount > 0)
{
- dests.insert(std::make_pair(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr), cd.change_dts.amount));
- amount_to_dests += cd.change_dts.amount;
- change += cd.change_dts.amount;
+ std::unordered_map<std::string, uint64_t>::iterator it = dests.find(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr));
+ if (it == dests.end())
+ {
+ fail_msg_writer() << tr("Claimed change does not go to a paid address");
+ return false;
+ }
+ if (it->second < cd.change_dts.amount)
+ {
+ fail_msg_writer() << tr("Claimed change is larger than payment to the change 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->testnet(), cd.change_dts.addr));
}
}
std::string dest_string;
@@ -3158,11 +3173,21 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
dest_string = tr("with no destinations");
uint64_t fee = amount - amount_to_dests;
- std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, change %s, %s, with min mixin %lu (full details in log file). Is this okay? (Y/Yes/N/No)")) % (unsigned long)txs.txes.size() % print_money(amount) % print_money(fee) % print_money(change) % dest_string % (unsigned long)min_mixin).str();
+ std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, change %s, %s, with min mixin %lu. Is this okay? (Y/Yes/N/No)")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % print_money(change) % dest_string % (unsigned long)min_mixin).str();
std::string accepted = command_line::input_line(prompt_str);
return is_it_true(accepted);
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
+{
+ return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];});
+}
+//----------------------------------------------------------------------------------------------------
+bool simple_wallet::accept_loaded_tx(const tools::wallet2::signed_tx_set &txs)
+{
+ return accept_loaded_tx([&txs](){return txs.ptx.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.ptx[n].construction_data;});
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
{
if(m_wallet->watch_only())
@@ -3198,7 +3223,7 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_)
try
{
std::vector<tools::wallet2::pending_tx> ptx_vector;
- bool r = m_wallet->load_tx("signed_monero_tx", ptx_vector);
+ bool r = m_wallet->load_tx("signed_monero_tx", ptx_vector, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); });
if (!r)
{
fail_msg_writer() << tr("Failed to load transaction from file");
@@ -3236,7 +3261,7 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_)
if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_money(dust_in_fee);
if (dust_not_in_fee != 0) prompt << tr(".") << ENDL << boost::format(tr("A total of %s from dust change will be sent to dust address"))
% print_money(dust_not_in_fee);
- prompt << tr(".") << ENDL << tr("Is this okay? (Y/Yes/N/No)");
+ prompt << tr(".") << ENDL << "Full transaction details are available in the log file" << ENDL << tr("Is this okay? (Y/Yes/N/No)");
std::string accepted = command_line::input_line(prompt.str());
if (std::cin.eof())
@@ -4091,6 +4116,103 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::export_outputs(const std::vector<std::string> &args)
+{
+ if (args.size() != 1)
+ {
+ fail_msg_writer() << tr("usage: export_outputs <filename>");
+ return true;
+ }
+ std::string filename = args[0];
+
+ try
+ {
+ std::vector<tools::wallet2::transfer_details> outs = m_wallet->export_outputs();
+
+ std::stringstream oss;
+ boost::archive::binary_oarchive ar(oss);
+ ar << outs;
+
+ std::string data(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
+ const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address;
+ data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
+ data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
+ bool r = epee::file_io_utils::save_string_to_file(filename, data + oss.str());
+ if (!r)
+ {
+ fail_msg_writer() << tr("failed to save file ") << filename;
+ return true;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ LOG_ERROR("Error exporting outputs: " << e.what());
+ fail_msg_writer() << "Error exporting outputs: " << e.what();
+ return true;
+ }
+
+ success_msg_writer() << tr("Outputs exported to ") << filename;
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool simple_wallet::import_outputs(const std::vector<std::string> &args)
+{
+ if (args.size() != 1)
+ {
+ fail_msg_writer() << tr("usage: import_outputs <filename>");
+ return true;
+ }
+ std::string filename = args[0];
+
+ std::string data;
+ bool r = epee::file_io_utils::load_file_to_string(filename, data);
+ if (!r)
+ {
+ fail_msg_writer() << tr("failed to read file ") << filename;
+ return true;
+ }
+ const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
+ if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
+ {
+ fail_msg_writer() << "Bad output export file magic in " << filename;
+ return true;
+ }
+ const size_t headerlen = magiclen + 2 * sizeof(crypto::public_key);
+ if (data.size() < headerlen)
+ {
+ fail_msg_writer() << "Bad data size from file " << filename;
+ return true;
+ }
+ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[magiclen];
+ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[magiclen + sizeof(crypto::public_key)];
+ const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address;
+ if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
+ {
+ fail_msg_writer() << "Outputs from " << filename << " are for a different account";
+ return true;
+ }
+
+ try
+ {
+ std::string body(data, headerlen);
+ std::stringstream iss;
+ iss << body;
+ boost::archive::binary_iarchive ar(iss);
+ std::vector<tools::wallet2::transfer_details> outputs;
+ ar >> outputs;
+
+ size_t n_outputs = m_wallet->import_outputs(outputs);
+ success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << "Failed to import outputs: " << e.what();
+ return true;
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::process_command(const std::vector<std::string> &args)
{
return m_cmd_binder.process_command_vec(args);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 375716604..fcc77ff69 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -152,11 +152,15 @@ namespace cryptonote
bool verify(const std::vector<std::string> &args);
bool export_key_images(const std::vector<std::string> &args);
bool import_key_images(const std::vector<std::string> &args);
+ bool export_outputs(const std::vector<std::string> &args);
+ bool import_outputs(const std::vector<std::string> &args);
uint64_t get_daemon_blockchain_height(std::string& err);
bool try_connect_to_daemon(bool silent = false);
bool ask_wallet_create_if_needed();
+ bool accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx);
bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs);
+ bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs);
bool get_address_from_str(const std::string &str, cryptonote::account_public_address &address, bool &has_payment_id, crypto::hash8 &payment_id);
/*!
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index aa99476ee..d2395ace1 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -81,6 +81,12 @@ bool WalletManagerImpl::closeWallet(Wallet *wallet)
bool WalletManagerImpl::walletExists(const std::string &path)
{
+ bool keys_file_exists;
+ bool wallet_file_exists;
+ tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists);
+ if(keys_file_exists){
+ return true;
+ }
return false;
}
@@ -88,10 +94,13 @@ bool WalletManagerImpl::walletExists(const std::string &path)
std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path)
{
std::vector<std::string> result;
+ boost::filesystem::path work_dir(path);
+ // return empty result if path doesn't exist
+ if(!boost::filesystem::is_directory(path)){
+ return result;
+ }
const boost::regex wallet_rx("(.*)\\.(keys)$"); // searching for <wallet_name>.keys files
boost::filesystem::recursive_directory_iterator end_itr; // Default ctor yields past-the-end
- boost::filesystem::path work_dir(path);
-
for (boost::filesystem::recursive_directory_iterator itr(path); itr != end_itr; ++itr) {
// Skip if not a file
if (!boost::filesystem::is_regular_file(itr->status()))
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 23e016f7b..8ea605375 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -74,8 +74,8 @@ using namespace cryptonote;
// arbitrary, used to generate different hashes from the same input
#define CHACHA8_KEY_TAIL 0x8c
-#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\001"
-#define SIGNED_TX_PREFIX "Monero signed tx set\001"
+#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\002"
+#define SIGNED_TX_PREFIX "Monero signed tx set\002"
#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone
#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone
@@ -246,6 +246,15 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::public_key pub,
}
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki)
+{
+ if (!cryptonote::generate_key_image_helper(ack, tx_public_key, real_output_index, in_ephemeral, ki))
+ return false;
+ if (m_watch_only)
+ memset(&ki, 0, 32);
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
{
class lazy_txid_getter
@@ -317,7 +326,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
// this assumes that the miner tx pays a single address
if (received)
{
- cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, 0, in_ephemeral[0], ki[0]);
+ wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, 0, in_ephemeral[0], ki[0]);
THROW_WALLET_EXCEPTION_IF(in_ephemeral[0].pub != boost::get<cryptonote::txout_to_key>(tx.vout[0].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
@@ -360,7 +369,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
if (received[i])
{
- cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
@@ -408,7 +417,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
if (received[i])
{
- cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
@@ -440,7 +449,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
{
if (received)
{
- cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
@@ -518,14 +527,14 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
{
- LOG_ERROR("key image " << epee::string_tools::pod_to_hex(ki)
+ LOG_ERROR("key image " << epee::string_tools::pod_to_hex(kit->first)
<< " from received " << print_money(tx.vout[o].amount) << " output already exists with "
<< (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
<< print_money(m_transfers[kit->second].amount()) << ", received output ignored");
}
else
{
- LOG_ERROR("key image " << epee::string_tools::pod_to_hex(ki)
+ LOG_ERROR("key image " << epee::string_tools::pod_to_hex(kit->first)
<< " from received " << print_money(tx.vout[o].amount) << " output already exists with "
<< print_money(m_transfers[kit->second].amount()) << ", replacing with new output");
// The new larger output replaced a previous smaller one
@@ -2138,9 +2147,14 @@ void wallet2::rescan_spent()
std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(key_images.size()));
// update spent status
+ key_image zero_ki;
+ memset(&zero_ki, 0, 32);
for (size_t i = 0; i < m_transfers.size(); ++i)
{
transfer_details& td = m_transfers[i];
+ // a view wallet may not know about key images
+ if (td.m_key_image == zero_ki)
+ continue;
if (td.m_spent != (daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT))
{
if (td.m_spent)
@@ -2630,11 +2644,8 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
crypto::secret_key tx_key;
- std::vector<cryptonote::tx_destination_entry> dests = sd.destinations;
- if (sd.change_dts.amount > 0)
- dests.push_back(sd.change_dts);
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sd.sources, dests, sd.extra, ptx.tx, sd.unlock_time, tx_key, sd.use_rct);
- THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.destinations, sd.unlock_time, m_testnet);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sd.sources, sd.splitted_dsts, sd.extra, ptx.tx, sd.unlock_time, tx_key, sd.use_rct);
+ THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_testnet);
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
// and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's
@@ -2661,13 +2672,13 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
ptx.key_images = key_images;
ptx.fee = 0;
for (const auto &i: sd.sources) ptx.fee += i.amount;
- for (const auto &i: dests) ptx.fee -= i.amount;
+ for (const auto &i: sd.splitted_dsts) ptx.fee -= i.amount;
ptx.dust = 0;
ptx.dust_added_to_fee = false;
ptx.change_dts = sd.change_dts;
-// ptx.selected_transfers = selected_transfers;
+ ptx.selected_transfers = sd.selected_transfers;
ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet
- ptx.dests = sd.destinations;
+ ptx.dests = sd.splitted_dsts;
ptx.construction_data = sd;
}
@@ -2681,7 +2692,7 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
return epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + s);
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx)
+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;
boost::system::error_code errcode;
@@ -2709,7 +2720,14 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
LOG_PRINT_L0("Failed to parse data from " << signed_filename);
return false;
}
- LOG_PRINT_L1("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions");
+ 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));
+
+ if (accept_func && !accept_func(signed_txs))
+ {
+ LOG_PRINT_L1("Transactions rejected by callback");
+ return false;
+ }
ptx = signed_txs.ptx;
@@ -3188,8 +3206,9 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
ptx.tx_key = tx_key;
ptx.dests = dsts;
ptx.construction_data.sources = sources;
- ptx.construction_data.destinations = dsts;
ptx.construction_data.change_dts = change_dts;
+ ptx.construction_data.splitted_dsts = splitted_dsts;
+ ptx.construction_data.selected_transfers = selected_transfers;
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
@@ -3307,8 +3326,9 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.tx_key = tx_key;
ptx.dests = dsts;
ptx.construction_data.sources = sources;
- ptx.construction_data.destinations = dsts;
ptx.construction_data.change_dts = change_dts;
+ ptx.construction_data.splitted_dsts = splitted_dsts;
+ ptx.construction_data.selected_transfers = selected_transfers;
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = true;
@@ -3531,7 +3551,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
if (!prefered_inputs.empty())
{
string s;
- for (auto i: prefered_inputs) s += print_money(m_transfers[i].amount()) + " ";
+ for (auto i: prefered_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") ";
LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s);
}
}
@@ -3551,7 +3571,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
size_t idx = !prefered_inputs.empty() ? pop_back(prefered_inputs) : !unused_transfers_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : pop_best_value(unused_dust_indices, tx.selected_transfers);
const transfer_details &td = m_transfers[idx];
- LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
+ LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()) << ", ki " << td.m_key_image);
// add this output to the list to spend
tx.selected_transfers.push_back(idx);
@@ -4243,7 +4263,11 @@ std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key
crypto::key_image ki;
cryptonote::keypair in_ephemeral;
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, td.m_internal_output_index, in_ephemeral, ki);
- THROW_WALLET_EXCEPTION_IF(ki != td.m_key_image,
+
+ bool zero_key_image = true;
+ for (size_t i = 0; i < sizeof(td.m_key_image); ++i)
+ zero_key_image &= (td.m_key_image.data[i] == 0);
+ THROW_WALLET_EXCEPTION_IF(!zero_key_image && ki != td.m_key_image,
error::wallet_internal_error, "key_image generated not matched with cached key image");
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
@@ -4330,6 +4354,50 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
return m_transfers[signed_key_images.size() - 1].m_block_height;
}
//----------------------------------------------------------------------------------------------------
+std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
+{
+ std::vector<tools::wallet2::transfer_details> outs;
+
+ outs.reserve(m_transfers.size());
+ for (size_t n = 0; n < m_transfers.size(); ++n)
+ {
+ const transfer_details &td = m_transfers[n];
+
+ outs.push_back(td);
+ }
+
+ return outs;
+}
+//----------------------------------------------------------------------------------------------------
+size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs)
+{
+ m_transfers.clear();
+ m_transfers.reserve(outputs.size());
+ for (size_t i = 0; i < outputs.size(); ++i)
+ {
+ transfer_details td = outputs[i];
+
+ // the hot wallet wouldn't have known about key images (except if we already exported them)
+ cryptonote::keypair in_ephemeral;
+ std::vector<tx_extra_field> tx_extra_fields;
+ tx_extra_pub_key pub_key_field;
+
+ THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + i);
+ THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(td.m_tx.extra, tx_extra_fields), error::wallet_internal_error,
+ "Transaction extra has unsupported format at index " + i);
+ THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), error::wallet_internal_error,
+ "Public key wasn't found in the transaction extra at index " + i);
+
+ cryptonote::generate_key_image_helper(m_account.get_keys(), pub_key_field.pub_key, td.m_internal_output_index, in_ephemeral, td.m_key_image);
+ THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key,
+ error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + i);
+
+ m_transfers.push_back(td);
+ }
+
+ return m_transfers.size();
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::generate_genesis(cryptonote::block& b) {
if (m_testnet)
{
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index fa9797219..6cd288ac1 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -154,16 +154,18 @@ namespace tools
struct tx_construction_data
{
std::vector<cryptonote::tx_source_entry> sources;
- std::vector<cryptonote::tx_destination_entry> destinations;
cryptonote::tx_destination_entry change_dts;
+ std::vector<cryptonote::tx_destination_entry> splitted_dsts;
+ std::list<size_t> selected_transfers;
std::vector<uint8_t> extra;
uint64_t unlock_time;
bool use_rct;
BEGIN_SERIALIZE_OBJECT()
FIELD(sources)
- FIELD(destinations)
FIELD(change_dts)
+ FIELD(splitted_dsts)
+ FIELD(selected_transfers)
FIELD(extra)
VARINT_FIELD(unlock_time)
FIELD(use_rct)
@@ -355,7 +357,7 @@ namespace tools
void commit_tx(std::vector<pending_tx>& ptx_vector);
bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename);
bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::function<bool(const unsigned_tx_set&)> accept_func = NULL);
- bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx);
+ bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
@@ -471,6 +473,9 @@ namespace tools
std::string sign(const std::string &data) const;
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const;
+ std::vector<tools::wallet2::transfer_details> export_outputs() const;
+ size_t import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs);
+
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const;
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent);
@@ -521,6 +526,7 @@ namespace tools
void set_unspent(size_t idx);
template<typename entry>
void get_outs(std::vector<std::vector<entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count);
+ bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki);
cryptonote::account_base m_account;
std::string m_daemon_address;
@@ -930,8 +936,9 @@ namespace tools
ptx.tx_key = tx_key;
ptx.dests = dsts;
ptx.construction_data.sources = sources;
- ptx.construction_data.destinations = dsts;
ptx.construction_data.change_dts = change_dts;
+ ptx.construction_data.splitted_dsts = splitted_dsts;
+ ptx.construction_data.selected_transfers = selected_transfers;
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt
index 5ec53cd2b..2ecd917c0 100644
--- a/tests/performance_tests/CMakeLists.txt
+++ b/tests/performance_tests/CMakeLists.txt
@@ -39,6 +39,7 @@ set(performance_tests_headers
generate_key_derivation.h
generate_key_image.h
generate_key_image_helper.h
+ generate_keypair.h
is_out_to_acc.h
multi_tx_test_base.h
performance_tests.h
diff --git a/tests/performance_tests/generate_keypair.h b/tests/performance_tests/generate_keypair.h
new file mode 100644
index 000000000..4ba577e2a
--- /dev/null
+++ b/tests/performance_tests/generate_keypair.h
@@ -0,0 +1,51 @@
+// 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
+
+#pragma once
+
+#include "crypto/crypto.h"
+#include "cryptonote_core/cryptonote_basic.h"
+
+class test_generate_keypair
+{
+public:
+ static const size_t loop_count = 10000;
+
+ bool init()
+ {
+ return true;
+ }
+
+ bool test()
+ {
+ cryptonote::keypair::generate();
+ return true;
+ }
+};
diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp
index d09276230..3f4751ee8 100644
--- a/tests/performance_tests/main.cpp
+++ b/tests/performance_tests/main.cpp
@@ -41,6 +41,7 @@
#include "generate_key_derivation.h"
#include "generate_key_image.h"
#include "generate_key_image_helper.h"
+#include "generate_keypair.h"
#include "is_out_to_acc.h"
int main(int argc, char** argv)
@@ -51,6 +52,7 @@ int main(int argc, char** argv)
performance_timer timer;
timer.start();
+goto ree;
TEST_PERFORMANCE3(test_construct_tx, 1, 1, false);
TEST_PERFORMANCE3(test_construct_tx, 1, 2, false);
TEST_PERFORMANCE3(test_construct_tx, 1, 10, false);
@@ -100,6 +102,8 @@ int main(int argc, char** argv)
TEST_PERFORMANCE0(test_derive_public_key);
TEST_PERFORMANCE0(test_derive_secret_key);
TEST_PERFORMANCE0(test_ge_frombytes_vartime);
+ree:
+ TEST_PERFORMANCE0(test_generate_keypair);
TEST_PERFORMANCE0(test_cn_slow_hash);
diff --git a/utils/gpg_keys/anonimal.asc b/utils/gpg_keys/anonimal.asc
new file mode 100644
index 000000000..0fb9f827e
--- /dev/null
+++ b/utils/gpg_keys/anonimal.asc
@@ -0,0 +1,64 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+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
+=EuZn
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/utils/gpg_keys/guzzi.asc b/utils/gpg_keys/guzzi.asc
new file mode 100644
index 000000000..2c374523f
--- /dev/null
+++ b/utils/gpg_keys/guzzi.asc
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+mQENBFed+r8BCADScb2cSXAk7v0+tbEUDOf6VuhKc26C2Kfwk/Ei2iWC7Vaa/bru
+aNLpM1nVuWKdNnfX3MZo38yItvxS4HlkI29pXcrzyqd3sfPklPgLliaayKkqh1Xx
+jl6T5NM9VVJ5dMo0pMzns49xFknOsBXdSH/ziZVD0SuSNctNb5XraCrfuDPw0fn0
+lLZ4a6WwUXL9+4Y+xbvNmYHjlnAB7xjvOpprSuJ769zDGpQlV2UXeRqjjMPzVbH4
+PYgmNAItbhvog2UfKeQK8K0Fwj1uNsZ5fnqvoa9lsgsbyV4x4DbQu/W5NVP1ylDC
+MnHKABa8Rg8zzjp0G7YuBGPy/JPffwZBoEuhABEBAAG0I2d1enppam9uZXMgPGd1
+enppam9uZXMxMkBnbWFpbC5jb20+iQE5BBMBCAAjBQJXnfq/AhsDBwsJCAcDAgEG
+FQgCCQoLBBYCAwECHgECF4AACgkQXNw+OlijFbIhbggAt6pEz8g++3vHXFaEsOiK
+fSJYheSuY2NGOgmS2WBWdPp6z7nobSScYzCeF4pOnCFxM99O7i9/kfDzVp4W7lXL
+VIvLiLvKwWLkVhHhgOlerLRYNR+TjS+GtGhhL6Y2Yj1AkG2pJd59SBhbhdkqdNo0
+D614GjnyK8SGlz9xjV9ZE4csTPH2p9xOqJoRCoUuEGWHNoox0vJTuJuKhCHta1y8
+T84uFcGCagxHxqv5eqgype32iueSMfsbyyFJ2WaLaCyYKcPGbXG9iFFLqtvJU9xv
+46oPYd1HYjtXVaRnbtiDljlokEiXiQ7WPsYEgZy76KMJ30fEyXICPYvTDR1aFLYI
+1LkBDQRXnfq/AQgA2JHleHFNtQM1ECeEGAYoGzt+IyPKzuT43ZgwuxK4t0kfKNN4
+KvihBcmFqjJAwwmS+9oNPeU4BgZ4k8DX2JP3JoGzFF7MK+i8OFW0ckZ4kbNZhtq3
+e8a1fkWnkA6pQA1JppiZqqI+VNLTTPvsH8pG2UA4rL4XvxeJ7jrFnQMCfiOiuIsd
+C+MeUAHthNeLreq1LXx0s0GfPMwMM5ckKdtEKmMVCw1zZ/J2RyBzkyWdyMVPSRRV
+lCDtgSivCSG+Y+ub4tgV8ast2/wKxCV92oRaeMQVsWZ9PHdVq0tUn4I98UtCwOh7
+reMPi6a8eWJcQ/s07schqXt2iTssv/1V3PTjZQARAQABiQEfBBgBCAAJBQJXnfq/
+AhsMAAoJEFzcPjpYoxWydDsH/2sLAtzKVgbeZFF/0e+6r/P0R3Fgkv6N9o+w4A5N
+pMDXCNjFjWVkYRgyON8Y8ijkkbIkBcXmp+01HxZjrQI0WavQaLj2tavz9Np/8wPb
+UXZYc0zjxki9mdFdNDW1vgoT9nSctB4bp0xf/NnYmkPMQfDzruVkf8bW6YQzkZRP
+apjY9IxUPKFx9hQcDAsov1xXww2uQPwEGHfeSMkeCU9zfPBNmaFCvBfFTFu5UHsl
+g2hw95UUsDlpYcMs7YdqYw40EXcTQJk1aoWT7kjO1KCQWF5EDc0YRhw/REXN9fJe
+S77oEy23Q/RtJQBXzHw3chyhe4/XMQQbmuY9+OfmhHVAbF4=
+=lOXz
+-----END PGP PUBLIC KEY BLOCK-----