aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile6
-rw-r--r--README.i18n.md2
-rw-r--r--README.md15
-rw-r--r--contrib/codefresh/codefresh.yml18
-rw-r--r--contrib/epee/include/fnv1.h45
-rw-r--r--contrib/epee/include/hex.h5
-rw-r--r--contrib/epee/include/mlocker.h87
-rw-r--r--contrib/epee/include/serialization/keyvalue_serialization.h9
-rw-r--r--contrib/epee/include/serialization/keyvalue_serialization_overloads.h6
-rw-r--r--contrib/epee/include/storages/parserse_base_utils.h9
-rw-r--r--contrib/epee/include/string_tools.h7
-rw-r--r--contrib/epee/include/wipeable_string.h28
-rw-r--r--contrib/epee/src/CMakeLists.txt2
-rw-r--r--contrib/epee/src/hex.cpp10
-rw-r--r--contrib/epee/src/mlocker.cpp182
-rw-r--r--contrib/epee/src/mlog.cpp4
-rw-r--r--contrib/epee/src/wipeable_string.cpp112
m---------external/miniupnp0
m---------external/rapidjson0
m---------external/unbound0
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h2
-rw-r--r--src/blockchain_db/blockchain_db.cpp4
-rw-r--r--src/blockchain_db/blockchain_db.h17
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp64
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h1
-rw-r--r--src/common/password.cpp22
-rw-r--r--src/common/password.h2
-rw-r--r--src/common/updates.cpp6
-rw-r--r--src/common/util.cpp1
-rw-r--r--src/crypto/CMakeLists.txt1
-rw-r--r--src/crypto/chacha.h11
-rw-r--r--src/crypto/crypto.h5
-rw-r--r--src/crypto/generic-ops.h28
-rw-r--r--src/crypto/keccak.c74
-rw-r--r--src/crypto/keccak.h14
-rw-r--r--src/cryptonote_basic/account.cpp68
-rw-r--r--src/cryptonote_basic/account.h16
-rw-r--r--src/cryptonote_core/blockchain.cpp58
-rw-r--r--src/cryptonote_core/blockchain.h30
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp28
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp3
-rw-r--r--src/cryptonote_core/tx_pool.cpp22
-rw-r--r--src/cryptonote_core/tx_pool.h9
-rw-r--r--src/cryptonote_protocol/block_queue.cpp39
-rw-r--r--src/cryptonote_protocol/block_queue.h6
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl19
-rw-r--r--src/device/device_default.cpp2
-rw-r--r--src/device/device_ledger.cpp3
-rw-r--r--src/gen_multisig/gen_multisig.cpp4
-rw-r--r--src/mnemonics/electrum-words.cpp121
-rw-r--r--src/mnemonics/electrum-words.h12
-rw-r--r--src/mnemonics/language_base.h29
-rw-r--r--src/multisig/multisig.cpp5
-rw-r--r--src/ringct/rctSigs.cpp8
-rw-r--r--src/ringct/rctTypes.h25
-rw-r--r--src/rpc/daemon_handler.cpp42
-rw-r--r--src/rpc/message_data_structs.h2
-rw-r--r--src/serialization/json_object.cpp328
-rw-r--r--src/serialization/json_object.h6
-rw-r--r--src/simplewallet/simplewallet.cpp482
-rw-r--r--src/simplewallet/simplewallet.h10
-rw-r--r--src/wallet/api/wallet.cpp18
-rw-r--r--src/wallet/api/wallet.h1
-rw-r--r--src/wallet/node_rpc_proxy.cpp2
-rw-r--r--src/wallet/wallet2.cpp469
-rw-r--r--src/wallet/wallet2.h112
-rw-r--r--src/wallet/wallet_errors.h9
-rw-r--r--src/wallet/wallet_rpc_server.cpp101
-rw-r--r--src/wallet/wallet_rpc_server.h5
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h46
-rw-r--r--tests/functional_tests/transactions_flow_test.cpp6
-rw-r--r--tests/fuzz/signature.cpp1
-rw-r--r--tests/performance_tests/equality.h72
-rw-r--r--tests/performance_tests/main.cpp6
-rw-r--r--tests/unit_tests/CMakeLists.txt8
-rw-r--r--tests/unit_tests/account.cpp71
-rw-r--r--tests/unit_tests/crypto.cpp15
-rw-r--r--tests/unit_tests/hardfork.cpp1
-rw-r--r--tests/unit_tests/json_serialization.cpp217
-rw-r--r--tests/unit_tests/keccak.cpp150
-rw-r--r--tests/unit_tests/mlocker.cpp186
-rw-r--r--tests/unit_tests/mnemonics.cpp30
-rw-r--r--tests/unit_tests/multisig.cpp15
-rw-r--r--tests/unit_tests/ringdb.cpp2
-rw-r--r--tests/unit_tests/serialization.cpp2
-rw-r--r--tests/unit_tests/wipeable_string.cpp204
86 files changed, 3105 insertions, 820 deletions
diff --git a/Dockerfile b/Dockerfile
index 0decb4fde..5fde5a477 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -23,9 +23,9 @@ RUN set -ex && \
WORKDIR /usr/local
#Cmake
-ARG CMAKE_VERSION=3.11.4
-ARG CMAKE_VERSION_DOT=v3.11
-ARG CMAKE_HASH=8f864e9f78917de3e1483e256270daabc4a321741592c5b36af028e72bff87f5
+ARG CMAKE_VERSION=3.12.0
+ARG CMAKE_VERSION_DOT=v3.12
+ARG CMAKE_HASH=d0781a90f6cdb9049d104ac16a150f9350b693498b9dea8a0331e799db6b9d69
RUN set -ex \
&& curl -s -O https://cmake.org/files/${CMAKE_VERSION_DOT}/cmake-${CMAKE_VERSION}.tar.gz \
&& echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}.tar.gz" | sha256sum -c \
diff --git a/README.i18n.md b/README.i18n.md
index b0379bfb3..504fc9e75 100644
--- a/README.i18n.md
+++ b/README.i18n.md
@@ -7,7 +7,7 @@ In order to use the same translation workflow as the [Monero Core GUI](https://g
### Tools for translators
-In order to create, update or build translations files, you need to have Qt tools installed. For translating, you need either the **Qt Linguist GUI** ([part of QT Creator](https://www.qt.io/download-open-source/#allDownloadsDiv-9) or a [3rd-party standalone version](https://github.com/lelegard/qtlinguist-installers/releases)), or another tool that supports Qt ts files, such as Transifex. The files are XML, so they can be edited in any plain text editor if needed.
+In order to create, update or build translations files, you need to have Qt tools installed. For translating, you need either the **Qt Linguist GUI** ([part of Qt Creator](https://www.qt.io/download) or a [3rd-party standalone version](https://github.com/lelegard/qtlinguist-installers/releases)), or another tool that supports Qt ts files, such as Transifex. The files are XML, so they can be edited in any plain text editor if needed.
### Creating / modifying translations
diff --git a/README.md b/README.md
index 98c300a9d..ee7c112e7 100644
--- a/README.md
+++ b/README.md
@@ -107,7 +107,7 @@ Dates are provided in the format YYYY-MM-DD.
| 1220516 | 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions |
| 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm |
| 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 |
-| 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.2.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs
+| 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.3.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs
| XXXXXXX | 2018-10-XX | XX | XXXXXXXXX | XXXXXXXXX | X
X's indicate that these details have not been determined as of commit date.
@@ -137,8 +137,9 @@ library archives (`.a`).
| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | NO | C++ libraries |
| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | NO | sha256 sum |
| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library |
+| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ |
| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver |
-| libsodium | ? | NO | `libsodium-dev` | ? | `libsodium-devel` | NO | libsodium |
+| libsodium | ? | NO | `libsodium-dev` | ? | `libsodium-devel` | NO | cryptography |
| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces |
| liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind |
| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing |
@@ -154,7 +155,7 @@ library archives (`.a`).
build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ```
Debian / Ubuntu one liner for all dependencies
-``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev ```
+``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev libpgm-dev```
### Cloning the repository
@@ -177,7 +178,7 @@ invokes cmake commands as needed.
* Change to the root of the source code directory, change to the most recent release branch, and build:
cd monero
- git checkout v0.12.2.0
+ git checkout v0.12.3.0
make
*Optional*: If your machine has several cores and enough memory, enable
@@ -239,7 +240,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```
git clone https://github.com/monero-project/monero.git
cd monero
- git checkout tags/v0.12.2.0
+ git checkout tags/v0.12.3.0
```
* Build:
```
@@ -336,9 +337,9 @@ application.
cd monero
-* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.12.1.0'. If you dont care about the version and just want binaries from master, skip this step:
+* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.12.3.0'. If you dont care about the version and just want binaries from master, skip this step:
- git checkout v0.12.1.0
+ git checkout v0.12.3.0
* If you are on a 64-bit system, run:
diff --git a/contrib/codefresh/codefresh.yml b/contrib/codefresh/codefresh.yml
new file mode 100644
index 000000000..a22debfa1
--- /dev/null
+++ b/contrib/codefresh/codefresh.yml
@@ -0,0 +1,18 @@
+version: '1.0'
+steps:
+ init_submodules:
+ title: Init Submodules
+ commands:
+ - git submodule update --init --recursive
+ image: codefreshio/git-image:latest
+ working_directory: ${{main_clone}}
+
+ BuildingDockerImage:
+ title: Building Docker Image
+ type: build
+ image_name: monero
+ working_directory: ./
+ tag: '${{CF_BRANCH_TAG_NORMALIZED}}'
+ dockerfile: Dockerfile
+ build_arguments:
+ - NPROC=1
diff --git a/contrib/epee/include/fnv1.h b/contrib/epee/include/fnv1.h
new file mode 100644
index 000000000..c04389bca
--- /dev/null
+++ b/contrib/epee/include/fnv1.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2018, 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.
+
+#pragma once
+
+namespace epee
+{
+
+namespace fnv
+{
+ inline uint64_t FNV1a(const char *ptr, size_t sz)
+ {
+ uint64_t h = 0xcbf29ce484222325;
+ for (size_t i = 0; i < sz; ++i)
+ h = (h ^ *(const uint8_t*)ptr++) * 0x100000001b3;
+ return h;
+ }
+}
+
+}
diff --git a/contrib/epee/include/hex.h b/contrib/epee/include/hex.h
index e960da1d2..02600c320 100644
--- a/contrib/epee/include/hex.h
+++ b/contrib/epee/include/hex.h
@@ -33,6 +33,7 @@
#include <iosfwd>
#include <string>
+#include "wipeable_string.h"
#include "span.h"
namespace epee
@@ -41,6 +42,8 @@ namespace epee
{
//! \return A std::string containing hex of `src`.
static std::string string(const span<const std::uint8_t> src);
+ //! \return A epee::wipeable_string containing hex of `src`.
+ static epee::wipeable_string wipeable_string(const span<const std::uint8_t> src);
//! \return An array containing hex of `src`.
template<std::size_t N>
@@ -59,6 +62,8 @@ namespace epee
static void formatted(std::ostream& out, const span<const std::uint8_t> src);
private:
+ template<typename T> T static convert(const span<const std::uint8_t> src);
+
//! Write `src` bytes as hex to `out`. `out` must be twice the length
static void buffer_unchecked(char* out, const span<const std::uint8_t> src) noexcept;
};
diff --git a/contrib/epee/include/mlocker.h b/contrib/epee/include/mlocker.h
new file mode 100644
index 000000000..d2fc2ed58
--- /dev/null
+++ b/contrib/epee/include/mlocker.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2018, 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.
+
+#pragma once
+
+#include <map>
+#include <boost/thread/mutex.hpp>
+
+namespace epee
+{
+ class mlocker
+ {
+ public:
+ mlocker(void *ptr, size_t len);
+ ~mlocker();
+
+ static size_t get_page_size();
+ static size_t get_num_locked_pages();
+ static size_t get_num_locked_objects();
+
+ static void lock(void *ptr, size_t len);
+ static void unlock(void *ptr, size_t len);
+
+ private:
+ static size_t page_size;
+ static size_t num_locked_objects;
+
+ static boost::mutex &mutex();
+ static std::map<size_t, unsigned int> &map();
+ static void lock_page(size_t page);
+ static void unlock_page(size_t page);
+
+ void *ptr;
+ size_t len;
+ };
+
+ /// Locks memory while in scope
+ ///
+ /// Primarily useful for making sure that private keys don't get swapped out
+ // to disk
+ template <class T>
+ struct mlocked : public T {
+ using type = T;
+
+ mlocked(): T() { mlocker::lock(this, sizeof(T)); }
+ mlocked(const T &t): T(t) { mlocker::lock(this, sizeof(T)); }
+ mlocked(const mlocked<T> &mt): T(mt) { mlocker::lock(this, sizeof(T)); }
+ mlocked(const T &&t): T(t) { mlocker::lock(this, sizeof(T)); }
+ mlocked(const mlocked<T> &&mt): T(mt) { mlocker::lock(this, sizeof(T)); }
+ mlocked<T> &operator=(const mlocked<T> &mt) { T::operator=(mt); return *this; }
+ ~mlocked() { mlocker::unlock(this, sizeof(T)); }
+ };
+
+ template<typename T>
+ T& unwrap(mlocked<T>& src) { return src; }
+
+ template<typename T>
+ const T& unwrap(mlocked<T> const& src) { return src; }
+
+ template <class T, size_t N>
+ using mlocked_arr = mlocked<std::array<T, N>>;
+}
diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h
index 5791e1998..fc5a21851 100644
--- a/contrib/epee/include/serialization/keyvalue_serialization.h
+++ b/contrib/epee/include/serialization/keyvalue_serialization.h
@@ -85,6 +85,14 @@ public: \
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name)
+#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, val_name, default_value) \
+ do { \
+ static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
+ bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name); \
+ if (!ret) \
+ epee::serialize_default(this_ref.varialble, default_value); \
+ } while(0);
+
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \
epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(this_ref.varialble, stg, hparent_section, val_name);
@@ -92,6 +100,7 @@ public: \
#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble)
#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble)
+#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(varialble, def) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, #varialble, def)
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble)
#define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value)
diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
index 09087f785..15c95f07a 100644
--- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
+++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
@@ -35,6 +35,11 @@
namespace epee
{
+ namespace
+ {
+ template<class C> void hint_resize(C &container, size_t size) {}
+ template<class C> void hint_resize(std::vector<C> &container, size_t size) { container.reserve(size); }
+ }
namespace serialization
{
@@ -158,6 +163,7 @@ namespace epee
false,
"size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type) << ", type " << typeid(typename stl_container::value_type).name());
size_t count = (loaded_size/sizeof(typename stl_container::value_type));
+ hint_resize(container, count);
for(size_t i = 0; i < count; i++)
container.insert(container.end(), *(pelem++));
}
diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h
index c809392f4..8c6c1a64d 100644
--- a/contrib/epee/include/storages/parserse_base_utils.h
+++ b/contrib/epee/include/storages/parserse_base_utils.h
@@ -28,6 +28,8 @@
#pragma once
+#include <algorithm>
+
namespace epee
{
namespace misc_utils
@@ -36,8 +38,12 @@ namespace misc_utils
{
inline std::string transform_to_escape_sequence(const std::string& src)
{
- //std::stringstream res;
+ static const char escaped[] = "\b\f\n\r\t\v\"\\/";
+ if (std::find_first_of(src.begin(), src.end(), escaped, escaped + sizeof(escaped)) == src.end())
+ return src;
+
std::string res;
+ res.reserve(2 * src.size());
for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it)
{
switch(*it)
@@ -84,6 +90,7 @@ namespace misc_utils
inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
{
val.clear();
+ val.reserve(std::distance(star_end_string, buf_end));
bool escape_mode = false;
std::string::const_iterator it = star_end_string;
++it;
diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h
index 8d8603076..aba065cc7 100644
--- a/contrib/epee/include/string_tools.h
+++ b/contrib/epee/include/string_tools.h
@@ -46,6 +46,7 @@
#include <boost/algorithm/string/predicate.hpp>
#include "hex.h"
#include "memwipe.h"
+#include "mlocker.h"
#include "span.h"
#include "warnings.h"
@@ -358,6 +359,12 @@ POP_WARNINGS
return hex_to_pod(hex_str, unwrap(s));
}
//----------------------------------------------------------------------------
+ template<class t_pod_type>
+ bool hex_to_pod(const std::string& hex_str, epee::mlocked<t_pod_type>& s)
+ {
+ return hex_to_pod(hex_str, unwrap(s));
+ }
+ //----------------------------------------------------------------------------
bool validate_hex(uint64_t length, const std::string& str);
//----------------------------------------------------------------------------
inline std::string get_extension(const std::string& str)
diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h
index 70d1a9586..4cebe5fdf 100644
--- a/contrib/epee/include/wipeable_string.h
+++ b/contrib/epee/include/wipeable_string.h
@@ -28,28 +28,43 @@
#pragma once
+#include <boost/optional/optional_fwd.hpp>
#include <stddef.h>
#include <vector>
#include <string>
+#include "fnv1.h"
namespace epee
{
class wipeable_string
{
public:
+ typedef char value_type;
+
wipeable_string() {}
wipeable_string(const wipeable_string &other);
wipeable_string(wipeable_string &&other);
wipeable_string(const std::string &other);
wipeable_string(std::string &&other);
wipeable_string(const char *s);
+ wipeable_string(const char *s, size_t len);
~wipeable_string();
void wipe();
void push_back(char c);
- void pop_back();
+ void operator+=(char c);
+ void operator+=(const std::string &s);
+ void operator+=(const epee::wipeable_string &s);
+ void operator+=(const char *s);
+ void append(const char *ptr, size_t len);
+ char pop_back();
const char *data() const noexcept { return buffer.data(); }
+ char *data() noexcept { return buffer.data(); }
size_t size() const noexcept { return buffer.size(); }
+ size_t length() const noexcept { return buffer.size(); }
bool empty() const noexcept { return buffer.empty(); }
+ void trim();
+ void split(std::vector<wipeable_string> &fields) const;
+ boost::optional<wipeable_string> parse_hexstr() const;
void resize(size_t sz);
void reserve(size_t sz);
void clear();
@@ -65,3 +80,14 @@ namespace epee
std::vector<char> buffer;
};
}
+
+namespace std
+{
+ template<> struct hash<epee::wipeable_string>
+ {
+ size_t operator()(const epee::wipeable_string &s) const
+ {
+ return epee::fnv::FNV1a(s.data(), s.size());
+ }
+ };
+}
diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt
index c4750cea0..0b5e7ae6c 100644
--- a/contrib/epee/src/CMakeLists.txt
+++ b/contrib/epee/src/CMakeLists.txt
@@ -27,7 +27,7 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c
- connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp)
+ connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp)
if (USE_READLINE AND GNU_READLINE_FOUND)
add_library(epee_readline STATIC readline_buffer.cpp)
endif()
diff --git a/contrib/epee/src/hex.cpp b/contrib/epee/src/hex.cpp
index c143b2dc2..5c8acc8be 100644
--- a/contrib/epee/src/hex.cpp
+++ b/contrib/epee/src/hex.cpp
@@ -52,17 +52,21 @@ namespace epee
}
}
- std::string to_hex::string(const span<const std::uint8_t> src)
+ template<typename T>
+ T to_hex::convert(const span<const std::uint8_t> src)
{
if (std::numeric_limits<std::size_t>::max() / 2 < src.size())
throw std::range_error("hex_view::to_string exceeded maximum size");
- std::string out{};
+ T out{};
out.resize(src.size() * 2);
- buffer_unchecked(std::addressof(out[0]), src);
+ to_hex::buffer_unchecked((char*)out.data(), src); // can't see the non const version in wipeable_string??
return out;
}
+ std::string to_hex::string(const span<const std::uint8_t> src) { return convert<std::string>(src); }
+ epee::wipeable_string to_hex::wipeable_string(const span<const std::uint8_t> src) { return convert<epee::wipeable_string>(src); }
+
void to_hex::buffer(std::ostream& out, const span<const std::uint8_t> src)
{
write_hex(std::ostreambuf_iterator<char>{out}, src);
diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp
new file mode 100644
index 000000000..5573d591a
--- /dev/null
+++ b/contrib/epee/src/mlocker.cpp
@@ -0,0 +1,182 @@
+// Copyright (c) 2018, 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.
+
+#if defined __GNUC__ && !defined _WIN32
+#define HAVE_MLOCK 1
+#endif
+
+#include <unistd.h>
+#if defined HAVE_MLOCK
+#include <sys/mman.h>
+#endif
+#include "misc_log_ex.h"
+#include "syncobj.h"
+#include "mlocker.h"
+
+static size_t query_page_size()
+{
+#if defined HAVE_MLOCK
+ long ret = sysconf(_SC_PAGESIZE);
+ if (ret <= 0)
+ {
+ MERROR("Failed to determine page size");
+ return 0;
+ }
+ MINFO("Page size: " << ret);
+ return ret;
+#else
+#warning Missing query_page_size implementation
+#endif
+ return 0;
+}
+
+static void do_lock(void *ptr, size_t len)
+{
+#if defined HAVE_MLOCK
+ int ret = mlock(ptr, len);
+ if (ret < 0)
+ MERROR("Error locking page at " << ptr << ": " << strerror(errno));
+#else
+#warning Missing do_lock implementation
+#endif
+}
+
+static void do_unlock(void *ptr, size_t len)
+{
+#if defined HAVE_MLOCK
+ int ret = munlock(ptr, len);
+ if (ret < 0)
+ MERROR("Error unlocking page at " << ptr << ": " << strerror(errno));
+#else
+#warning Missing implementation of page size detection
+#endif
+}
+
+namespace epee
+{
+ size_t mlocker::page_size = 0;
+ size_t mlocker::num_locked_objects = 0;
+
+ boost::mutex &mlocker::mutex()
+ {
+ static boost::mutex vmutex;
+ return vmutex;
+ }
+ std::map<size_t, unsigned int> &mlocker::map()
+ {
+ static std::map<size_t, unsigned int> vmap;
+ return vmap;
+ }
+
+ size_t mlocker::get_page_size()
+ {
+ CRITICAL_REGION_LOCAL(mutex());
+ if (page_size == 0)
+ page_size = query_page_size();
+ return page_size;
+ }
+
+ mlocker::mlocker(void *ptr, size_t len): ptr(ptr), len(len)
+ {
+ lock(ptr, len);
+ }
+
+ mlocker::~mlocker()
+ {
+ unlock(ptr, len);
+ }
+
+ void mlocker::lock(void *ptr, size_t len)
+ {
+ size_t page_size = get_page_size();
+ if (page_size == 0)
+ return;
+
+ CRITICAL_REGION_LOCAL(mutex());
+ const size_t first = ((uintptr_t)ptr) / page_size;
+ const size_t last = (((uintptr_t)ptr) + len - 1) / page_size;
+ for (size_t page = first; page <= last; ++page)
+ lock_page(page);
+ ++num_locked_objects;
+ }
+
+ void mlocker::unlock(void *ptr, size_t len)
+ {
+ size_t page_size = get_page_size();
+ if (page_size == 0)
+ return;
+ CRITICAL_REGION_LOCAL(mutex());
+ const size_t first = ((uintptr_t)ptr) / page_size;
+ const size_t last = (((uintptr_t)ptr) + len - 1) / page_size;
+ for (size_t page = first; page <= last; ++page)
+ unlock_page(page);
+ --num_locked_objects;
+ }
+
+ size_t mlocker::get_num_locked_pages()
+ {
+ CRITICAL_REGION_LOCAL(mutex());
+ return map().size();
+ }
+
+ size_t mlocker::get_num_locked_objects()
+ {
+ CRITICAL_REGION_LOCAL(mutex());
+ return num_locked_objects;
+ }
+
+ void mlocker::lock_page(size_t page)
+ {
+ std::pair<std::map<size_t, unsigned int>::iterator, bool> p = map().insert(std::make_pair(page, 1));
+ if (p.second)
+ {
+ do_lock((void*)(page * page_size), page_size);
+ }
+ else
+ {
+ ++p.first->second;
+ }
+ }
+
+ void mlocker::unlock_page(size_t page)
+ {
+ std::map<size_t, unsigned int>::iterator i = map().find(page);
+ if (i == map().end())
+ {
+ MERROR("Attempt to unlock unlocked page at " << (void*)(page * page_size));
+ }
+ else
+ {
+ if (!--i->second)
+ {
+ map().erase(i);
+ do_unlock((void*)(page * page_size), page_size);
+ }
+ }
+ }
+}
diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp
index e8248c958..818fc0a69 100644
--- a/contrib/epee/src/mlog.cpp
+++ b/contrib/epee/src/mlog.cpp
@@ -142,7 +142,9 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s
{
std::vector<boost::filesystem::path> found_files;
const boost::filesystem::directory_iterator end_itr;
- for (boost::filesystem::directory_iterator iter(boost::filesystem::path(filename_base).parent_path()); iter != end_itr; ++iter)
+ const boost::filesystem::path filename_base_path(filename_base);
+ const boost::filesystem::path parent_path = filename_base_path.has_parent_path() ? filename_base_path.parent_path() : ".";
+ for (boost::filesystem::directory_iterator iter(parent_path); iter != end_itr; ++iter)
{
const std::string filename = iter->path().string();
if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0)
diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp
index 6ed4ee8a2..7c9722765 100644
--- a/contrib/epee/src/wipeable_string.cpp
+++ b/contrib/epee/src/wipeable_string.cpp
@@ -26,11 +26,22 @@
// 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 <boost/optional/optional.hpp>
#include <string.h>
#include "memwipe.h"
#include "misc_log_ex.h"
#include "wipeable_string.h"
+namespace
+{
+ int atolower(int c)
+ {
+ if (c >= 'A' && c <= 'Z')
+ c |= 32;
+ return c;
+ }
+}
+
namespace epee
{
@@ -69,6 +80,12 @@ wipeable_string::wipeable_string(const char *s)
memcpy(buffer.data(), s, size());
}
+wipeable_string::wipeable_string(const char *s, size_t len)
+{
+ grow(len);
+ memcpy(buffer.data(), s, len);
+}
+
wipeable_string::~wipeable_string()
{
wipe();
@@ -109,9 +126,100 @@ void wipeable_string::push_back(char c)
buffer.back() = c;
}
-void wipeable_string::pop_back()
+void wipeable_string::operator+=(char c)
+{
+ push_back(c);
+}
+
+void wipeable_string::append(const char *ptr, size_t len)
+{
+ const size_t orgsz = size();
+ CHECK_AND_ASSERT_THROW_MES(orgsz < std::numeric_limits<size_t>::max() - len, "Appended data too large");
+ grow(orgsz + len);
+ if (len > 0)
+ memcpy(data() + orgsz, ptr, len);
+}
+
+void wipeable_string::operator+=(const char *s)
+{
+ append(s, strlen(s));
+}
+
+void wipeable_string::operator+=(const epee::wipeable_string &s)
+{
+ append(s.data(), s.size());
+}
+
+void wipeable_string::operator+=(const std::string &s)
+{
+ append(s.c_str(), s.size());
+}
+
+void wipeable_string::trim()
+{
+ size_t prefix = 0;
+ while (prefix < size() && data()[prefix] == ' ')
+ ++prefix;
+ if (prefix > 0)
+ memmove(buffer.data(), buffer.data() + prefix, size() - prefix);
+
+ size_t suffix = 0;
+ while (suffix < size()-prefix && data()[size() - 1 - prefix - suffix] == ' ')
+ ++suffix;
+
+ resize(size() - prefix - suffix);
+}
+
+void wipeable_string::split(std::vector<wipeable_string> &fields) const
+{
+ fields.clear();
+ size_t len = size();
+ const char *ptr = data();
+ bool space = true;
+ while (len--)
+ {
+ const char c = *ptr++;
+ if (c != ' ')
+ {
+ if (space)
+ fields.push_back({});
+ fields.back().push_back(c);
+ }
+ space = c == ' ';
+ }
+}
+
+boost::optional<epee::wipeable_string> wipeable_string::parse_hexstr() const
+{
+ if (size() % 2 != 0)
+ return boost::none;
+ boost::optional<epee::wipeable_string> res = epee::wipeable_string("");
+ const size_t len = size();
+ const char *d = data();
+ res->grow(0, len / 2);
+ static constexpr const char hex[] = u8"0123456789abcdef";
+ for (size_t i = 0; i < len; i += 2)
+ {
+ char c = atolower(d[i]);
+ const char *ptr0 = strchr(hex, c);
+ if (!ptr0)
+ return boost::none;
+ c = atolower(d[i+1]);
+ const char *ptr1 = strchr(hex, c);
+ if (!ptr1)
+ return boost::none;
+ res->push_back(((ptr0-hex)<<4) | (ptr1-hex));
+ }
+ return res;
+}
+
+char wipeable_string::pop_back()
{
- resize(size() - 1);
+ const size_t sz = size();
+ CHECK_AND_ASSERT_THROW_MES(sz > 0, "Popping from an empty string");
+ const char c = buffer.back();
+ resize(sz - 1);
+ return c;
}
void wipeable_string::resize(size_t sz)
diff --git a/external/miniupnp b/external/miniupnp
-Subproject 6a63f9954959119568fbc4af57d7b491b9428d8
+Subproject 6b9b73a567e351b844f96c077f7b752ea92e298
diff --git a/external/rapidjson b/external/rapidjson
-Subproject af223d44f4e8d3772cb1ac0ce8bc2a132b51717
+Subproject 129d19ba7f496df5e33658527a7158c79b99c21
diff --git a/external/unbound b/external/unbound
-Subproject 193bdc4ee3fe2b0d17e547e86512528c2614483
+Subproject d3724dfa553429d368c27aef160f02f5e8b8075
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index c90d030a2..8c99ab255 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -303,7 +303,6 @@ public:
virtual uint64_t get_indexing_base() const { return 1; }
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index);
- virtual output_data_t get_output_key(const uint64_t& global_index) const;
virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs);
virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const;
@@ -419,6 +418,7 @@ private:
* @return the global index of the desired output
*/
uint64_t get_output_global_index(const uint64_t& amount, const uint64_t& index);
+ output_data_t get_output_key(const uint64_t& global_index) const;
void checkpoint_worker() const;
void check_open() const;
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 8544cc3f0..78d63fdcf 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -87,8 +87,8 @@ const command_line::arg_descriptor<std::string> arg_db_type = {
};
const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
"db-sync-mode"
-, "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[nblocks_per_sync]."
-, "fast:async:1000"
+, "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[<nblocks_per_sync>[blocks]|<nbytes_per_sync>[bytes]]."
+, "fast:async:250000000bytes"
};
const command_line::arg_descriptor<bool> arg_db_salvage = {
"db-salvage"
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 6851e2404..4431ca44c 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1262,23 +1262,6 @@ public:
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) = 0;
/**
- * @brief get some of an output's data
- *
- * The subclass should return the public key, unlock time, and block height
- * for the output with the given global index, collected in a struct.
- *
- * If the output cannot be found, the subclass should throw OUTPUT_DNE.
- *
- * If any of these parts cannot be found, but some are, the subclass
- * should throw DB_ERROR with a message stating as much.
- *
- * @param global_index the output's index (global)
- *
- * @return the requested output data
- */
- virtual output_data_t get_output_key(const uint64_t& global_index) const = 0;
-
- /**
* @brief gets an output's tx hash and index
*
* The subclass should return the hash of the transaction which created the
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 627038ca7..32ed0e9a2 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1971,14 +1971,25 @@ std::vector<uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs(const std
MDB_val v;
+ uint64_t prev_height = heights[0];
for (uint64_t height: heights)
{
- MDB_val_set(v, height);
- result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (height == prev_height + 1)
+ {
+ MDB_val k2;
+ result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT);
+ }
+ else
+ {
+ v.mv_size = sizeof(uint64_t);
+ v.mv_data = (void*)&height;
+ result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ }
if (result)
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
const mdb_block_info *bi = (const mdb_block_info *)v.mv_data;
res.push_back(bi->bi_cum_rct);
+ prev_height = height;
}
TXN_POSTFIX_RDONLY();
@@ -2454,55 +2465,6 @@ uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const
return num_elems;
}
-// This is a lot harder now that we've removed the output_keys index
-output_data_t BlockchainLMDB::get_output_key(const uint64_t &global_index) const
-{
- LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " (unused version - does nothing)");
- check_open();
- TXN_PREFIX_RDONLY();
- RCURSOR(output_txs);
- RCURSOR(tx_indices);
-
- output_data_t od;
- MDB_val_set(v, global_index);
- auto get_result = mdb_cursor_get(m_cur_output_txs, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
- if (get_result == MDB_NOTFOUND)
- throw1(OUTPUT_DNE("output with given index not in db"));
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch output tx hash"));
-
- outtx *ot = (outtx *)v.mv_data;
-
- MDB_val_set(val_h, ot->tx_hash);
- get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &val_h, MDB_GET_BOTH);
- if (get_result)
- throw0(DB_ERROR(lmdb_error(std::string("DB error attempting to fetch transaction index from hash ") + epee::string_tools::pod_to_hex(ot->tx_hash) + ": ", get_result).c_str()));
-
- txindex *tip = (txindex *)val_h.mv_data;
- MDB_val_set(val_tx_id, tip->data.tx_id);
- MDB_val result;
- get_result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &result, MDB_SET);
- if (get_result == MDB_NOTFOUND)
- throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(ot->tx_hash)).append(" not found in db").c_str()));
- else if (get_result)
- throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", get_result).c_str()));
-
- blobdata bd;
- bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size);
-
- transaction tx;
- if (!parse_and_validate_tx_base_from_blob(bd, tx))
- throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
-
- const tx_out tx_output = tx.vout[ot->local_index];
- od.unlock_time = tip->data.unlock_time;
- od.height = tip->data.block_id;
- od.pubkey = boost::get<txout_to_key>(tx_output.target).key;
-
- TXN_POSTFIX_RDONLY();
- return od;
-}
-
output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 8b214d2df..8ff2073da 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -243,7 +243,6 @@ public:
virtual uint64_t get_num_outputs(const uint64_t& amount) const;
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index);
- virtual output_data_t get_output_key(const uint64_t& global_index) const;
virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false);
virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const;
diff --git a/src/common/password.cpp b/src/common/password.cpp
index 3ce2ba42a..5671c4a4e 100644
--- a/src/common/password.cpp
+++ b/src/common/password.cpp
@@ -54,7 +54,7 @@ namespace
return 0 != _isatty(_fileno(stdin));
}
- bool read_from_tty(epee::wipeable_string& pass)
+ bool read_from_tty(epee::wipeable_string& pass, bool hide_input)
{
static constexpr const char BACKSPACE = 8;
@@ -62,7 +62,7 @@ namespace
DWORD mode_old;
::GetConsoleMode(h_cin, &mode_old);
- DWORD mode_new = mode_old & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
+ DWORD mode_new = mode_old & ~((hide_input ? ENABLE_ECHO_INPUT : 0) | ENABLE_LINE_INPUT);
::SetConsoleMode(h_cin, mode_new);
bool r = true;
@@ -107,14 +107,14 @@ namespace
return 0 != isatty(fileno(stdin));
}
- int getch() noexcept
+ int getch(bool hide_input) noexcept
{
struct termios tty_old;
tcgetattr(STDIN_FILENO, &tty_old);
struct termios tty_new;
tty_new = tty_old;
- tty_new.c_lflag &= ~(ICANON | ECHO);
+ tty_new.c_lflag &= ~(ICANON | (hide_input ? ECHO : 0));
tcsetattr(STDIN_FILENO, TCSANOW, &tty_new);
int ch = getchar();
@@ -124,14 +124,14 @@ namespace
return ch;
}
- bool read_from_tty(epee::wipeable_string& aPass)
+ bool read_from_tty(epee::wipeable_string& aPass, bool hide_input)
{
static constexpr const char BACKSPACE = 127;
aPass.reserve(tools::password_container::max_password_size);
while (aPass.size() < tools::password_container::max_password_size)
{
- int ch = getch();
+ int ch = getch(hide_input);
if (EOF == ch || ch == EOT)
{
return false;
@@ -159,18 +159,18 @@ namespace
#endif // end !WIN32
- bool read_from_tty(const bool verify, const char *message, epee::wipeable_string& pass1, epee::wipeable_string& pass2)
+ bool read_from_tty(const bool verify, const char *message, bool hide_input, epee::wipeable_string& pass1, epee::wipeable_string& pass2)
{
while (true)
{
if (message)
std::cout << message <<": " << std::flush;
- if (!read_from_tty(pass1))
+ if (!read_from_tty(pass1, hide_input))
return false;
if (verify)
{
std::cout << "Confirm password: ";
- if (!read_from_tty(pass2))
+ if (!read_from_tty(pass2, hide_input))
return false;
if(pass1!=pass2)
{
@@ -229,12 +229,12 @@ namespace tools
std::atomic<bool> password_container::is_prompting(false);
- boost::optional<password_container> password_container::prompt(const bool verify, const char *message)
+ boost::optional<password_container> password_container::prompt(const bool verify, const char *message, bool hide_input)
{
is_prompting = true;
password_container pass1{};
password_container pass2{};
- if (is_cin_tty() ? read_from_tty(verify, message, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
+ if (is_cin_tty() ? read_from_tty(verify, message, hide_input, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
{
is_prompting = false;
return {std::move(pass1)};
diff --git a/src/common/password.h b/src/common/password.h
index 61937b93a..529881e40 100644
--- a/src/common/password.h
+++ b/src/common/password.h
@@ -49,7 +49,7 @@ namespace tools
password_container(std::string&& password) noexcept;
//! \return A password from stdin TTY prompt or `std::cin` pipe.
- static boost::optional<password_container> prompt(bool verify, const char *mesage = "Password");
+ static boost::optional<password_container> prompt(bool verify, const char *mesage = "Password", bool hide_input = true);
static std::atomic<bool> is_prompting;
password_container(const password_container&) = delete;
diff --git a/src/common/updates.cpp b/src/common/updates.cpp
index 9eb402e0b..9f12f8dbc 100644
--- a/src/common/updates.cpp
+++ b/src/common/updates.cpp
@@ -69,12 +69,12 @@ namespace tools
continue;
bool alnum = true;
- for (auto c: hash)
+ for (auto c: fields[3])
if (!isalnum(c))
alnum = false;
- if (hash.size() != 64 && !alnum)
+ if (fields[3].size() != 64 && !alnum)
{
- MWARNING("Invalid hash: " << hash);
+ MWARNING("Invalid hash: " << fields[3]);
continue;
}
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 5e0d2726e..25312bdda 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -38,7 +38,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
-#include <ustat.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 71dcedcab..0c635e7cb 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -78,6 +78,7 @@ target_link_libraries(cncrypto
PUBLIC
epee
${Boost_SYSTEM_LIBRARY}
+ ${SODIUM_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h
index 1dc270faf..6e85ad0e9 100644
--- a/src/crypto/chacha.h
+++ b/src/crypto/chacha.h
@@ -40,6 +40,7 @@
#include <memory.h>
#include "memwipe.h"
+#include "mlocker.h"
#include "hash.h"
namespace crypto {
@@ -50,7 +51,7 @@ namespace crypto {
#if defined(__cplusplus)
}
- using chacha_key = tools::scrubbed_arr<uint8_t, CHACHA_KEY_SIZE>;
+ using chacha_key = epee::mlocked<tools::scrubbed_arr<uint8_t, CHACHA_KEY_SIZE>>;
#pragma pack(push, 1)
// MS VC 2012 doesn't interpret `class chacha_iv` as POD in spite of [9.0.10], so it is a struct
@@ -71,20 +72,20 @@ namespace crypto {
inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
- tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
+ epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE>> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
for (uint64_t n = 1; n < kdf_rounds; ++n)
crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
- memcpy(&unwrap(key), pwd_hash.data(), sizeof(key));
+ memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key));
}
inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
- tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
+ epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE>> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/);
for (uint64_t n = 1; n < kdf_rounds; ++n)
crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
- memcpy(&unwrap(key), pwd_hash.data(), sizeof(key));
+ memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key));
}
inline void generate_chacha_key(std::string password, chacha_key& key, uint64_t kdf_rounds) {
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index a2d61b04e..33cc0a25a 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -41,6 +41,7 @@
#include "common/pod-class.h"
#include "common/util.h"
#include "memwipe.h"
+#include "mlocker.h"
#include "generic-ops.h"
#include "hex.h"
#include "span.h"
@@ -65,7 +66,7 @@ namespace crypto {
friend class crypto_ops;
};
- using secret_key = tools::scrubbed<ec_scalar>;
+ using secret_key = epee::mlocked<tools::scrubbed<ec_scalar>>;
POD_CLASS public_keyV {
std::vector<public_key> keys;
@@ -282,6 +283,6 @@ namespace crypto {
}
CRYPTO_MAKE_HASHABLE(public_key)
-CRYPTO_MAKE_HASHABLE(secret_key)
+CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(secret_key)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_COMPARABLE(signature)
diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h
index 62bc758c9..42b98706e 100644
--- a/src/crypto/generic-ops.h
+++ b/src/crypto/generic-ops.h
@@ -33,19 +33,30 @@
#include <cstddef>
#include <cstring>
#include <functional>
+#include <sodium/crypto_verify_32.h>
#define CRYPTO_MAKE_COMPARABLE(type) \
namespace crypto { \
inline bool operator==(const type &_v1, const type &_v2) { \
- return std::memcmp(&_v1, &_v2, sizeof(type)) == 0; \
+ return !memcmp(&_v1, &_v2, sizeof(_v1)); \
} \
inline bool operator!=(const type &_v1, const type &_v2) { \
- return std::memcmp(&_v1, &_v2, sizeof(type)) != 0; \
+ return !operator==(_v1, _v2); \
} \
}
-#define CRYPTO_MAKE_HASHABLE(type) \
-CRYPTO_MAKE_COMPARABLE(type) \
+#define CRYPTO_MAKE_COMPARABLE_CONSTANT_TIME(type) \
+namespace crypto { \
+ inline bool operator==(const type &_v1, const type &_v2) { \
+ static_assert(sizeof(_v1) == 32, "constant time comparison is only implenmted for 32 bytes"); \
+ return crypto_verify_32((const unsigned char*)&_v1, (const unsigned char*)&_v2) == 0; \
+ } \
+ inline bool operator!=(const type &_v1, const type &_v2) { \
+ return !operator==(_v1, _v2); \
+ } \
+}
+
+#define CRYPTO_DEFINE_HASH_FUNCTIONS(type) \
namespace crypto { \
static_assert(sizeof(std::size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \
inline std::size_t hash_value(const type &_v) { \
@@ -60,3 +71,12 @@ namespace std { \
} \
}; \
}
+
+#define CRYPTO_MAKE_HASHABLE(type) \
+CRYPTO_MAKE_COMPARABLE(type) \
+CRYPTO_DEFINE_HASH_FUNCTIONS(type)
+
+#define CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(type) \
+CRYPTO_MAKE_COMPARABLE_CONSTANT_TIME(type) \
+CRYPTO_DEFINE_HASH_FUNCTIONS(type)
+
diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c
index de8e2a5b3..8fcd2138e 100644
--- a/src/crypto/keccak.c
+++ b/src/crypto/keccak.c
@@ -132,3 +132,77 @@ void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md)
{
keccak(in, inlen, md, sizeof(state_t));
}
+
+#define KECCAK_FINALIZED 0x80000000
+#define KECCAK_BLOCKLEN 136
+#define KECCAK_WORDS 17
+#define KECCAK_DIGESTSIZE 32
+#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0)))
+#define KECCAK_PROCESS_BLOCK(st, block) { \
+ for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \
+ ((st))[i_] ^= ((block))[i_]; \
+ }; \
+ keccakf(st, KECCAK_ROUNDS); }
+
+
+void keccak_init(KECCAK_CTX * ctx){
+ memset(ctx, 0, sizeof(KECCAK_CTX));
+}
+
+void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen){
+ if (ctx->rest & KECCAK_FINALIZED) {
+ local_abort("Bad keccak use");
+ }
+
+ const size_t idx = ctx->rest;
+ ctx->rest = (ctx->rest + inlen) % KECCAK_BLOCKLEN;
+
+ // fill partial block
+ if (idx) {
+ size_t left = KECCAK_BLOCKLEN - idx;
+ memcpy((char*)ctx->message + idx, in, (inlen < left ? inlen : left));
+ if (inlen < left) return;
+
+ KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message);
+
+ in += left;
+ inlen -= left;
+ }
+
+ const bool is_aligned = IS_ALIGNED_64(in);
+ while (inlen >= KECCAK_BLOCKLEN) {
+ const uint64_t* aligned_message_block;
+ if (is_aligned) {
+ aligned_message_block = (uint64_t*)in;
+ } else {
+ memcpy(ctx->message, in, KECCAK_BLOCKLEN);
+ aligned_message_block = ctx->message;
+ }
+
+ KECCAK_PROCESS_BLOCK(ctx->hash, aligned_message_block);
+ in += KECCAK_BLOCKLEN;
+ inlen -= KECCAK_BLOCKLEN;
+ }
+ if (inlen) {
+ memcpy(ctx->message, in, inlen);
+ }
+}
+
+void keccak_finish(KECCAK_CTX * ctx, uint8_t *md){
+ if (!(ctx->rest & KECCAK_FINALIZED))
+ {
+ // clear the rest of the data queue
+ memset((char*)ctx->message + ctx->rest, 0, KECCAK_BLOCKLEN - ctx->rest);
+ ((char*)ctx->message)[ctx->rest] |= 0x01;
+ ((char*)ctx->message)[KECCAK_BLOCKLEN - 1] |= 0x80;
+
+ // process final block
+ KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message);
+ ctx->rest = KECCAK_FINALIZED; // mark context as finalized
+ }
+
+ static_assert(KECCAK_BLOCKLEN > KECCAK_DIGESTSIZE, "");
+ if (md) {
+ memcpy(md, ctx->hash, KECCAK_DIGESTSIZE);
+ }
+}
diff --git a/src/crypto/keccak.h b/src/crypto/keccak.h
index fb9d8bd04..9123c7a3b 100644
--- a/src/crypto/keccak.h
+++ b/src/crypto/keccak.h
@@ -15,6 +15,17 @@
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
#endif
+// SHA3 Algorithm context.
+typedef struct KECCAK_CTX
+{
+ // 1600 bits algorithm hashing state
+ uint64_t hash[25];
+ // 1088-bit buffer for leftovers, block size = 136 B for 256-bit keccak
+ uint64_t message[17];
+ // count of bytes in the message[] buffer
+ size_t rest;
+} KECCAK_CTX;
+
// compute a keccak hash (md) of given byte length from "in"
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen);
@@ -23,4 +34,7 @@ void keccakf(uint64_t st[25], int norounds);
void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md);
+void keccak_init(KECCAK_CTX * ctx);
+void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen);
+void keccak_finish(KECCAK_CTX * ctx, uint8_t *md);
#endif
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index aac6ec22b..4cbfa8142 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -44,6 +44,9 @@ extern "C"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "account"
+#define KEYS_ENCRYPTION_SALT 'k'
+
+
using namespace std;
DISABLE_VS_WARNINGS(4244 4345)
@@ -60,7 +63,70 @@ DISABLE_VS_WARNINGS(4244 4345)
m_device = &hwdev;
MCDEBUG("device", "account_keys::set_device device type: "<<typeid(hwdev).name());
}
+ //-----------------------------------------------------------------
+ static void derive_key(const crypto::chacha_key &base_key, crypto::chacha_key &key)
+ {
+ static_assert(sizeof(base_key) == sizeof(crypto::hash), "chacha key and hash should be the same size");
+ epee::mlocked<tools::scrubbed_arr<char, sizeof(base_key)+1>> data;
+ memcpy(data.data(), &base_key, sizeof(base_key));
+ data[sizeof(base_key)] = KEYS_ENCRYPTION_SALT;
+ crypto::generate_chacha_key(data.data(), sizeof(data), key, 1);
+ }
+ //-----------------------------------------------------------------
+ static epee::wipeable_string get_key_stream(const crypto::chacha_key &base_key, const crypto::chacha_iv &iv, size_t bytes)
+ {
+ // derive a new key
+ crypto::chacha_key key;
+ derive_key(base_key, key);
+ // chacha
+ epee::wipeable_string buffer0(std::string(bytes, '\0'));
+ epee::wipeable_string buffer1 = buffer0;
+ crypto::chacha20(buffer0.data(), buffer0.size(), key, iv, buffer1.data());
+ return buffer1;
+ }
+ //-----------------------------------------------------------------
+ void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
+ {
+ // encrypt a large enough byte stream with chacha20
+ epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size()));
+ const char *ptr = key_stream.data();
+ for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
+ m_spend_secret_key.data[i] ^= *ptr++;
+ for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
+ m_view_secret_key.data[i] ^= *ptr++;
+ for (crypto::secret_key &k: m_multisig_keys)
+ {
+ for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
+ k.data[i] ^= *ptr++;
+ }
+ }
+ //-----------------------------------------------------------------
+ void account_keys::encrypt(const crypto::chacha_key &key)
+ {
+ m_encryption_iv = crypto::rand<crypto::chacha_iv>();
+ xor_with_key_stream(key);
+ }
+ //-----------------------------------------------------------------
+ void account_keys::decrypt(const crypto::chacha_key &key)
+ {
+ xor_with_key_stream(key);
+ }
+ //-----------------------------------------------------------------
+ void account_keys::encrypt_viewkey(const crypto::chacha_key &key)
+ {
+ // encrypt a large enough byte stream with chacha20
+ epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
+ const char *ptr = key_stream.data();
+ ptr += sizeof(crypto::secret_key);
+ for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
+ m_view_secret_key.data[i] ^= *ptr++;
+ }
+ //-----------------------------------------------------------------
+ void account_keys::decrypt_viewkey(const crypto::chacha_key &key)
+ {
+ encrypt_viewkey(key);
+ }
//-----------------------------------------------------------------
account_base::account_base()
{
@@ -157,7 +223,7 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey)
{
crypto::secret_key fake;
- memset(&unwrap(fake), 0, sizeof(fake));
+ memset(&unwrap(unwrap(fake)), 0, sizeof(fake));
create_from_keys(address, fake, viewkey);
}
//-----------------------------------------------------------------
diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h
index b5d119c46..dac66ff1a 100644
--- a/src/cryptonote_basic/account.h
+++ b/src/cryptonote_basic/account.h
@@ -44,18 +44,29 @@ namespace cryptonote
crypto::secret_key m_view_secret_key;
std::vector<crypto::secret_key> m_multisig_keys;
hw::device *m_device = &hw::get_device("default");
+ crypto::chacha_iv m_encryption_iv;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_account_address)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_secret_key)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
+ const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
+ KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
END_KV_SERIALIZE_MAP()
account_keys& operator=(account_keys const&) = default;
+ void encrypt(const crypto::chacha_key &key);
+ void decrypt(const crypto::chacha_key &key);
+ void encrypt_viewkey(const crypto::chacha_key &key);
+ void decrypt_viewkey(const crypto::chacha_key &key);
+
hw::device& get_device() const ;
void set_device( hw::device &hwdev) ;
+
+ private:
+ void xor_with_key_stream(const crypto::chacha_key &key);
};
/************************************************************************/
@@ -87,6 +98,11 @@ namespace cryptonote
void forget_spend_key();
const std::vector<crypto::secret_key> &get_multisig_keys() const { return m_keys.m_multisig_keys; }
+ void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); }
+ void decrypt_keys(const crypto::chacha_key &key) { m_keys.decrypt(key); }
+ void encrypt_viewkey(const crypto::chacha_key &key) { m_keys.encrypt_viewkey(key); }
+ void decrypt_viewkey(const crypto::chacha_key &key) { m_keys.decrypt_viewkey(key); }
+
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int /*ver*/)
{
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 87ef47c11..e96dc6bb6 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -156,9 +156,10 @@ static const struct {
//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0),
- m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false),
+ m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false),
m_difficulty_for_next_block_top_hash(crypto::null_hash),
- m_difficulty_for_next_block(1)
+ m_difficulty_for_next_block(1),
+ m_btc_valid(false)
{
LOG_PRINT_L3("Blockchain::" << __func__);
}
@@ -632,6 +633,7 @@ block Blockchain::pop_block_from_blockchain()
update_next_cumulative_size_limit();
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
+ invalidate_block_template_cache();
return popped_block;
}
@@ -642,6 +644,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
CRITICAL_REGION_LOCAL(m_blockchain_lock);
m_timestamps_and_difficulties_height = 0;
m_alternative_chains.clear();
+ invalidate_block_template_cache();
m_db->reset();
m_hardfork->init();
@@ -1212,9 +1215,26 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_size;
uint64_t already_generated_coins;
+ uint64_t pool_cookie;
CRITICAL_REGION_BEGIN(m_blockchain_lock);
height = m_db->height();
+ if (m_btc_valid) {
+ // The pool cookie is atomic. The lack of locking is OK, as if it changes
+ // just as we compare it, we'll just use a slightly old template, but
+ // this would be the case anyway if we'd lock, and the change happened
+ // just after the block template was created
+ if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce && m_btc_pool_cookie == m_tx_pool.cookie()) {
+ MDEBUG("Using cached template");
+ m_btc.timestamp = time(NULL); // update timestamp unconditionally
+ b = m_btc;
+ diffic = m_btc_difficulty;
+ expected_reward = m_btc_expected_reward;
+ return true;
+ }
+ MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()));
+ invalidate_block_template_cache();
+ }
b.major_version = m_hardfork->get_current_version();
b.minor_version = m_hardfork->get_ideal_version();
@@ -1241,6 +1261,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
{
return false;
}
+ pool_cookie = m_tx_pool.cookie();
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0;
uint64_t real_fee = 0;
@@ -1355,6 +1376,8 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size <<
", cumulative size " << cumulative_size << " is now good");
#endif
+
+ cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie);
return true;
}
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
@@ -2294,7 +2317,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
total_height = get_current_blockchain_height();
size_t count = 0, size = 0;
blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height)));
- for(size_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++)
+ for(uint64_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++)
{
blocks.resize(blocks.size()+1);
blocks.back().first.first = m_db->get_block_blob_from_height(i);
@@ -3697,6 +3720,7 @@ leave:
// appears to be a NOP *and* is called elsewhere. wat?
m_tx_pool.on_blockchain_inc(new_height, id);
get_difficulty_for_next_block(); // just to cache it
+ invalidate_block_template_cache();
return true;
}
@@ -3877,11 +3901,13 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
store_blockchain();
m_sync_counter = 0;
}
- else if (m_db_blocks_per_sync && m_sync_counter >= m_db_blocks_per_sync)
+ else if (m_db_sync_threshold && ((m_db_sync_on_blocks && m_sync_counter >= m_db_sync_threshold) || (!m_db_sync_on_blocks && m_bytes_to_sync >= m_db_sync_threshold)))
{
+ MDEBUG("Sync threshold met, syncing");
if(m_db_sync_mode == db_async)
{
m_sync_counter = 0;
+ m_bytes_to_sync = 0;
m_async_service.dispatch(boost::bind(&Blockchain::store_blockchain, this));
}
else if(m_db_sync_mode == db_sync)
@@ -4073,6 +4099,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
}
total_txs += entry.txs.size();
}
+ m_bytes_to_sync += bytes;
while (!(stop_batch = m_db->batch_start(blocks_entry.size(), bytes))) {
m_blockchain_lock.unlock();
m_tx_pool.unlock();
@@ -4423,7 +4450,7 @@ bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, con
return m_db->for_all_txpool_txes(f, include_blob, include_unrelayed_txes);
}
-void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, blockchain_db_sync_mode sync_mode, bool fast_sync)
+void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold, blockchain_db_sync_mode sync_mode, bool fast_sync)
{
if (sync_mode == db_defaultsync)
{
@@ -4432,7 +4459,8 @@ void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync,
}
m_db_sync_mode = sync_mode;
m_fast_sync = fast_sync;
- m_db_blocks_per_sync = blocks_per_sync;
+ m_db_sync_on_blocks = sync_on_blocks;
+ m_db_sync_threshold = sync_threshold;
m_max_prepare_blocks_threads = maxthreads;
}
@@ -4666,6 +4694,24 @@ bool Blockchain::for_all_outputs(uint64_t amount, std::function<bool(uint64_t he
return m_db->for_all_outputs(amount, f);;
}
+void Blockchain::invalidate_block_template_cache()
+{
+ MDEBUG("Invalidating block template cache");
+ m_btc_valid = false;
+}
+
+void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie)
+{
+ MDEBUG("Setting block template cache");
+ m_btc = b;
+ m_btc_address = address;
+ m_btc_nonce = nonce;
+ m_btc_difficulty = diff;
+ m_btc_expected_reward = expected_reward;
+ m_btc_pool_cookie = pool_cookie;
+ m_btc_valid = true;
+}
+
namespace cryptonote {
template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&) const;
template bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>&, std::vector<cryptonote::blobdata>&, std::vector<crypto::hash>&, bool) const;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index d95c8ed15..2292ffbf3 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -729,11 +729,12 @@ namespace cryptonote
* @brief sets various performance options
*
* @param maxthreads max number of threads when preparing blocks for addition
- * @param blocks_per_sync number of blocks to cache before syncing to database
+ * @param sync_on_blocks whether to sync based on blocks or bytes
+ * @param sync_threshold number of blocks/bytes to cache before syncing to database
* @param sync_mode the ::blockchain_db_sync_mode to use
* @param fast_sync sync using built-in block hashes as trusted
*/
- void set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync,
+ void set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold,
blockchain_db_sync_mode sync_mode, bool fast_sync);
/**
@@ -1017,11 +1018,13 @@ namespace cryptonote
bool m_fast_sync;
bool m_show_time_stats;
bool m_db_default_sync;
- uint64_t m_db_blocks_per_sync;
+ bool m_db_sync_on_blocks;
+ uint64_t m_db_sync_threshold;
uint64_t m_max_prepare_blocks_threads;
uint64_t m_fake_pow_calc_time;
uint64_t m_fake_scan_time;
uint64_t m_sync_counter;
+ uint64_t m_bytes_to_sync;
std::vector<uint64_t> m_timestamps;
std::vector<difficulty_type> m_difficulties;
uint64_t m_timestamps_and_difficulties_height;
@@ -1052,6 +1055,15 @@ namespace cryptonote
std::atomic<bool> m_cancel;
+ // block template cache
+ block m_btc;
+ account_public_address m_btc_address;
+ blobdata m_btc_nonce;
+ difficulty_type m_btc_difficulty;
+ uint64_t m_btc_pool_cookie;
+ uint64_t m_btc_expected_reward;
+ bool m_btc_valid;
+
/**
* @brief collects the keys for all outputs being "spent" as an input
*
@@ -1407,5 +1419,17 @@ namespace cryptonote
* that implicit data.
*/
bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys);
+
+ /**
+ * @brief invalidates any cached block template
+ */
+ void invalidate_block_template_cache();
+
+ /**
+ * @brief stores a new cached block template
+ *
+ * At some point, may be used to push an update to miners
+ */
+ void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie);
};
} // namespace cryptonote
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 18490c65e..d0db38799 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -439,9 +439,10 @@ namespace cryptonote
MGINFO("Loading blockchain from folder " << folder.string() << " ...");
const std::string filename = folder.string();
- // default to fast:async:1
+ // default to fast:async:1 if overridden
blockchain_db_sync_mode sync_mode = db_defaultsync;
- uint64_t blocks_per_sync = 1;
+ bool sync_on_blocks = true;
+ uint64_t sync_threshold = 1;
if (m_nettype == FAKECHAIN)
{
@@ -491,7 +492,7 @@ namespace cryptonote
else if(options[0] == "fastest")
{
db_flags = DBF_FASTEST;
- blocks_per_sync = 1000; // default to fastest:async:1000
+ sync_threshold = 1000; // default to fastest:async:1000
sync_mode = db_sync_mode_is_default ? db_defaultsync : db_async;
}
else
@@ -509,9 +510,22 @@ namespace cryptonote
if(options.size() >= 3 && !safemode)
{
char *endptr;
- uint64_t bps = strtoull(options[2].c_str(), &endptr, 0);
- if (*endptr == '\0')
- blocks_per_sync = bps;
+ uint64_t threshold = strtoull(options[2].c_str(), &endptr, 0);
+ if (*endptr == '\0' || !strcmp(endptr, "blocks"))
+ {
+ sync_on_blocks = true;
+ sync_threshold = threshold;
+ }
+ else if (!strcmp(endptr, "bytes"))
+ {
+ sync_on_blocks = false;
+ sync_threshold = threshold;
+ }
+ else
+ {
+ LOG_ERROR("Invalid db sync mode: " << options[2]);
+ return false;
+ }
}
if (db_salvage)
@@ -528,7 +542,7 @@ namespace cryptonote
}
m_blockchain_storage.set_user_options(blocks_threads,
- blocks_per_sync, sync_mode, fast_sync);
+ sync_on_blocks, sync_threshold, sync_mode, fast_sync);
const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)};
const cryptonote::test_options regtest_test_options = {
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 071ce591e..1581f3088 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -516,6 +516,7 @@ namespace cryptonote
uint64_t amount_in = 0, amount_out = 0;
rct::ctkeyV inSk;
+ inSk.reserve(sources.size());
// mixRing indexing is done the other way round for simple
rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs);
rct::keyV destinations;
@@ -532,6 +533,7 @@ namespace cryptonote
ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
ctkey.mask = sources[i].mask;
inSk.push_back(ctkey);
+ memwipe(&ctkey, sizeof(rct::ctkey));
// inPk: (public key, commitment)
// will be done when filling in mixRing
if (msout)
@@ -590,6 +592,7 @@ namespace cryptonote
tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof, hwdev);
else
tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof, hwdev); // same index assumption
+ memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey));
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 3b8dd8107..41c58fcb6 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -102,7 +102,7 @@ namespace cryptonote
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
- tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0)
+ tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0), m_cookie(0)
{
}
@@ -306,6 +306,8 @@ namespace cryptonote
tvc.m_verifivation_failed = false;
m_txpool_size += blob_size;
+ ++m_cookie;
+
MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size));
prune(m_txpool_max_size);
@@ -341,6 +343,7 @@ namespace cryptonote
bytes = m_txpool_max_size;
CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain);
+ bool changed = false;
// this will never remove the first one, but we don't care
auto it = --m_txs_by_fee_and_receive_time.end();
@@ -377,6 +380,7 @@ namespace cryptonote
remove_transaction_keyimages(tx);
MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first);
m_txs_by_fee_and_receive_time.erase(it--);
+ changed = true;
}
catch (const std::exception &e)
{
@@ -384,6 +388,8 @@ namespace cryptonote
return;
}
}
+ if (changed)
+ ++m_cookie;
if (m_txpool_size > bytes)
MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes);
}
@@ -401,6 +407,7 @@ namespace cryptonote
auto ins_res = kei_image_set.insert(id);
CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set");
}
+ ++m_cookie;
return true;
}
//---------------------------------------------------------------------------------
@@ -435,6 +442,7 @@ namespace cryptonote
}
}
+ ++m_cookie;
return true;
}
//---------------------------------------------------------------------------------
@@ -480,6 +488,7 @@ namespace cryptonote
}
m_txs_by_fee_and_receive_time.erase(sorted_it);
+ ++m_cookie;
return true;
}
//---------------------------------------------------------------------------------
@@ -553,6 +562,7 @@ namespace cryptonote
// ignore error
}
}
+ ++m_cookie;
}
return true;
}
@@ -1051,6 +1061,7 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
+ bool changed = false;
LockedTXN lock(m_blockchain);
for(size_t i = 0; i!= tx.vin.size(); i++)
{
@@ -1071,6 +1082,7 @@ namespace cryptonote
{
MDEBUG("Marking " << txid << " as double spending " << itk.k_image);
meta.double_spend_seen = true;
+ changed = true;
try
{
m_blockchain.update_txpool_tx(txid, meta);
@@ -1084,6 +1096,8 @@ namespace cryptonote
}
}
}
+ if (changed)
+ ++m_cookie;
}
//---------------------------------------------------------------------------------
std::string tx_memory_pool::print_pool(bool short_format) const
@@ -1299,6 +1313,8 @@ namespace cryptonote
}
}
}
+ if (n_removed > 0)
+ ++m_cookie;
return n_removed;
}
//---------------------------------------------------------------------------------
@@ -1355,6 +1371,10 @@ namespace cryptonote
}
}
}
+
+ m_cookie = 0;
+
+ // Ignore deserialization error
return true;
}
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 4ade7ddbe..4abfef85c 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -362,6 +362,13 @@ namespace cryptonote
*/
size_t validate(uint8_t version);
+ /**
+ * @brief return the cookie
+ *
+ * @return the cookie
+ */
+ uint64_t cookie() const { return m_cookie; }
+
/**
* @brief get the cumulative txpool size in bytes
*
@@ -549,6 +556,8 @@ private:
//!< container for transactions organized by fee per size and receive time
sorted_tx_container m_txs_by_fee_and_receive_time;
+ std::atomic<uint64_t> m_cookie; //!< incremented at each change
+
/**
* @brief get an iterator to a transaction in the sorted container
*
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index c39d67ceb..05f4189fb 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -57,7 +57,11 @@ void block_queue::add_blocks(uint64_t height, std::vector<cryptonote::block_comp
bool has_hashes = remove_span(height, &hashes);
blocks.insert(span(height, std::move(bcel), connection_id, rate, size));
if (has_hashes)
+ {
+ for (const crypto::hash &h: hashes)
+ requested_hashes.insert(h);
set_span_hashes(height, connection_id, hashes);
+ }
}
void block_queue::add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time)
@@ -76,11 +80,19 @@ void block_queue::flush_spans(const boost::uuids::uuid &connection_id, bool all)
block_map::iterator j = i++;
if (j->connection_id == connection_id && (all || j->blocks.size() == 0))
{
- blocks.erase(j);
+ erase_block(j);
}
}
}
+void block_queue::erase_block(block_map::iterator j)
+{
+ CHECK_AND_ASSERT_THROW_MES(j != blocks.end(), "Invalid iterator");
+ for (const crypto::hash &h: j->hashes)
+ requested_hashes.erase(h);
+ blocks.erase(j);
+}
+
void block_queue::flush_stale_spans(const std::set<boost::uuids::uuid> &live_connections)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
@@ -92,7 +104,7 @@ void block_queue::flush_stale_spans(const std::set<boost::uuids::uuid> &live_con
block_map::iterator j = i++;
if (live_connections.find(j->connection_id) == live_connections.end() && j->blocks.size() == 0)
{
- blocks.erase(j);
+ erase_block(j);
}
}
}
@@ -106,7 +118,7 @@ bool block_queue::remove_span(uint64_t start_block_height, std::vector<crypto::h
{
if (hashes)
*hashes = std::move(i->hashes);
- blocks.erase(i);
+ erase_block(i);
return true;
}
}
@@ -121,7 +133,7 @@ void block_queue::remove_spans(const boost::uuids::uuid &connection_id, uint64_t
block_map::iterator j = i++;
if (j->connection_id == connection_id && j->start_block_height <= start_block_height)
{
- blocks.erase(j);
+ erase_block(j);
}
}
}
@@ -160,16 +172,15 @@ std::string block_queue::get_overview() const
return s;
}
+inline bool block_queue::requested_internal(const crypto::hash &hash) const
+{
+ return requested_hashes.find(hash) != requested_hashes.end();
+}
+
bool block_queue::requested(const crypto::hash &hash) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
- for (const auto &span: blocks)
- {
- for (const auto &h: span.hashes)
- if (h == hash)
- return true;
- }
- return false;
+ return requested_internal(hash);
}
std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::vector<crypto::hash> &block_hashes, boost::posix_time::ptime time)
@@ -184,7 +195,7 @@ std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_hei
uint64_t span_start_height = last_block_height - block_hashes.size() + 1;
std::vector<crypto::hash>::const_iterator i = block_hashes.begin();
- while (i != block_hashes.end() && requested(*i))
+ while (i != block_hashes.end() && requested_internal(*i))
{
++i;
++span_start_height;
@@ -256,8 +267,10 @@ void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uui
if (i->start_block_height == start_height && i->connection_id == connection_id)
{
span s = *i;
- blocks.erase(i);
+ erase_block(i);
s.hashes = std::move(hashes);
+ for (const crypto::hash &h: s.hashes)
+ requested_hashes.insert(h);
blocks.insert(s);
return;
}
diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h
index 9059e89ac..9cce95075 100644
--- a/src/cryptonote_protocol/block_queue.h
+++ b/src/cryptonote_protocol/block_queue.h
@@ -33,6 +33,7 @@
#include <string>
#include <vector>
#include <set>
+#include <unordered_set>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/uuid/uuid.hpp>
@@ -93,7 +94,12 @@ namespace cryptonote
bool requested(const crypto::hash &hash) const;
private:
+ void erase_block(block_map::iterator j);
+ inline bool requested_internal(const crypto::hash &hash) const;
+
+ private:
block_map blocks;
mutable boost::recursive_mutex mutex;
+ std::unordered_set<crypto::hash> requested_hashes;
};
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index a931d3b57..c2c660e8c 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -1683,11 +1683,6 @@ skip:
fluffy_arg.b = arg.b;
fluffy_arg.b.txs = fluffy_txs;
- // pre-serialize them
- std::string fullBlob, fluffyBlob;
- epee::serialization::store_t_to_binary(arg, fullBlob);
- epee::serialization::store_t_to_binary(fluffy_arg, fluffyBlob);
-
// sort peers between fluffy ones and others
std::list<boost::uuids::uuid> fullConnections, fluffyConnections;
m_p2p->for_each_connection([this, &exclude_context, &fullConnections, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)
@@ -1709,8 +1704,18 @@ skip:
});
// send fluffy ones first, we want to encourage people to run that
- m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, fluffyBlob, fluffyConnections);
- m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, fullBlob, fullConnections);
+ if (!fluffyConnections.empty())
+ {
+ std::string fluffyBlob;
+ epee::serialization::store_t_to_binary(fluffy_arg, fluffyBlob);
+ m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, fluffyBlob, fluffyConnections);
+ }
+ if (!fullConnections.empty())
+ {
+ std::string fullBlob;
+ epee::serialization::store_t_to_binary(arg, fullBlob);
+ m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, fullBlob, fullConnections);
+ }
return true;
}
diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp
index bf14813ea..a4f40e041 100644
--- a/src/device/device_default.cpp
+++ b/src/device/device_default.cpp
@@ -103,7 +103,7 @@ namespace hw {
bool device_default::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) {
const crypto::secret_key &view_key = keys.m_view_secret_key;
const crypto::secret_key &spend_key = keys.m_spend_secret_key;
- tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1> data;
+ epee::mlocked<tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1>> data;
memcpy(data.data(), &view_key, sizeof(view_key));
memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key));
data[sizeof(data) - 1] = CHACHA8_KEY_TAIL;
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index 7a34dad5e..c4e9e40b7 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -136,7 +136,8 @@ namespace hw {
}
bool operator==(const crypto::key_derivation &d0, const crypto::key_derivation &d1) {
- return !memcmp(&d0, &d1, sizeof(d0));
+ static_assert(sizeof(crypto::key_derivation) == 32, "key_derivation must be 32 bytes");
+ return !crypto_verify_32((const unsigned char*)&d0, (const unsigned char*)&d1);
}
/* ===================================================================== */
diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp
index 03e0a7946..e680a8157 100644
--- a/src/gen_multisig/gen_multisig.cpp
+++ b/src/gen_multisig/gen_multisig.cpp
@@ -92,7 +92,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
{
std::string name = basename + "-" + std::to_string(n + 1);
wallets[n].reset(new tools::wallet2(nettype));
- wallets[n]->init("");
+ wallets[n]->init(false, "");
wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false, create_address_file);
}
@@ -101,11 +101,13 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
std::vector<crypto::public_key> pk(total);
for (size_t n = 0; n < total; ++n)
{
+ wallets[n]->decrypt_keys(pwd_container->password());
if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n]))
{
tools::fail_msg_writer() << tr("Failed to verify multisig info");
return false;
}
+ wallets[n]->encrypt_keys(pwd_container->password());
}
// make the wallets multisig
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index 19a9c26bb..3d6338856 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -43,8 +43,11 @@
#include <vector>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
+#include "wipeable_string.h"
+#include "misc_language.h"
#include "crypto/crypto.h" // for declaration of crypto::secret_key
#include <fstream>
+#include "common/int-util.h"
#include "mnemonics/electrum-words.h"
#include <stdexcept>
#include <boost/filesystem.hpp>
@@ -80,9 +83,9 @@ namespace crypto
namespace
{
- uint32_t create_checksum_index(const std::vector<std::string> &word_list,
+ uint32_t create_checksum_index(const std::vector<epee::wipeable_string> &word_list,
uint32_t unique_prefix_length);
- bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length);
+ bool checksum_test(std::vector<epee::wipeable_string> seed, uint32_t unique_prefix_length);
/*!
* \brief Finds the word list that contains the seed words and puts the indices
@@ -93,7 +96,7 @@ namespace
* \param language Language instance pointer to write to after it is found.
* \return true if all the words were present in some language false if not.
*/
- bool find_seed_language(const std::vector<std::string> &seed,
+ bool find_seed_language(const std::vector<epee::wipeable_string> &seed,
bool has_checksum, std::vector<uint32_t> &matched_indices, Language::Base **language)
{
// If there's a new language added, add an instance of it here.
@@ -114,17 +117,19 @@ namespace
});
Language::Base *fallback = NULL;
+ std::vector<epee::wipeable_string>::const_iterator it2;
+ matched_indices.reserve(seed.size());
+
// Iterate through all the languages and find a match
for (std::vector<Language::Base*>::iterator it1 = language_instances.begin();
it1 != language_instances.end(); it1++)
{
- const std::unordered_map<std::string, uint32_t> &word_map = (*it1)->get_word_map();
- const std::unordered_map<std::string, uint32_t> &trimmed_word_map = (*it1)->get_trimmed_word_map();
+ const std::unordered_map<epee::wipeable_string, uint32_t> &word_map = (*it1)->get_word_map();
+ const std::unordered_map<epee::wipeable_string, uint32_t> &trimmed_word_map = (*it1)->get_trimmed_word_map();
// To iterate through seed words
- std::vector<std::string>::const_iterator it2;
bool full_match = true;
- std::string trimmed_word;
+ epee::wipeable_string trimmed_word;
// Iterate through all the words and see if they're all present
for (it2 = seed.begin(); it2 != seed.end(); it2++)
{
@@ -167,6 +172,7 @@ namespace
return true;
}
// Some didn't match. Clear the index array.
+ memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));
matched_indices.clear();
}
@@ -181,6 +187,7 @@ namespace
}
MINFO("No match found");
+ memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));
return false;
}
@@ -190,12 +197,12 @@ namespace
* \param unique_prefix_length the prefix length of each word to use for checksum
* \return Checksum index
*/
- uint32_t create_checksum_index(const std::vector<std::string> &word_list,
+ uint32_t create_checksum_index(const std::vector<epee::wipeable_string> &word_list,
uint32_t unique_prefix_length)
{
- std::string trimmed_words = "";
+ epee::wipeable_string trimmed_words = "";
- for (std::vector<std::string>::const_iterator it = word_list.begin(); it != word_list.end(); it++)
+ for (std::vector<epee::wipeable_string>::const_iterator it = word_list.begin(); it != word_list.end(); it++)
{
if (it->length() > unique_prefix_length)
{
@@ -217,22 +224,22 @@ namespace
* \param unique_prefix_length the prefix length of each word to use for checksum
* \return True if the test passed false if not.
*/
- bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length)
+ bool checksum_test(std::vector<epee::wipeable_string> seed, uint32_t unique_prefix_length)
{
if (seed.empty())
return false;
// The last word is the checksum.
- std::string last_word = seed.back();
+ epee::wipeable_string last_word = seed.back();
seed.pop_back();
- std::string checksum = seed[create_checksum_index(seed, unique_prefix_length)];
+ epee::wipeable_string checksum = seed[create_checksum_index(seed, unique_prefix_length)];
- std::string trimmed_checksum = checksum.length() > unique_prefix_length ? Language::utf8prefix(checksum, unique_prefix_length) :
+ epee::wipeable_string trimmed_checksum = checksum.length() > unique_prefix_length ? Language::utf8prefix(checksum, unique_prefix_length) :
checksum;
- std::string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) :
+ epee::wipeable_string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) :
last_word;
bool ret = trimmed_checksum == trimmed_last_word;
- MINFO("Checksum is %s" << (ret ? "valid" : "invalid"));
+ MINFO("Checksum is " << (ret ? "valid" : "invalid"));
return ret;
}
}
@@ -260,13 +267,12 @@ namespace crypto
* \param language_name Language of the seed as found gets written here.
* \return false if not a multiple of 3 words, or if word is not in the words list
*/
- bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate,
+ bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string& dst, size_t len, bool duplicate,
std::string &language_name)
{
- std::vector<std::string> seed;
+ std::vector<epee::wipeable_string> seed;
- boost::algorithm::trim(words);
- boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on);
+ words.split(seed);
if (len % 4)
{
@@ -291,6 +297,7 @@ namespace crypto
}
std::vector<uint32_t> matched_indices;
+ auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));});
Language::Base *language;
if (!find_seed_language(seed, has_checksum, matched_indices, &language))
{
@@ -313,33 +320,33 @@ namespace crypto
for (unsigned int i=0; i < seed.size() / 3; i++)
{
- uint32_t val;
- uint32_t w1, w2, w3;
- w1 = matched_indices[i*3];
- w2 = matched_indices[i*3 + 1];
- w3 = matched_indices[i*3 + 2];
+ uint32_t w[4];
+ w[1] = matched_indices[i*3];
+ w[2] = matched_indices[i*3 + 1];
+ w[3] = matched_indices[i*3 + 2];
- val = w1 + word_list_length * (((word_list_length - w1) + w2) % word_list_length) +
- word_list_length * word_list_length * (((word_list_length - w2) + w3) % word_list_length);
+ w[0]= w[1] + word_list_length * (((word_list_length - w[1]) + w[2]) % word_list_length) +
+ word_list_length * word_list_length * (((word_list_length - w[2]) + w[3]) % word_list_length);
- if (!(val % word_list_length == w1))
+ if (!(w[0]% word_list_length == w[1]))
{
+ memwipe(w, sizeof(w));
MERROR("Invalid seed: mumble mumble");
return false;
}
- dst.append((const char*)&val, 4); // copy 4 bytes to position
+ dst.append((const char*)&w[0], 4); // copy 4 bytes to position
+ memwipe(w, sizeof(w));
}
if (len > 0 && duplicate)
{
const size_t expected = len * 3 / 32;
- std::string wlist_copy = words;
if (seed.size() == expected/2)
{
- dst.append(dst); // if electrum 12-word seed, duplicate
- wlist_copy += ' ';
- wlist_copy += words;
+ dst += ' '; // if electrum 12-word seed, duplicate
+ dst += dst; // if electrum 12-word seed, duplicate
+ dst.pop_back(); // trailing space
}
}
@@ -353,10 +360,10 @@ namespace crypto
* \param language_name Language of the seed as found gets written here.
* \return false if not a multiple of 3 words, or if word is not in the words list
*/
- bool words_to_bytes(std::string words, crypto::secret_key& dst,
+ bool words_to_bytes(const epee::wipeable_string &words, crypto::secret_key& dst,
std::string &language_name)
{
- std::string s;
+ epee::wipeable_string s;
if (!words_to_bytes(words, s, sizeof(dst), true, language_name))
{
MERROR("Invalid seed: failed to convert words to bytes");
@@ -378,7 +385,7 @@ namespace crypto
* \param language_name Seed language name
* \return true if successful false if not. Unsuccessful if wrong key size.
*/
- bool bytes_to_words(const char *src, size_t len, std::string& words,
+ bool bytes_to_words(const char *src, size_t len, epee::wipeable_string& words,
const std::string &language_name)
{
@@ -397,39 +404,38 @@ namespace crypto
}
const std::vector<std::string> &word_list = language->get_word_list();
// To store the words for random access to add the checksum word later.
- std::vector<std::string> words_store;
+ std::vector<epee::wipeable_string> words_store;
uint32_t word_list_length = word_list.size();
// 4 bytes -> 3 words. 8 digits base 16 -> 3 digits base 1626
- for (unsigned int i=0; i < len/4; i++, words += ' ')
+ for (unsigned int i=0; i < len/4; i++, words.push_back(' '))
{
- uint32_t w1, w2, w3;
-
- uint32_t val;
+ uint32_t w[4];
- memcpy(&val, src + (i * 4), 4);
+ w[0] = SWAP32LE(*(const uint32_t*)(src + (i * 4)));
- w1 = val % word_list_length;
- w2 = ((val / word_list_length) + w1) % word_list_length;
- w3 = (((val / word_list_length) / word_list_length) + w2) % word_list_length;
+ w[1] = w[0] % word_list_length;
+ w[2] = ((w[0] / word_list_length) + w[1]) % word_list_length;
+ w[3] = (((w[0] / word_list_length) / word_list_length) + w[2]) % word_list_length;
- words += word_list[w1];
+ words += word_list[w[1]];
words += ' ';
- words += word_list[w2];
+ words += word_list[w[2]];
words += ' ';
- words += word_list[w3];
+ words += word_list[w[3]];
+
+ words_store.push_back(word_list[w[1]]);
+ words_store.push_back(word_list[w[2]]);
+ words_store.push_back(word_list[w[3]]);
- words_store.push_back(word_list[w1]);
- words_store.push_back(word_list[w2]);
- words_store.push_back(word_list[w3]);
+ memwipe(w, sizeof(w));
}
- words.pop_back();
- words += (' ' + words_store[create_checksum_index(words_store, language->get_unique_prefix_length())]);
+ words += words_store[create_checksum_index(words_store, language->get_unique_prefix_length())];
return true;
}
- bool bytes_to_words(const crypto::secret_key& src, std::string& words,
+ bool bytes_to_words(const crypto::secret_key& src, epee::wipeable_string& words,
const std::string &language_name)
{
return bytes_to_words(src.data, sizeof(src), words, language_name);
@@ -473,11 +479,10 @@ namespace crypto
* \param seed The seed to check (a space delimited concatenated word list)
* \return true if the seed passed is a old style seed false if not.
*/
- bool get_is_old_style_seed(std::string seed)
+ bool get_is_old_style_seed(const epee::wipeable_string &seed)
{
- std::vector<std::string> word_list;
- boost::algorithm::trim(seed);
- boost::split(word_list, seed, boost::is_any_of(" "), boost::token_compress_on);
+ std::vector<epee::wipeable_string> word_list;
+ seed.split(word_list);
return word_list.size() != (seed_length + 1);
}
diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h
index 856edb92a..5401b9779 100644
--- a/src/mnemonics/electrum-words.h
+++ b/src/mnemonics/electrum-words.h
@@ -44,6 +44,8 @@
#include <map>
#include "crypto/crypto.h" // for declaration of crypto::secret_key
+namespace epee { class wipeable_string; }
+
/*!
* \namespace crypto
*
@@ -70,7 +72,7 @@ namespace crypto
* \param language_name Language of the seed as found gets written here.
* \return false if not a multiple of 3 words, or if word is not in the words list
*/
- bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate,
+ bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string& dst, size_t len, bool duplicate,
std::string &language_name);
/*!
* \brief Converts seed words to bytes (secret key).
@@ -79,7 +81,7 @@ namespace crypto
* \param language_name Language of the seed as found gets written here.
* \return false if not a multiple of 3 words, or if word is not in the words list
*/
- bool words_to_bytes(std::string words, crypto::secret_key& dst,
+ bool words_to_bytes(const epee::wipeable_string &words, crypto::secret_key& dst,
std::string &language_name);
/*!
@@ -90,7 +92,7 @@ namespace crypto
* \param language_name Seed language name
* \return true if successful false if not. Unsuccessful if wrong key size.
*/
- bool bytes_to_words(const char *src, size_t len, std::string& words,
+ bool bytes_to_words(const char *src, size_t len, epee::wipeable_string& words,
const std::string &language_name);
/*!
@@ -100,7 +102,7 @@ namespace crypto
* \param language_name Seed language name
* \return true if successful false if not. Unsuccessful if wrong key size.
*/
- bool bytes_to_words(const crypto::secret_key& src, std::string& words,
+ bool bytes_to_words(const crypto::secret_key& src, epee::wipeable_string& words,
const std::string &language_name);
/*!
@@ -115,7 +117,7 @@ namespace crypto
* \param seed The seed to check (a space delimited concatenated word list)
* \return true if the seed passed is a old style seed false if not.
*/
- bool get_is_old_style_seed(std::string seed);
+ bool get_is_old_style_seed(const epee::wipeable_string &seed);
/*!
* \brief Returns the name of a language in English
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h
index 2b0c37c6b..cf518ab2a 100644
--- a/src/mnemonics/language_base.h
+++ b/src/mnemonics/language_base.h
@@ -53,15 +53,20 @@ namespace Language
* \param count How many characters to return.
* \return A string consisting of the first count characters in s.
*/
- inline std::string utf8prefix(const std::string &s, size_t count)
+ template<typename T>
+ inline T utf8prefix(const T &s, size_t count)
{
- std::string prefix = "";
- const char *ptr = s.c_str();
- while (count-- && *ptr)
+ T prefix = "";
+ size_t avail = s.size();
+ const char *ptr = s.data();
+ while (count-- && avail--)
{
prefix += *ptr++;
- while (((*ptr) & 0xc0) == 0x80)
+ while (avail && ((*ptr) & 0xc0) == 0x80)
+ {
prefix += *ptr++;
+ --avail;
+ }
}
return prefix;
}
@@ -79,8 +84,8 @@ namespace Language
ALLOW_DUPLICATE_PREFIXES = 1<<1,
};
const std::vector<std::string> word_list; /*!< A pointer to the array of words */
- std::unordered_map<std::string, uint32_t> word_map; /*!< hash table to find word's index */
- std::unordered_map<std::string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
+ std::unordered_map<epee::wipeable_string, uint32_t> word_map; /*!< hash table to find word's index */
+ std::unordered_map<epee::wipeable_string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
std::string language_name; /*!< Name of language */
std::string english_language_name; /*!< Name of language */
uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */
@@ -103,7 +108,7 @@ namespace Language
else
throw std::runtime_error("Too short word in " + language_name + " word list: " + *it);
}
- std::string trimmed;
+ epee::wipeable_string trimmed;
if (it->length() > unique_prefix_length)
{
trimmed = utf8prefix(*it, unique_prefix_length);
@@ -115,9 +120,9 @@ namespace Language
if (trimmed_word_map.find(trimmed) != trimmed_word_map.end())
{
if (flags & ALLOW_DUPLICATE_PREFIXES)
- MWARNING("Duplicate prefix in " << language_name << " word list: " << trimmed);
+ MWARNING("Duplicate prefix in " << language_name << " word list: " << std::string(trimmed.data(), trimmed.size()));
else
- throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + trimmed);
+ throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + std::string(trimmed.data(), trimmed.size()));
}
trimmed_word_map[trimmed] = ii;
}
@@ -145,7 +150,7 @@ namespace Language
* \brief Returns a pointer to the word map.
* \return A pointer to the word map.
*/
- const std::unordered_map<std::string, uint32_t>& get_word_map() const
+ const std::unordered_map<epee::wipeable_string, uint32_t>& get_word_map() const
{
return word_map;
}
@@ -153,7 +158,7 @@ namespace Language
* \brief Returns a pointer to the trimmed word map.
* \return A pointer to the trimmed word map.
*/
- const std::unordered_map<std::string, uint32_t>& get_trimmed_word_map() const
+ const std::unordered_map<epee::wipeable_string, uint32_t>& get_trimmed_word_map() const
{
return trimmed_word_map;
}
diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp
index d85c47772..a0a788b7d 100644
--- a/src/multisig/multisig.cpp
+++ b/src/multisig/multisig.cpp
@@ -47,9 +47,12 @@ namespace cryptonote
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key)
{
rct::keyV data;
+ data.reserve(2);
data.push_back(rct::sk2rct(key));
data.push_back(multisig_salt);
- return rct::rct2sk(rct::hash_to_scalar(data));
+ crypto::secret_key result = rct::rct2sk(rct::hash_to_scalar(data));
+ memwipe(&data[0], sizeof(rct::key));
+ return result;
}
//-----------------------------------------------------------------
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index f74216ed4..cc966c44b 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -492,7 +492,9 @@ namespace rct {
for (size_t j = 0; j < outPk.size(); j++) {
sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row..
}
- return MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev);
+ mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev);
+ memwipe(sk.data(), sk.size() * sizeof(key));
+ return result;
}
@@ -521,7 +523,9 @@ namespace rct {
M[i][0] = pubs[i].dest;
subKeys(M[i][1], pubs[i].mask, Cout);
}
- return MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev);
+ mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev);
+ memwipe(&sk[0], sizeof(key));
+ return result;
}
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index 844291d0c..452a68eb2 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -36,6 +36,7 @@
#include <vector>
#include <iostream>
#include <cinttypes>
+#include <sodium/crypto_verify_32.h>
extern "C" {
#include "crypto/crypto-ops.h"
@@ -81,7 +82,7 @@ namespace rct {
unsigned char operator[](int i) const {
return bytes[i];
}
- bool operator==(const key &k) const { return !memcmp(bytes, k.bytes, sizeof(bytes)); }
+ bool operator==(const key &k) const { return !crypto_verify_32(bytes, k.bytes); }
unsigned char bytes[32];
};
typedef std::vector<key> keyV; //vector of keys
@@ -403,6 +404,16 @@ namespace rct {
};
struct rctSig: public rctSigBase {
rctSigPrunable p;
+
+ keyV& get_pseudo_outs()
+ {
+ return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts;
+ }
+
+ keyV const& get_pseudo_outs() const
+ {
+ return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts;
+ }
};
//other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint
@@ -514,16 +525,16 @@ namespace rct {
static inline const crypto::secret_key rct2sk(const rct::key &k) { return (const crypto::secret_key&)k; }
static inline const crypto::key_image rct2ki(const rct::key &k) { return (const crypto::key_image&)k; }
static inline const crypto::hash rct2hash(const rct::key &k) { return (const crypto::hash&)k; }
- static inline bool operator==(const rct::key &k0, const crypto::public_key &k1) { return !memcmp(&k0, &k1, 32); }
- static inline bool operator!=(const rct::key &k0, const crypto::public_key &k1) { return memcmp(&k0, &k1, 32); }
+ static inline bool operator==(const rct::key &k0, const crypto::public_key &k1) { return !crypto_verify_32(k0.bytes, (const unsigned char*)&k1); }
+ static inline bool operator!=(const rct::key &k0, const crypto::public_key &k1) { return crypto_verify_32(k0.bytes, (const unsigned char*)&k1); }
}
namespace cryptonote {
- static inline bool operator==(const crypto::public_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); }
- static inline bool operator!=(const crypto::public_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); }
- static inline bool operator==(const crypto::secret_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); }
- static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); }
+ static inline bool operator==(const crypto::public_key &k0, const rct::key &k1) { return !crypto_verify_32((const unsigned char*)&k0, k1.bytes); }
+ static inline bool operator!=(const crypto::public_key &k0, const rct::key &k1) { return crypto_verify_32((const unsigned char*)&k0, k1.bytes); }
+ static inline bool operator==(const crypto::secret_key &k0, const rct::key &k1) { return !crypto_verify_32((const unsigned char*)&k0, k1.bytes); }
+ static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return crypto_verify_32((const unsigned char*)&k0, k1.bytes); }
}
namespace rct {
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 55858cc2a..25abe4825 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -86,53 +86,45 @@ namespace rpc
res.error_details = "incorrect number of transactions retrieved for block";
return;
}
- std::vector<transaction> txs;
- for (const auto& p : it->second)
- {
- txs.resize(txs.size() + 1);
- if (!parse_and_validate_tx_from_blob(p.second, txs.back()))
- {
- res.blocks.clear();
- res.output_indices.clear();
- res.status = Message::STATUS_FAILED;
- res.error_details = "failed retrieving a requested transaction";
- return;
- }
- }
cryptonote::rpc::block_output_indices& indices = res.output_indices[block_count];
// miner tx output indices
{
cryptonote::rpc::tx_output_indices tx_indices;
- bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices);
- if (!r)
+ if (!m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices))
{
res.status = Message::STATUS_FAILED;
res.error_details = "core::get_tx_outputs_gindexs() returned false";
return;
}
- indices.push_back(tx_indices);
+ indices.push_back(std::move(tx_indices));
}
- // assume each block returned is returned with all its transactions
- // in the correct order.
- auto tx_it = txs.begin();
- for (const crypto::hash& h : bwt.block.tx_hashes)
+ auto hash_it = bwt.block.tx_hashes.begin();
+ bwt.transactions.reserve(it->second.size());
+ for (const auto& blob : it->second)
{
- bwt.transactions.emplace(h, *tx_it);
- tx_it++;
+ bwt.transactions.emplace_back();
+ if (!parse_and_validate_tx_from_blob(blob.second, bwt.transactions.back()))
+ {
+ res.blocks.clear();
+ res.output_indices.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "failed retrieving a requested transaction";
+ return;
+ }
cryptonote::rpc::tx_output_indices tx_indices;
- bool r = m_core.get_tx_outputs_gindexs(h, tx_indices);
- if (!r)
+ if (!m_core.get_tx_outputs_gindexs(*hash_it, tx_indices))
{
res.status = Message::STATUS_FAILED;
res.error_details = "core::get_tx_outputs_gindexs() returned false";
return;
}
- indices.push_back(tx_indices);
+ indices.push_back(std::move(tx_indices));
+ ++hash_it;
}
it++;
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
index fc1b2329d..20390aee8 100644
--- a/src/rpc/message_data_structs.h
+++ b/src/rpc/message_data_structs.h
@@ -44,7 +44,7 @@ namespace rpc
struct block_with_transactions
{
cryptonote::block block;
- std::unordered_map<crypto::hash, cryptonote::transaction> transactions;
+ std::vector<cryptonote::transaction> transactions;
};
typedef std::vector<uint64_t> tx_output_indices;
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index c2467b863..67fe709dc 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -28,6 +28,8 @@
#include "json_object.h"
+#include <boost/range/adaptor/transformed.hpp>
+#include <boost/variant/apply_visitor.hpp>
#include <limits>
#include <type_traits>
#include "string_tools.h"
@@ -219,11 +221,11 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, ra
INSERT_INTO_JSON_OBJECT(val, doc, version, tx.version);
INSERT_INTO_JSON_OBJECT(val, doc, unlock_time, tx.unlock_time);
- INSERT_INTO_JSON_OBJECT(val, doc, vin, tx.vin);
- INSERT_INTO_JSON_OBJECT(val, doc, vout, tx.vout);
+ INSERT_INTO_JSON_OBJECT(val, doc, inputs, tx.vin);
+ INSERT_INTO_JSON_OBJECT(val, doc, outputs, tx.vout);
INSERT_INTO_JSON_OBJECT(val, doc, extra, tx.extra);
INSERT_INTO_JSON_OBJECT(val, doc, signatures, tx.signatures);
- INSERT_INTO_JSON_OBJECT(val, doc, rct_signatures, tx.rct_signatures);
+ INSERT_INTO_JSON_OBJECT(val, doc, ringct, tx.rct_signatures);
}
@@ -236,11 +238,11 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
GET_FROM_JSON_OBJECT(val, tx.version, version);
GET_FROM_JSON_OBJECT(val, tx.unlock_time, unlock_time);
- GET_FROM_JSON_OBJECT(val, tx.vin, vin);
- GET_FROM_JSON_OBJECT(val, tx.vout, vout);
+ GET_FROM_JSON_OBJECT(val, tx.vin, inputs);
+ GET_FROM_JSON_OBJECT(val, tx.vout, outputs);
GET_FROM_JSON_OBJECT(val, tx.extra, extra);
GET_FROM_JSON_OBJECT(val, tx.signatures, signatures);
- GET_FROM_JSON_OBJECT(val, tx.rct_signatures, rct_signatures);
+ GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct);
}
void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val)
@@ -277,26 +279,31 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapid
{
val.SetObject();
- if (txin.type() == typeid(cryptonote::txin_gen))
+ struct add_input
{
- val.AddMember("type", "txin_gen", doc.GetAllocator());
- INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_gen>(txin));
- }
- else if (txin.type() == typeid(cryptonote::txin_to_script))
- {
- val.AddMember("type", "txin_to_script", doc.GetAllocator());
- INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_script>(txin));
- }
- else if (txin.type() == typeid(cryptonote::txin_to_scripthash))
- {
- val.AddMember("type", "txin_to_scripthash", doc.GetAllocator());
- INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_scripthash>(txin));
- }
- else if (txin.type() == typeid(cryptonote::txin_to_key))
- {
- val.AddMember("type", "txin_to_key", doc.GetAllocator());
- INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_key>(txin));
- }
+ using result_type = void;
+
+ rapidjson::Document& doc;
+ rapidjson::Value& val;
+
+ void operator()(cryptonote::txin_to_key const& input) const
+ {
+ INSERT_INTO_JSON_OBJECT(val, doc, to_key, input);
+ }
+ void operator()(cryptonote::txin_gen const& input) const
+ {
+ INSERT_INTO_JSON_OBJECT(val, doc, gen, input);
+ }
+ void operator()(cryptonote::txin_to_script const& input) const
+ {
+ INSERT_INTO_JSON_OBJECT(val, doc, to_script, input);
+ }
+ void operator()(cryptonote::txin_to_scripthash const& input) const
+ {
+ INSERT_INTO_JSON_OBJECT(val, doc, to_scripthash, input);
+ }
+ };
+ boost::apply_visitor(add_input{doc, val}, txin);
}
@@ -307,31 +314,37 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin)
throw WRONG_TYPE("json object");
}
- OBJECT_HAS_MEMBER_OR_THROW(val, "type")
- OBJECT_HAS_MEMBER_OR_THROW(val, "value")
- if (val["type"]== "txin_gen")
- {
- cryptonote::txin_gen tmpVal;
- fromJsonValue(val["value"], tmpVal);
- txin = tmpVal;
- }
- else if (val["type"]== "txin_to_script")
+ if (val.MemberCount() != 1)
{
- cryptonote::txin_to_script tmpVal;
- fromJsonValue(val["value"], tmpVal);
- txin = tmpVal;
+ throw MISSING_KEY("Invalid input object");
}
- else if (val["type"] == "txin_to_scripthash")
- {
- cryptonote::txin_to_scripthash tmpVal;
- fromJsonValue(val["value"], tmpVal);
- txin = tmpVal;
- }
- else if (val["type"] == "txin_to_key")
+
+ for (auto const& elem : val.GetObject())
{
- cryptonote::txin_to_key tmpVal;
- fromJsonValue(val["value"], tmpVal);
- txin = tmpVal;
+ if (elem.name == "to_key")
+ {
+ cryptonote::txin_to_key tmpVal;
+ fromJsonValue(elem.value, tmpVal);
+ txin = std::move(tmpVal);
+ }
+ else if (elem.name == "gen")
+ {
+ cryptonote::txin_gen tmpVal;
+ fromJsonValue(elem.value, tmpVal);
+ txin = std::move(tmpVal);
+ }
+ else if (elem.name == "to_script")
+ {
+ cryptonote::txin_to_script tmpVal;
+ fromJsonValue(elem.value, tmpVal);
+ txin = std::move(tmpVal);
+ }
+ else if (elem.name == "to_scripthash")
+ {
+ cryptonote::txin_to_scripthash tmpVal;
+ fromJsonValue(elem.value, tmpVal);
+ txin = std::move(tmpVal);
+ }
}
}
@@ -405,7 +418,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin,
INSERT_INTO_JSON_OBJECT(val, doc, amount, txin.amount);
INSERT_INTO_JSON_OBJECT(val, doc, key_offsets, txin.key_offsets);
- INSERT_INTO_JSON_OBJECT(val, doc, k_image, txin.k_image);
+ INSERT_INTO_JSON_OBJECT(val, doc, key_image, txin.k_image);
}
@@ -418,58 +431,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin)
GET_FROM_JSON_OBJECT(val, txin.amount, amount);
GET_FROM_JSON_OBJECT(val, txin.key_offsets, key_offsets);
- GET_FROM_JSON_OBJECT(val, txin.k_image, k_image);
-}
-
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val)
-{
- val.SetObject();
-
- if (txout.type() == typeid(cryptonote::txout_to_script))
- {
- val.AddMember("type", "txout_to_script", doc.GetAllocator());
- INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_script>(txout));
- }
- else if (txout.type() == typeid(cryptonote::txout_to_scripthash))
- {
- val.AddMember("type", "txout_to_scripthash", doc.GetAllocator());
- INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_scripthash>(txout));
- }
- else if (txout.type() == typeid(cryptonote::txout_to_key))
- {
- val.AddMember("type", "txout_to_key", doc.GetAllocator());
- INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_key>(txout));
- }
-}
-
-
-void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout)
-{
- if (!val.IsObject())
- {
- throw WRONG_TYPE("json object");
- }
-
- OBJECT_HAS_MEMBER_OR_THROW(val, "type")
- OBJECT_HAS_MEMBER_OR_THROW(val, "value")
- if (val["type"]== "txout_to_script")
- {
- cryptonote::txout_to_script tmpVal;
- fromJsonValue(val["value"], tmpVal);
- txout = tmpVal;
- }
- else if (val["type"] == "txout_to_scripthash")
- {
- cryptonote::txout_to_scripthash tmpVal;
- fromJsonValue(val["value"], tmpVal);
- txout = tmpVal;
- }
- else if (val["type"] == "txout_to_key")
- {
- cryptonote::txout_to_key tmpVal;
- fromJsonValue(val["value"], tmpVal);
- txout = tmpVal;
- }
+ GET_FROM_JSON_OBJECT(val, txin.k_image, key_image);
}
void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val)
@@ -533,7 +495,28 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapi
val.SetObject();
INSERT_INTO_JSON_OBJECT(val, doc, amount, txout.amount);
- INSERT_INTO_JSON_OBJECT(val, doc, target, txout.target);
+
+ struct add_output
+ {
+ using result_type = void;
+
+ rapidjson::Document& doc;
+ rapidjson::Value& val;
+
+ void operator()(cryptonote::txout_to_key const& output) const
+ {
+ INSERT_INTO_JSON_OBJECT(val, doc, to_key, output);
+ }
+ void operator()(cryptonote::txout_to_script const& output) const
+ {
+ INSERT_INTO_JSON_OBJECT(val, doc, to_script, output);
+ }
+ void operator()(cryptonote::txout_to_scripthash const& output) const
+ {
+ INSERT_INTO_JSON_OBJECT(val, doc, to_scripthash, output);
+ }
+ };
+ boost::apply_visitor(add_output{doc, val}, txout.target);
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout)
@@ -543,8 +526,37 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout)
throw WRONG_TYPE("json object");
}
- GET_FROM_JSON_OBJECT(val, txout.amount, amount);
- GET_FROM_JSON_OBJECT(val, txout.target, target);
+ if (val.MemberCount() != 2)
+ {
+ throw MISSING_KEY("Invalid input object");
+ }
+
+ for (auto const& elem : val.GetObject())
+ {
+ if (elem.name == "amount")
+ {
+ fromJsonValue(elem.value, txout.amount);
+ }
+
+ if (elem.name == "to_key")
+ {
+ cryptonote::txout_to_key tmpVal;
+ fromJsonValue(elem.value, tmpVal);
+ txout.target = std::move(tmpVal);
+ }
+ else if (elem.name == "to_script")
+ {
+ cryptonote::txout_to_script tmpVal;
+ fromJsonValue(elem.value, tmpVal);
+ txout.target = std::move(tmpVal);
+ }
+ else if (elem.name == "to_scripthash")
+ {
+ cryptonote::txout_to_scripthash tmpVal;
+ fromJsonValue(elem.value, tmpVal);
+ txout.target = std::move(tmpVal);
+ }
+ }
}
void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val)
@@ -617,7 +629,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entr
val.SetObject();
INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block);
- INSERT_INTO_JSON_OBJECT(val, doc, txs, blk.txs);
+ INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.txs);
}
@@ -629,7 +641,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry
}
GET_FROM_JSON_OBJECT(val, blk.block, block);
- GET_FROM_JSON_OBJECT(val, blk.txs, txs);
+ GET_FROM_JSON_OBJECT(val, blk.txs, transactions);
}
void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val)
@@ -936,51 +948,70 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp
void toJsonValue(rapidjson::Document& doc, const rct::rctSig& sig, rapidjson::Value& val)
{
+ using boost::adaptors::transform;
+
val.SetObject();
+ const auto just_mask = [] (rct::ctkey const& key) -> rct::key const&
+ {
+ return key.mask;
+ };
+
INSERT_INTO_JSON_OBJECT(val, doc, type, sig.type);
- INSERT_INTO_JSON_OBJECT(val, doc, message, sig.message);
- INSERT_INTO_JSON_OBJECT(val, doc, mixRing, sig.mixRing);
- INSERT_INTO_JSON_OBJECT(val, doc, pseudoOuts, sig.pseudoOuts);
- INSERT_INTO_JSON_OBJECT(val, doc, ecdhInfo, sig.ecdhInfo);
- INSERT_INTO_JSON_OBJECT(val, doc, outPk, sig.outPk);
- INSERT_INTO_JSON_OBJECT(val, doc, txnFee, sig.txnFee);
- INSERT_INTO_JSON_OBJECT(val, doc, p, sig.p);
+ INSERT_INTO_JSON_OBJECT(val, doc, encrypted, sig.ecdhInfo);
+ INSERT_INTO_JSON_OBJECT(val, doc, commitments, transform(sig.outPk, just_mask));
+ INSERT_INTO_JSON_OBJECT(val, doc, fee, sig.txnFee);
+
+ // prunable
+ {
+ rapidjson::Value prunable;
+ prunable.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(prunable, doc, range_proofs, sig.p.rangeSigs);
+ INSERT_INTO_JSON_OBJECT(prunable, doc, bulletproofs, sig.p.bulletproofs);
+ INSERT_INTO_JSON_OBJECT(prunable, doc, mlsags, sig.p.MGs);
+ INSERT_INTO_JSON_OBJECT(prunable, doc, pseudo_outs, sig.get_pseudo_outs());
+
+ val.AddMember("prunable", prunable, doc.GetAllocator());
+ }
}
void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
{
+ using boost::adaptors::transform;
+
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
+ std::vector<rct::key> commitments;
+
GET_FROM_JSON_OBJECT(val, sig.type, type);
- GET_FROM_JSON_OBJECT(val, sig.message, message);
- GET_FROM_JSON_OBJECT(val, sig.mixRing, mixRing);
- GET_FROM_JSON_OBJECT(val, sig.pseudoOuts, pseudoOuts);
- GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, ecdhInfo);
- GET_FROM_JSON_OBJECT(val, sig.outPk, outPk);
- GET_FROM_JSON_OBJECT(val, sig.txnFee, txnFee);
- GET_FROM_JSON_OBJECT(val, sig.p, p);
-}
+ GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, encrypted);
+ GET_FROM_JSON_OBJECT(val, commitments, commitments);
+ GET_FROM_JSON_OBJECT(val, sig.txnFee, fee);
-void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val)
-{
- val.SetObject();
+ // prunable
+ {
+ OBJECT_HAS_MEMBER_OR_THROW(val, "prunable");
+ const auto& prunable = val["prunable"];
- INSERT_INTO_JSON_OBJECT(val, doc, dest, key.dest);
- INSERT_INTO_JSON_OBJECT(val, doc, mask, key.mask);
-}
+ rct::keyV pseudo_outs;
-void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key)
-{
- if (!val.IsObject())
+ GET_FROM_JSON_OBJECT(prunable, sig.p.rangeSigs, range_proofs);
+ GET_FROM_JSON_OBJECT(prunable, sig.p.bulletproofs, bulletproofs);
+ GET_FROM_JSON_OBJECT(prunable, sig.p.MGs, mlsags);
+ GET_FROM_JSON_OBJECT(prunable, pseudo_outs, pseudo_outs);
+
+ sig.get_pseudo_outs() = std::move(pseudo_outs);
+ }
+
+ sig.outPk.reserve(commitments.size());
+ for (rct::key const& commitment : commitments)
{
- throw WRONG_TYPE("json object");
+ sig.outPk.push_back({{}, commitment});
}
- GET_FROM_JSON_OBJECT(val, key.dest, dest);
- GET_FROM_JSON_OBJECT(val, key.mask, mask);
}
void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val)
@@ -1002,27 +1033,6 @@ void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple)
GET_FROM_JSON_OBJECT(val, tuple.amount, amount);
}
-void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val)
-{
- val.SetObject();
-
- INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs);
- INSERT_INTO_JSON_OBJECT(val, doc, bulletproofs, sig.bulletproofs);
- INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs);
-}
-
-void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig)
-{
- if (!val.IsObject())
- {
- throw WRONG_TYPE("json object");
- }
-
- GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs);
- GET_FROM_JSON_OBJECT(val, sig.bulletproofs, bulletproofs);
- GET_FROM_JSON_OBJECT(val, sig.MGs, MGs);
-}
-
void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val)
{
val.SetObject();
@@ -1040,10 +1050,16 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig)
throw WRONG_TYPE("json object");
}
+ const auto ci = val.FindMember("Ci");
+ if (ci == val.MemberEnd())
+ {
+ throw MISSING_KEY("Ci");
+ }
+
GET_FROM_JSON_OBJECT(val, sig.asig, asig);
std::vector<rct::key> keyVector;
- cryptonote::json::fromJsonValue(val["Ci"], keyVector);
+ cryptonote::json::fromJsonValue(ci->value, keyVector);
if (!(keyVector.size() == 64))
{
throw WRONG_TYPE("key64 (rct::key[64])");
@@ -1098,10 +1114,10 @@ void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::V
val.SetObject();
std::vector<rct::key> keyVector(sig.s0, std::end(sig.s0));
- INSERT_INTO_JSON_OBJECT(val, doc, s0, sig.s0);
+ INSERT_INTO_JSON_OBJECT(val, doc, s0, keyVector);
keyVector.assign(sig.s1, std::end(sig.s1));
- INSERT_INTO_JSON_OBJECT(val, doc, s1, sig.s1);
+ INSERT_INTO_JSON_OBJECT(val, doc, s1, keyVector);
INSERT_INTO_JSON_OBJECT(val, doc, ee, sig.ee);
}
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index de21ace66..da3351fe3 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -263,15 +263,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp
void toJsonValue(rapidjson::Document& doc, const rct::rctSig& i, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& i, rct::rctSig& sig);
-void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val);
-void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key);
-
void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple);
-void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val);
-void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig);
-
void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig);
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index e07c7e49b..f5aeabded 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -95,17 +95,18 @@ typedef cryptonote::simple_wallet sw;
m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
/* stop any background refresh, and take over */ \
m_wallet->stop(); \
- m_idle_mutex.lock(); \
- while (m_auto_refresh_refreshing) \
- m_idle_cond.notify_one(); \
- m_idle_mutex.unlock(); \
-/* if (auto_refresh_run)*/ \
- /*m_auto_refresh_thread.join();*/ \
boost::unique_lock<boost::mutex> lock(m_idle_mutex); \
+ m_idle_cond.notify_all(); \
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
})
+#define SCOPED_WALLET_UNLOCK() \
+ LOCK_IDLE_SCOPE(); \
+ boost::optional<tools::password_container> pwd_container = boost::none; \
+ if (m_wallet->ask_password() && !m_wallet->watch_only() && !(pwd_container = get_and_verify_password())) { return true; } \
+ tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container);
+
enum TransferType {
TransferOriginal,
TransferNew,
@@ -128,8 +129,6 @@ namespace
const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false};
const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false};
- const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false};
- const command_line::arg_descriptor<bool> arg_untrusted_daemon = {"untrusted-daemon", sw::tr("Disable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false};
const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0};
const command_line::arg_descriptor<bool> arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false};
@@ -152,12 +151,31 @@ namespace
//
// Note also that input for passwords is NOT translated, to remain compatible with any
// passwords containing special characters that predate this switch to UTF-8 support.
- static std::string cp850_to_utf8(const std::string &cp850_str)
+ template<typename T>
+ static T cp850_to_utf8(const T &cp850_str)
{
boost::locale::generator gen;
gen.locale_cache_enabled(true);
std::locale loc = gen("en_US.CP850");
- return boost::locale::conv::to_utf<char>(cp850_str, loc);
+ const boost::locale::conv::method_type how = boost::locale::conv::default_method;
+ T result;
+ const char *begin = cp850_str.data();
+ const char *end = begin + cp850_str.size();
+ result.reserve(end-begin);
+ typedef std::back_insert_iterator<T> inserter_type;
+ inserter_type inserter(result);
+ boost::locale::utf::code_point c;
+ while(begin!=end) {
+ c=boost::locale::utf::utf_traits<char>::template decode<char const *>(begin,end);
+ if(c==boost::locale::utf::illegal || c==boost::locale::utf::incomplete) {
+ if(how==boost::locale::conv::stop)
+ throw boost::locale::conv::conversion_error();
+ }
+ else {
+ boost::locale::utf::utf_traits<char>::template encode<inserter_type>(c,inserter);
+ }
+ }
+ return result;
}
#endif
@@ -177,6 +195,28 @@ namespace
return epee::string_tools::trim(buf);
}
+ epee::wipeable_string input_secure_line(const std::string& prompt)
+ {
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
+ auto pwd_container = tools::password_container::prompt(false, prompt.c_str(), false);
+ if (!pwd_container)
+ {
+ MERROR("Failed to read secure line");
+ return "";
+ }
+
+ epee::wipeable_string buf = pwd_container->password();
+
+#ifdef WIN32
+ buf = cp850_to_utf8(buf);
+#endif
+
+ buf.trim();
+ return buf;
+ }
+
boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
{
#ifdef HAVE_READLINE
@@ -512,6 +552,18 @@ namespace
}
return true;
}
+
+ void print_secret_key(const crypto::secret_key &k)
+ {
+ static constexpr const char hex[] = u8"0123456789abcdef";
+ const uint8_t *ptr = (const uint8_t*)k.data;
+ for (size_t i = 0, sz = sizeof(k); i < sz; ++i)
+ {
+ putchar(hex[*ptr >> 4]);
+ putchar(hex[*ptr & 15]);
+ ++ptr;
+ }
+ }
}
bool parse_priority(const std::string& arg, uint32_t& priority)
@@ -527,6 +579,18 @@ bool parse_priority(const std::string& arg, uint32_t& priority)
return false;
}
+std::string join_priority_strings(const char *delimiter)
+{
+ std::string s;
+ for (size_t n = 0; n < allowed_priority_strings.size(); ++n)
+ {
+ if (!s.empty())
+ s += delimiter;
+ s += allowed_priority_strings[n];
+ }
+ return s;
+}
+
std::string simple_wallet::get_commands_str()
{
std::stringstream ss;
@@ -561,12 +625,15 @@ std::string simple_wallet::get_command_usage(const std::vector<std::string> &arg
bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+ SCOPED_WALLET_UNLOCK();
// don't log
+ PAUSE_READLINE();
if (m_wallet->key_on_device()) {
std::cout << "secret: On device. Not available" << std::endl;
} else {
- std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << std::endl;
+ printf("secret: ");
+ print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
+ putchar('\n');
}
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
@@ -580,12 +647,15 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
fail_msg_writer() << tr("wallet is watch-only and has no spend key");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+ SCOPED_WALLET_UNLOCK();
// don't log
+ PAUSE_READLINE();
if (m_wallet->key_on_device()) {
std::cout << "secret: On device. Not available" << std::endl;
} else {
- std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key) << std::endl;
+ printf("secret: ");
+ print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
+ putchar('\n');
}
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
@@ -595,7 +665,7 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
bool simple_wallet::print_seed(bool encrypted)
{
bool success = false;
- std::string seed;
+ epee::wipeable_string seed;
bool ready, multisig;
if (m_wallet->key_on_device())
@@ -608,7 +678,8 @@ bool simple_wallet::print_seed(bool encrypted)
fail_msg_writer() << tr("wallet is watch-only and has no seed");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ SCOPED_WALLET_UNLOCK();
multisig = m_wallet->multisig(&ready);
if (multisig)
@@ -677,22 +748,36 @@ bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = s
fail_msg_writer() << tr("wallet is watch-only and has no seed");
return true;
}
- if (!m_wallet->is_deterministic())
- {
- fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
- return true;
- }
-
- const auto pwd_container = get_and_verify_password();
- if (pwd_container)
+
+ epee::wipeable_string password;
{
- std::string mnemonic_language = get_mnemonic_language();
- if (mnemonic_language.empty())
+ SCOPED_WALLET_UNLOCK();
+
+ if (!m_wallet->is_deterministic())
+ {
+ fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
return true;
+ }
- m_wallet->set_seed_language(std::move(mnemonic_language));
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ // we need the password, even if ask-password is unset
+ if (!pwd_container)
+ {
+ pwd_container = get_and_verify_password();
+ if (pwd_container == boost::none)
+ {
+ fail_msg_writer() << tr("Incorrect password");
+ return true;
+ }
+ }
+ password = pwd_container->password();
}
+
+ std::string mnemonic_language = get_mnemonic_language();
+ if (mnemonic_language.empty())
+ return true;
+
+ m_wallet->set_seed_language(std::move(mnemonic_language));
+ m_wallet->rewrite(m_wallet_file, password);
return true;
}
@@ -713,8 +798,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args)
try
{
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
- m_wallet->store();
+ m_wallet->change_password(m_wallet_file, orig_pwd_container->password(), pwd_container->password());
}
catch (const tools::error::wallet_logic_error& e)
{
@@ -817,12 +901,7 @@ bool simple_wallet::prepare_multisig(const std::vector<std::string> &args)
return true;
}
- const auto orig_pwd_container = get_and_verify_password();
- if(orig_pwd_container == boost::none)
- {
- fail_msg_writer() << tr("Your password is incorrect.");
- return true;
- }
+ SCOPED_WALLET_UNLOCK();
std::string multisig_info = m_wallet->get_multisig_info();
success_msg_writer() << multisig_info;
@@ -855,13 +934,6 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
return true;
}
- const auto orig_pwd_container = get_and_verify_password();
- if(orig_pwd_container == boost::none)
- {
- fail_msg_writer() << tr("Your original password was incorrect.");
- return true;
- }
-
if (args.size() < 2)
{
fail_msg_writer() << tr("usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...]");
@@ -876,6 +948,13 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
return true;
}
+ const auto orig_pwd_container = get_and_verify_password();
+ if(orig_pwd_container == boost::none)
+ {
+ fail_msg_writer() << tr("Your original password was incorrect.");
+ return true;
+ }
+
LOCK_IDLE_SCOPE();
try
@@ -917,6 +996,14 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
fail_msg_writer() << tr("command not supported by HW wallet");
return true;
}
+
+ const auto pwd_container = get_and_verify_password();
+ if(pwd_container == boost::none)
+ {
+ fail_msg_writer() << tr("Your original password was incorrect.");
+ return true;
+ }
+
if (!m_wallet->multisig(&ready))
{
fail_msg_writer() << tr("This wallet is not multisig");
@@ -928,12 +1015,7 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
return true;
}
- const auto orig_pwd_container = get_and_verify_password();
- if(orig_pwd_container == boost::none)
- {
- fail_msg_writer() << tr("Your original password was incorrect.");
- return true;
- }
+ LOCK_IDLE_SCOPE();
if (args.size() < 2)
{
@@ -943,7 +1025,7 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
try
{
- if (!m_wallet->finalize_multisig(orig_pwd_container->password(), args))
+ if (!m_wallet->finalize_multisig(pwd_container->password(), args))
{
fail_msg_writer() << tr("Failed to finalize multisig");
return true;
@@ -981,8 +1063,8 @@ bool simple_wallet::export_multisig(const std::vector<std::string> &args)
fail_msg_writer() << tr("usage: export_multisig_info <filename>");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password())
- return true;
+
+ SCOPED_WALLET_UNLOCK();
const std::string filename = args[0];
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
@@ -1033,8 +1115,8 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
fail_msg_writer() << tr("usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password())
- return true;
+
+ SCOPED_WALLET_UNLOCK();
std::vector<cryptonote::blobdata> info;
for (size_t n = 0; n < args.size(); ++n)
@@ -1050,11 +1132,11 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
info.push_back(std::move(data));
}
- LOCK_IDLE_SCOPE();
-
// all read and parsed, actually import
try
{
+ m_in_manual_refresh.store(true, std::memory_order_relaxed);
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
size_t n_outputs = m_wallet->import_multisig(info);
// Clear line "Height xxx of xxx"
std::cout << "\r \r";
@@ -1065,7 +1147,7 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
fail_msg_writer() << tr("Failed to import multisig info: ") << e.what();
return true;
}
- if (is_daemon_trusted())
+ if (m_wallet->is_trusted_daemon())
{
try
{
@@ -1112,7 +1194,8 @@ bool simple_wallet::sign_multisig(const std::vector<std::string> &args)
fail_msg_writer() << tr("usage: sign_multisig <filename>");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ SCOPED_WALLET_UNLOCK();
std::string filename = args[0];
std::vector<crypto::hash> txids;
@@ -1185,7 +1268,8 @@ bool simple_wallet::submit_multisig(const std::vector<std::string> &args)
fail_msg_writer() << tr("usage: submit_multisig <filename>");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ SCOPED_WALLET_UNLOCK();
if (!try_connect_to_daemon())
return true;
@@ -1217,7 +1301,7 @@ bool simple_wallet::submit_multisig(const std::vector<std::string> &args)
}
catch (const std::exception &e)
{
- handle_transfer_exception(std::current_exception(), is_daemon_trusted());
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
}
catch (...)
{
@@ -1252,7 +1336,8 @@ bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args)
fail_msg_writer() << tr("usage: export_raw_multisig <filename>");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ SCOPED_WALLET_UNLOCK();
std::string filename = args[0];
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
@@ -1777,12 +1862,12 @@ bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/*
bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
- int priority = 0;
+ uint32_t priority = 0;
try
{
if (strchr(args[1].c_str(), '-'))
{
- fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4 ");
+ fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
return true;
}
if (args[1] == "0")
@@ -1791,11 +1876,23 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/*
}
else
{
- priority = boost::lexical_cast<int>(args[1]);
- if (priority < 1 || priority > 4)
+ bool found = false;
+ for (size_t n = 0; n < allowed_priority_strings.size(); ++n)
{
- fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4");
- return true;
+ if (allowed_priority_strings[n] == args[1])
+ {
+ found = true;
+ priority = n;
+ }
+ }
+ if (!found)
+ {
+ priority = boost::lexical_cast<int>(args[1]);
+ if (priority < 1 || priority > 4)
+ {
+ fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
+ return true;
+ }
}
}
@@ -1809,7 +1906,7 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/*
}
catch(const boost::bad_lexical_cast &)
{
- fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4");
+ fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
return true;
}
catch(...)
@@ -1874,6 +1971,14 @@ bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = st
if (pwd_container)
{
parse_bool_and_use(args[1], [&](bool r) {
+ const bool cur_r = m_wallet->ask_password();
+ if (!m_wallet->watch_only())
+ {
+ if (cur_r && !r)
+ m_wallet->decrypt_keys(pwd_container->password());
+ else if (!cur_r && r)
+ m_wallet->encrypt_keys(pwd_container->password());
+ }
m_wallet->ask_password(r);
m_wallet->rewrite(m_wallet_file, pwd_container->password());
});
@@ -2347,7 +2452,7 @@ simple_wallet::simple_wallet()
tr("Show the unspent outputs of a specified address within an optional amount range."));
m_cmd_binder.set_handler("rescan_bc",
boost::bind(&simple_wallet::rescan_blockchain, this, _1),
- tr("Rescan the blockchain from scratch."));
+ tr("Rescan the blockchain from scratch, losing any information which can not be recovered from the blockchain itself."));
m_cmd_binder.set_handler("set_tx_note",
boost::bind(&simple_wallet::set_tx_note, this, _1),
tr("set_tx_note <txid> [free text note]"),
@@ -2476,6 +2581,10 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
std::string seed_language = m_wallet->get_seed_language();
if (m_use_english_language_names)
seed_language = crypto::ElectrumWords::get_english_name_for(seed_language);
+ std::string priority_string = "invalid";
+ uint32_t priority = m_wallet->get_default_priority();
+ if (priority < allowed_priority_strings.size())
+ priority_string = allowed_priority_strings[priority];
success_msg_writer() << "seed = " << seed_language;
success_msg_writer() << "always-confirm-transfers = " << m_wallet->always_confirm_transfers();
success_msg_writer() << "print-ring-members = " << m_wallet->print_ring_members();
@@ -2483,7 +2592,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
success_msg_writer() << "default-ring-size = " << (m_wallet->default_mixin() ? m_wallet->default_mixin() + 1 : 0);
success_msg_writer() << "auto-refresh = " << m_wallet->auto_refresh();
success_msg_writer() << "refresh-type = " << get_refresh_type_name(m_wallet->get_refresh_type());
- success_msg_writer() << "priority = " << m_wallet->get_default_priority();
+ success_msg_writer() << "priority = " << priority<< " (" << priority_string << ")";
success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id();
success_msg_writer() << "ask-password = " << m_wallet->ask_password();
success_msg_writer() << "unit = " << cryptonote::get_unit(cryptonote::get_default_decimal_point());
@@ -2539,7 +2648,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= ") << MIN_RING_SIZE);
CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)"));
- CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4"));
+ CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4, or one of ") << join_priority_strings(", "));
CHECK_SIMPLE_VARIABLE("confirm-missing-payment-id", set_confirm_missing_payment_id, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanonero, piconero"));
@@ -2679,28 +2788,45 @@ bool simple_wallet::ask_wallet_create_if_needed()
* \brief Prints the seed with a nice message
* \param seed seed to print
*/
-void simple_wallet::print_seed(std::string seed)
+void simple_wallet::print_seed(const epee::wipeable_string &seed)
{
success_msg_writer(true) << "\n" << tr("NOTE: the following 25 words can be used to recover access to your wallet. "
"Write them down and store them somewhere safe and secure. Please do not store them in "
"your email or on file storage services outside of your immediate control.\n");
- boost::replace_nth(seed, " ", 15, "\n");
- boost::replace_nth(seed, " ", 7, "\n");
// don't log
- std::cout << seed << std::endl;
+ int space_index = 0;
+ size_t len = seed.size();
+ for (const char *ptr = seed.data(); len--; ++ptr)
+ {
+ if (*ptr == ' ')
+ {
+ if (space_index == 15 || space_index == 7)
+ putchar('\n');
+ else
+ putchar(*ptr);
+ ++space_index;
+ }
+ else
+ putchar(*ptr);
+ }
+ putchar('\n');
+ fflush(stdout);
}
//----------------------------------------------------------------------------------------------------
-static bool might_be_partial_seed(std::string words)
+static bool might_be_partial_seed(const epee::wipeable_string &words)
{
- std::vector<std::string> seed;
+ std::vector<epee::wipeable_string> seed;
- boost::algorithm::trim(words);
- boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on);
+ words.split(seed);
return seed.size() < 24;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){
+ m_electrum_seed.wipe();
+ });
+
const bool testnet = tools::wallet2::has_testnet_option(vm);
const bool stagenet = tools::wallet2::has_stagenet_option(vm);
if (testnet && stagenet)
@@ -2710,7 +2836,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
- std::string multisig_keys;
+ epee::wipeable_string multisig_keys;
if (!handle_command_line(vm))
return false;
@@ -2752,8 +2878,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
if (m_restore_multisig_wallet)
{
- const char *prompt = "Specify multisig seed: ";
- m_electrum_seed = input_line(prompt);
+ const char *prompt = "Specify multisig seed";
+ m_electrum_seed = input_secure_line(prompt);
if (std::cin.eof())
return false;
if (m_electrum_seed.empty())
@@ -2767,8 +2893,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
m_electrum_seed = "";
do
{
- const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
- std::string electrum_seed = input_line(prompt);
+ const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed" : "Electrum seed continued";
+ epee::wipeable_string electrum_seed = input_secure_line(prompt);
if (std::cin.eof())
return false;
if (electrum_seed.empty())
@@ -2776,18 +2902,21 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
return false;
}
- m_electrum_seed += electrum_seed + " ";
+ m_electrum_seed += electrum_seed;
+ m_electrum_seed += ' ';
} while (might_be_partial_seed(m_electrum_seed));
}
}
if (m_restore_multisig_wallet)
{
- if (!epee::string_tools::parse_hexstr_to_binbuff(m_electrum_seed, multisig_keys))
+ const boost::optional<epee::wipeable_string> parsed = m_electrum_seed.parse_hexstr();
+ if (!parsed)
{
fail_msg_writer() << tr("Multisig seed failed verification");
return false;
}
+ multisig_keys = *parsed;
}
else
{
@@ -2809,7 +2938,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
crypto::secret_key key;
crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key);
sc_reduce32((unsigned char*)key.data);
- multisig_keys = m_wallet->decrypt(multisig_keys, key, true);
+ multisig_keys = m_wallet->decrypt<epee::wipeable_string>(std::string(multisig_keys.data(), multisig_keys.size()), key, true);
}
else
m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
@@ -3112,7 +3241,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
m_wallet_file = m_generate_from_json;
try
{
- m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file, password_prompter);
+ m_wallet = tools::wallet2::make_from_json(vm, false, m_wallet_file, password_prompter);
}
catch (const std::exception &e)
{
@@ -3260,22 +3389,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
- // set --trusted-daemon if local and not overridden
- if (!m_trusted_daemon)
- {
- try
- {
- m_trusted_daemon = false;
- if (tools::is_local_address(m_wallet->get_daemon_address()))
- {
- MINFO(tr("Daemon is local, assuming trusted"));
- m_trusted_daemon = true;
- }
- }
- catch (const std::exception &e) { }
- }
-
- if (!is_daemon_trusted())
+ if (!m_wallet->is_trusted_daemon())
message_writer() << (boost::format(tr("Warning: using an untrusted daemon at %s, privacy will be lessened")) % m_wallet->get_daemon_address()).str();
if (m_wallet->get_ring_database().empty())
@@ -3309,10 +3423,6 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet);
m_restore_multisig_wallet = command_line::get_arg(vm, arg_restore_multisig_wallet);
m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic);
- if (!command_line::is_arg_defaulted(vm, arg_trusted_daemon) || !command_line::is_arg_defaulted(vm, arg_untrusted_daemon))
- m_trusted_daemon = command_line::get_arg(vm, arg_trusted_daemon) && !command_line::get_arg(vm, arg_untrusted_daemon);
- if (!command_line::is_arg_defaulted(vm, arg_trusted_daemon) && !command_line::is_arg_defaulted(vm, arg_untrusted_daemon))
- message_writer() << tr("--trusted-daemon and --untrusted-daemon are both seen, assuming untrusted");
m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version);
m_restore_height = command_line::get_arg(vm, arg_restore_height);
m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay);
@@ -3414,7 +3524,7 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language)
{
- auto rc = tools::wallet2::make_new(vm, password_prompter);
+ auto rc = tools::wallet2::make_new(vm, false, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -3469,7 +3579,10 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
recovery_val = m_wallet->generate(m_wallet_file, std::move(rc.second).password(), recovery_key, recover, two_random, create_address_file);
message_writer(console_color_white, true) << tr("Generated new wallet: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
- std::cout << tr("View key: ") << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << ENDL;
+ PAUSE_READLINE();
+ std::cout << tr("View key: ");
+ print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
+ putchar('\n');
}
catch (const std::exception& e)
{
@@ -3478,7 +3591,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
}
// convert rng value to electrum-style word list
- std::string electrum_words;
+ epee::wipeable_string electrum_words;
crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language);
@@ -3506,7 +3619,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
const crypto::secret_key& viewkey)
{
- auto rc = tools::wallet2::make_new(vm, password_prompter);
+ auto rc = tools::wallet2::make_new(vm, false, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -3552,7 +3665,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const std::string &device_name) {
- auto rc = tools::wallet2::make_new(vm, password_prompter);
+ auto rc = tools::wallet2::make_new(vm, false, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -3586,9 +3699,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
}
//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
- const std::string &multisig_keys, const std::string &old_language)
+ const epee::wipeable_string &multisig_keys, const std::string &old_language)
{
- auto rc = tools::wallet2::make_new(vm, password_prompter);
+ auto rc = tools::wallet2::make_new(vm, false, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -3659,7 +3772,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
epee::wipeable_string password;
try
{
- auto rc = tools::wallet2::make_from_file(vm, m_wallet_file, password_prompter);
+ auto rc = tools::wallet2::make_from_file(vm, false, m_wallet_file, password_prompter);
m_wallet = std::move(rc.first);
password = std::move(std::move(rc.second).password());
if (!m_wallet)
@@ -3686,7 +3799,12 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
// NOTE: this is_deprecated() refers to the wallet file format before becoming JSON. It does not refer to the "old english" seed words form of "deprecated" used elsewhere.
if (m_wallet->is_deprecated())
{
- if (m_wallet->is_deterministic())
+ bool is_deterministic;
+ {
+ SCOPED_WALLET_UNLOCK();
+ is_deterministic = m_wallet->is_deterministic();
+ }
+ if (is_deterministic)
{
message_writer(console_color_green, false) << "\n" << tr("You had been using "
"a deprecated version of the wallet. Please proceed to upgrade your wallet.\n");
@@ -3697,7 +3815,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
m_wallet->rewrite(m_wallet_file, password);
// Display the seed
- std::string seed;
+ epee::wipeable_string seed;
m_wallet->get_seed(seed);
print_seed(seed);
}
@@ -3816,7 +3934,7 @@ bool simple_wallet::save_watch_only(const std::vector<std::string> &args/* = std
//----------------------------------------------------------------------------------------------------
bool simple_wallet::start_mining(const std::vector<std::string>& args)
{
- if (!is_daemon_trusted())
+ if (!m_wallet->is_trusted_daemon())
{
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
return true;
@@ -3924,34 +4042,34 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
daemon_url = args[0];
}
LOCK_IDLE_SCOPE();
- m_wallet->init(daemon_url);
+ m_wallet->init(false, daemon_url);
if (args.size() == 2)
{
if (args[1] == "trusted")
- m_trusted_daemon = true;
+ m_wallet->set_trusted_daemon(true);
else if (args[1] == "untrusted")
- m_trusted_daemon = false;
+ m_wallet->set_trusted_daemon(false);
else
{
fail_msg_writer() << tr("Expected trusted or untrusted, got ") << args[1] << ": assuming untrusted";
- m_trusted_daemon = false;
+ m_wallet->set_trusted_daemon(false);
}
}
else
{
- m_trusted_daemon = false;
+ m_wallet->set_trusted_daemon(false);
try
{
if (tools::is_local_address(m_wallet->get_daemon_address()))
{
MINFO(tr("Daemon is local, assuming trusted"));
- m_trusted_daemon = true;
+ m_wallet->set_trusted_daemon(true);
}
}
catch (const std::exception &e) { }
}
- success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (*m_trusted_daemon ? tr("trusted") : tr("untrusted"));
+ success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (m_wallet->is_trusted_daemon() ? tr("trusted") : tr("untrusted"));
} else {
fail_msg_writer() << tr("This does not seem to be a valid daemon URL.");
}
@@ -4038,6 +4156,32 @@ void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txi
{
}
//----------------------------------------------------------------------------------------------------
+boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char *reason)
+{
+ // can't ask for password from a background thread
+ if (!m_in_manual_refresh.load(std::memory_order_relaxed))
+ {
+ message_writer(console_color_red, false) << tr("Password needed - use the refresh command");
+ m_cmd_binder.print_prompt();
+ return boost::none;
+ }
+
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
+ std::string msg = tr("Enter password");
+ if (reason && *reason)
+ msg += std::string(" (") + reason + ")";
+ auto pwd_container = tools::password_container::prompt(false, msg.c_str());
+ if (!pwd_container)
+ {
+ MERROR("Failed to read password");
+ return boost::none;
+ }
+
+ return pwd_container->password();
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init)
{
if (!try_connect_to_daemon(is_init))
@@ -4061,7 +4205,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init
{
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
- m_wallet->refresh(is_daemon_trusted(), start_height, fetched_blocks);
+ m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks);
ok = true;
// Clear line "Height xxx of xxx"
std::cout << "\r \r";
@@ -4352,7 +4496,7 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
{
- if (!is_daemon_trusted())
+ if (!m_wallet->is_trusted_daemon())
{
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
return true;
@@ -4513,11 +4657,10 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_)
{
// "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
if (!try_connect_to_daemon())
return true;
- LOCK_IDLE_SCOPE();
+ SCOPED_WALLET_UNLOCK();
std::vector<std::string> local_args = args_;
@@ -4701,16 +4844,16 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
return true;
}
unlock_block = bc_height + locked_blocks;
- ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted());
+ ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
break;
case TransferNew:
- ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted());
+ ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
break;
default:
LOG_ERROR("Unknown transfer method, using original");
/* FALLTHRU */
case TransferOriginal:
- ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, is_daemon_trusted());
+ ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra);
break;
}
@@ -4886,7 +5029,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
catch (const std::exception &e)
{
- handle_transfer_exception(std::current_exception(), is_daemon_trusted());
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
}
catch (...)
{
@@ -4920,15 +5063,15 @@ bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
{
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
if (!try_connect_to_daemon())
return true;
- LOCK_IDLE_SCOPE();
+ SCOPED_WALLET_UNLOCK();
+
try
{
// figure out what tx will be necessary
- auto ptx_vector = m_wallet->create_unmixable_sweep_transactions(is_daemon_trusted());
+ auto ptx_vector = m_wallet->create_unmixable_sweep_transactions();
if (ptx_vector.empty())
{
@@ -5007,13 +5150,13 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
{
try
{
- m_wallet->discard_unmixable_outputs(is_daemon_trusted());
+ m_wallet->discard_unmixable_outputs();
} catch (...) {}
}
}
catch (const std::exception &e)
{
- handle_transfer_exception(std::current_exception(), is_daemon_trusted());
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
}
catch (...)
{
@@ -5037,7 +5180,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
if (!try_connect_to_daemon())
return true;
@@ -5201,12 +5343,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
}
}
- LOCK_IDLE_SCOPE();
+ SCOPED_WALLET_UNLOCK();
try
{
// figure out what tx will be necessary
- auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted());
+ auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
if (ptx_vector.empty())
{
@@ -5290,7 +5432,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
}
catch (const std::exception& e)
{
- handle_transfer_exception(std::current_exception(), is_daemon_trusted());
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
}
catch (...)
{
@@ -5303,7 +5445,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
{
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+ SCOPED_WALLET_UNLOCK();
if (!try_connect_to_daemon())
return true;
@@ -5419,7 +5561,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
try
{
// figure out what tx will be necessary
- auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, is_daemon_trusted());
+ auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra);
if (ptx_vector.empty())
{
@@ -5489,7 +5631,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
}
catch (const std::exception& e)
{
- handle_transfer_exception(std::current_exception(), is_daemon_trusted());
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
}
catch (...)
{
@@ -5729,7 +5871,8 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
fail_msg_writer() << tr("usage: sign_transfer [export_raw]");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ SCOPED_WALLET_UNLOCK();
const bool export_raw = args_.size() == 1;
std::vector<tools::wallet2::pending_tx> ptx;
@@ -5794,7 +5937,7 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_)
}
catch (const std::exception& e)
{
- handle_transfer_exception(std::current_exception(), is_daemon_trusted());
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
}
catch (...)
{
@@ -5818,7 +5961,6 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
fail_msg_writer() << tr("usage: get_tx_key <txid>");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
crypto::hash txid;
if (!epee::string_tools::hex_to_pod(local_args[0], txid))
@@ -5827,7 +5969,7 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
return true;
}
- LOCK_IDLE_SCOPE();
+ SCOPED_WALLET_UNLOCK();
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
@@ -5932,7 +6074,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+ SCOPED_WALLET_UNLOCK();
try
{
@@ -6147,7 +6289,7 @@ bool simple_wallet::get_spend_proof(const std::vector<std::string> &args)
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+ SCOPED_WALLET_UNLOCK();
try
{
@@ -6242,9 +6384,7 @@ bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args)
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
-
- LOCK_IDLE_SCOPE();
+ SCOPED_WALLET_UNLOCK();
try
{
@@ -6345,7 +6485,7 @@ static std::string get_human_readable_timespan(std::chrono::seconds seconds)
if (ts < 3600 * 24 * 30.5)
return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days");
if (ts < 3600 * 24 * 365.25)
- return std::to_string((uint64_t)(ts / (3600 * 24 * 365.25))) + tr(" months");
+ return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months");
return tr("a long time");
}
//----------------------------------------------------------------------------------------------------
@@ -6497,6 +6637,9 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
if (pool) {
try
{
+ m_in_manual_refresh.store(true, std::memory_order_relaxed);
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
+
m_wallet->update_pool_state();
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
@@ -6680,6 +6823,14 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
{
+ message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain.");
+ message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc");
+ std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): "));
+ if(!std::cin.eof())
+ {
+ if (!command_line::is_yes(confirm))
+ return true;
+ }
return refresh_main(0, true);
}
//----------------------------------------------------------------------------------------------------
@@ -6699,7 +6850,7 @@ void simple_wallet::wallet_idle_thread()
{
uint64_t fetched_blocks;
if (try_connect_to_daemon(true))
- m_wallet->refresh(is_daemon_trusted(), 0, fetched_blocks);
+ m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, fetched_blocks);
}
catch(...) {}
m_auto_refresh_refreshing = false;
@@ -7345,7 +7496,8 @@ bool simple_wallet::sign(const std::vector<std::string> &args)
fail_msg_writer() << tr("This wallet is multisig and cannot sign");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ SCOPED_WALLET_UNLOCK();
std::string filename = args[0];
std::string data;
bool r = epee::file_io_utils::load_file_to_string(filename, data);
@@ -7414,14 +7566,14 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args)
fail_msg_writer() << tr("wallet is watch-only and cannot export key images");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ SCOPED_WALLET_UNLOCK();
std::string filename = args[0];
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
return true;
try
{
- LOCK_IDLE_SCOPE();
if (!m_wallet->export_key_images(filename))
{
fail_msg_writer() << tr("failed to save file ") << filename;
@@ -7446,7 +7598,7 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
fail_msg_writer() << tr("command not supported by HW wallet");
return true;
}
- if (!is_daemon_trusted())
+ if (!m_wallet->is_trusted_daemon())
{
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
return true;
@@ -7493,12 +7645,12 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args)
fail_msg_writer() << tr("usage: export_outputs <filename>");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ SCOPED_WALLET_UNLOCK();
std::string filename = args[0];
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
return true;
- LOCK_IDLE_SCOPE();
try
{
std::string data = m_wallet->export_outputs_to_str();
@@ -7544,7 +7696,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args)
try
{
- LOCK_IDLE_SCOPE();
+ SCOPED_WALLET_UNLOCK();
size_t n_outputs = m_wallet->import_outputs_from_str(data);
success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
}
@@ -7777,8 +7929,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_restore_multisig_wallet );
command_line::add_arg(desc_params, arg_non_deterministic );
command_line::add_arg(desc_params, arg_electrum_seed );
- command_line::add_arg(desc_params, arg_trusted_daemon);
- command_line::add_arg(desc_params, arg_untrusted_daemon);
command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version);
command_line::add_arg(desc_params, arg_restore_height);
command_line::add_arg(desc_params, arg_do_not_relay);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 472ad0c00..bfbe633ac 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -44,6 +44,7 @@
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "wallet/wallet2.h"
#include "console_handler.h"
+#include "wipeable_string.h"
#include "common/i18n.h"
#include "common/password.h"
#include "crypto/crypto.h" // for definition of crypto::secret_key
@@ -96,7 +97,7 @@ namespace cryptonote
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
- const std::string &multisig_keys, const std::string &old_language);
+ const epee::wipeable_string &multisig_keys, const std::string &old_language);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();
@@ -232,13 +233,12 @@ namespace cryptonote
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
std::string get_prompt() const;
bool print_seed(bool encrypted);
- bool is_daemon_trusted() const { return *m_trusted_daemon; }
/*!
* \brief Prints the seed with a nice message
* \param seed seed to print
*/
- void print_seed(std::string seed);
+ void print_seed(const epee::wipeable_string &seed);
/*!
* \brief Gets the word seed language from the user.
@@ -261,6 +261,7 @@ namespace cryptonote
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);
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
+ virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason);
//----------------------------------------------------------
friend class refresh_progress_reporter_t;
@@ -329,13 +330,12 @@ namespace cryptonote
std::string m_import_path;
std::string m_subaddress_lookahead;
- std::string m_electrum_seed; // electrum-style seed parameter
+ epee::wipeable_string m_electrum_seed; // electrum-style seed parameter
crypto::secret_key m_recovery_key; // recovery key (used as random for wallet gen)
bool m_restore_deterministic_wallet; // recover flag
bool m_restore_multisig_wallet; // recover flag
bool m_non_deterministic; // old 2-random generation
- boost::optional<bool> m_trusted_daemon;
bool m_allow_mismatched_daemon_version;
bool m_restoring; // are we restoring, by whatever method?
uint64_t m_restore_height; // optional
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index b48bf07e0..7b4ad27e4 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -369,7 +369,6 @@ void Wallet::error(const std::string &category, const std::string &str) {
WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
:m_wallet(nullptr)
, m_status(Wallet::Status_Ok)
- , m_trustedDaemon(false)
, m_wallet2Callback(nullptr)
, m_recoveringFromSeed(false)
, m_recoveringFromDevice(false)
@@ -733,10 +732,10 @@ bool WalletImpl::close(bool store)
std::string WalletImpl::seed() const
{
- std::string seed;
+ epee::wipeable_string seed;
if (m_wallet)
m_wallet->get_seed(seed);
- return seed;
+ return std::string(seed.data(), seed.size()); // TODO
}
std::string WalletImpl::getSeedLanguage() const
@@ -1358,7 +1357,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
dsts.push_back(de);
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */,
adjusted_priority,
- extra, subaddr_account, subaddr_indices, m_trustedDaemon);
+ extra, subaddr_account, subaddr_indices);
} else {
// for the GUI, sweep_all (i.e. amount set as "(all)") will always sweep all the funds in all the addresses
if (subaddr_indices.empty())
@@ -1368,7 +1367,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
}
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */,
adjusted_priority,
- extra, subaddr_account, subaddr_indices, m_trustedDaemon);
+ extra, subaddr_account, subaddr_indices);
}
if (multisig().isMultisig) {
@@ -1454,7 +1453,7 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
do {
try {
- transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(m_trustedDaemon);
+ transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions();
} catch (const tools::error::daemon_busy&) {
// TODO: make it translatable with "tr"?
@@ -1891,12 +1890,12 @@ Wallet::ConnectionStatus WalletImpl::connected() const
void WalletImpl::setTrustedDaemon(bool arg)
{
- m_trustedDaemon = arg;
+ m_wallet->set_trusted_daemon(arg);
}
bool WalletImpl::trustedDaemon() const
{
- return m_trustedDaemon;
+ return m_wallet->is_trusted_daemon();
}
bool WalletImpl::watchOnly() const
@@ -2032,7 +2031,8 @@ bool WalletImpl::isNewWallet() const
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
{
- if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
+ // claim RPC so there's no in-memory encryption for now
+ if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
return false;
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 9218d3ad5..0f3b1ce04 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -219,7 +219,6 @@ private:
mutable std::string m_errorString;
std::string m_password;
TransactionHistoryImpl * m_history;
- bool m_trustedDaemon;
Wallet2CallbackImpl * m_wallet2Callback;
AddressBookImpl * m_addressBook;
SubaddressImpl * m_subaddress;
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 401ada61b..2072495cd 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -146,7 +146,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get hard fork status");
- m_earliest_height[version] = resp_t.enabled ? resp_t.earliest_height : std::numeric_limits<uint64_t>::max();
+ m_earliest_height[version] = resp_t.earliest_height;
}
earliest_height = m_earliest_height[version];
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 9deaad09b..a2e36706e 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -89,6 +89,7 @@ using namespace cryptonote;
// arbitrary, used to generate different hashes from the same input
#define CHACHA8_KEY_TAIL 0x8c
+#define CACHE_KEY_TAIL 0x8d
#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\004"
#define SIGNED_TX_PREFIX "Monero signed tx set\004"
@@ -121,8 +122,6 @@ using namespace cryptonote;
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
-std::atomic<unsigned int> tools::wallet2::key_ref::refs(0);
-
namespace
{
std::string get_default_ringdb_path()
@@ -141,6 +140,8 @@ namespace
struct options {
const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
+ const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false};
+ const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true};
const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true};
const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 18081"), 0};
@@ -197,7 +198,7 @@ std::string get_size_string(const cryptonote::blobdata &tx)
return get_size_string(tx.size());
}
-std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
@@ -237,8 +238,29 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
+ boost::optional<bool> trusted_daemon;
+ if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon))
+ trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon);
+ THROW_WALLET_EXCEPTION_IF(!command_line::is_arg_defaulted(vm, opts.trusted_daemon) && !command_line::is_arg_defaulted(vm, opts.untrusted_daemon),
+ tools::error::wallet_internal_error, tools::wallet2::tr("--trusted-daemon and --untrusted-daemon are both seen, assuming untrusted"));
+
+ // set --trusted-daemon if local and not overridden
+ if (!trusted_daemon)
+ {
+ try
+ {
+ trusted_daemon = false;
+ if (tools::is_local_address(daemon_address))
+ {
+ MINFO(tr("Daemon is local, assuming trusted"));
+ trusted_daemon = true;
+ }
+ }
+ catch (const std::exception &e) { }
+ }
+
std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds));
- wallet->init(std::move(daemon_address), std::move(login));
+ wallet->init(rpc, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
return wallet;
@@ -273,7 +295,7 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
}
-std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
@@ -411,7 +433,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error,
tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
- wallet.reset(make_basic(vm, opts, password_prompter).release());
+ wallet.reset(make_basic(vm, rpc, opts, password_prompter).release());
wallet->set_refresh_from_block_height(field_scan_from_height);
wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
@@ -648,11 +670,40 @@ const size_t MAX_SPLIT_ATTEMPTS = 30;
constexpr const std::chrono::seconds wallet2::rpc_timeout;
const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); }
+wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
+ w(w),
+ locked(password != boost::none)
+{
+ if (!locked || w.is_rpc())
+ return;
+ const epee::wipeable_string pass = password->password();
+ w.generate_chacha_key_from_password(pass, key);
+ w.decrypt_keys(key);
+}
+
+wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password):
+ w(w),
+ locked(locked)
+{
+ if (!locked)
+ return;
+ w.generate_chacha_key_from_password(password, key);
+ w.decrypt_keys(key);
+}
+
+wallet_keys_unlocker::~wallet_keys_unlocker()
+{
+ if (!locked)
+ return;
+ w.encrypt_keys(key);
+}
+
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
m_run(true),
m_callback(0),
+ m_trusted_daemon(false),
m_nettype(nettype),
m_always_confirm_transfers(true),
m_print_ring_members(false),
@@ -693,7 +744,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
m_key_on_device(false),
m_ring_history_saved(false),
m_ringdb(),
- m_last_block_reward(0)
+ m_last_block_reward(0),
+ m_encrypt_keys_after_refresh(boost::none),
+ m_rpc(false)
{
}
@@ -716,6 +769,8 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
const options opts{};
command_line::add_arg(desc_params, opts.daemon_address);
command_line::add_arg(desc_params, opts.daemon_host);
+ command_line::add_arg(desc_params, opts.trusted_daemon);
+ command_line::add_arg(desc_params, opts.untrusted_daemon);
command_line::add_arg(desc_params, opts.password);
command_line::add_arg(desc_params, opts.password_file);
command_line::add_arg(desc_params, opts.daemon_port);
@@ -726,14 +781,14 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.kdf_rounds);
}
-std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- return generate_from_json(json_file, vm, opts, password_prompter);
+ return generate_from_json(json_file, vm, rpc, opts, password_prompter);
}
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
- const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+ const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
auto pwd = get_password(vm, opts, password_prompter, false);
@@ -741,7 +796,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
{
return {nullptr, password_container{}};
}
- auto wallet = make_basic(vm, opts, password_prompter);
+ auto wallet = make_basic(vm, rpc, opts, password_prompter);
if (wallet)
{
wallet->load(wallet_file, pwd->password());
@@ -749,7 +804,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
return {std::move(wallet), std::move(*pwd)};
}
-std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
+std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
auto pwd = get_password(vm, opts, password_prompter, true);
@@ -757,18 +812,19 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const
{
return {nullptr, password_container{}};
}
- return {make_basic(vm, opts, password_prompter), std::move(*pwd)};
+ return {make_basic(vm, rpc, opts, password_prompter), std::move(*pwd)};
}
-std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- return make_basic(vm, opts, password_prompter);
+ return make_basic(vm, rpc, opts, password_prompter);
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl)
+bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl, bool trusted_daemon)
{
+ m_rpc = rpc;
m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected())
m_http_client.disconnect();
@@ -776,6 +832,7 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::
m_upper_transaction_size_limit = upper_transaction_size_limit;
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
+ m_trusted_daemon = trusted_daemon;
// When switching from light wallet to full wallet, we need to reset the height we got from lw node.
return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl);
}
@@ -785,11 +842,10 @@ bool wallet2::is_deterministic() const
crypto::secret_key second;
keccak((uint8_t *)&get_account().get_keys().m_spend_secret_key, sizeof(crypto::secret_key), (uint8_t *)&second, sizeof(crypto::secret_key));
sc_reduce32((uint8_t *)&second);
- bool keys_deterministic = memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
- return keys_deterministic;
+ return memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase) const
+bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase) const
{
bool keys_deterministic = is_deterministic();
if (!keys_deterministic)
@@ -815,7 +871,7 @@ bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string
return true;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase, bool raw) const
+bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase, bool raw) const
{
bool ready;
uint32_t threshold, total;
@@ -838,7 +894,7 @@ bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &
crypto::secret_key skey;
crypto::public_key pkey;
const account_keys &keys = get_account().get_keys();
- std::string data;
+ epee::wipeable_string data;
data.append((const char*)&threshold, sizeof(uint32_t));
data.append((const char*)&total, sizeof(uint32_t));
skey = keys.m_spend_secret_key;
@@ -864,7 +920,7 @@ bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &
if (raw)
{
- seed = epee::string_tools::buff_to_hex_nodelimer(data);
+ seed = epee::to_hex::wipeable_string({(const unsigned char*)data.data(), data.size()});
}
else
{
@@ -1103,9 +1159,25 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs) const
+void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs)
{
THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
+
+ // if keys are encrypted, ask for password
+ if (m_ask_password && !m_rpc && !m_watch_only && !m_multisig_rescan_k)
+ {
+ static critical_section password_lock;
+ CRITICAL_REGION_LOCAL(password_lock);
+ if (!m_encrypt_keys_after_refresh)
+ {
+ boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password("output received");
+ THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero"));
+ THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero"));
+ decrypt_keys(*pwd);
+ m_encrypt_keys_after_refresh = *pwd;
+ }
+ }
+
if (m_multisig)
{
tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key;
@@ -1763,34 +1835,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
req.block_ids = short_chain_history;
- uint32_t rpc_version;
- boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
- // no error
- if (!!result)
- {
- // empty string -> not connection
- THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion");
- THROW_WALLET_EXCEPTION_IF(*result == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "getversion");
- if (*result != CORE_RPC_STATUS_OK)
- {
- MDEBUG("Cannot determine daemon RPC version, not asking for pruned blocks");
- req.prune = false; // old daemon
- }
- }
- else
- {
- if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 7))
- {
- MDEBUG("Daemon is recent enough, asking for pruned blocks");
- req.prune = true;
- }
- else
- {
- MDEBUG("Daemon is too old, not asking for pruned blocks");
- req.prune = false;
- }
- }
-
+ req.prune = true;
req.start_height = start_height;
req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
m_daemon_rpc_mutex.lock();
@@ -2063,6 +2108,14 @@ void wallet2::update_pool_state(bool refreshed)
{
MDEBUG("update_pool_state start");
+ auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
+ if (m_encrypt_keys_after_refresh)
+ {
+ encrypt_keys(*m_encrypt_keys_after_refresh);
+ m_encrypt_keys_after_refresh = boost::none;
+ }
+ });
+
// get the pool state
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res;
@@ -2369,8 +2422,6 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
//----------------------------------------------------------------------------------------------------
void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
{
- key_ref kref(*this);
-
if(m_light_wallet) {
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
@@ -2438,6 +2489,14 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// subsequent pulls in this refresh.
start_height = 0;
+ auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
+ if (m_encrypt_keys_after_refresh)
+ {
+ encrypt_keys(*m_encrypt_keys_after_refresh);
+ m_encrypt_keys_after_refresh = boost::none;
+ }
+ });
+
bool first = true;
while(m_run.load(std::memory_order_relaxed))
{
@@ -2507,6 +2566,12 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
throw std::runtime_error("proxy exception in refresh thread");
}
}
+ catch (const tools::error::password_needed&)
+ {
+ blocks_fetched += added_blocks;
+ waiter.wait(&tpool);
+ throw;
+ }
catch (const std::exception&)
{
blocks_fetched += added_blocks;
@@ -2591,6 +2656,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
req.amounts.push_back(0);
req.from_height = 0;
req.cumulative = true;
+ req.binary = true;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
@@ -2728,8 +2794,20 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
std::string multisig_signers;
cryptonote::account_base account = m_account;
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
+
+ if (m_ask_password && !m_rpc && !m_watch_only)
+ {
+ account.encrypt_viewkey(key);
+ account.decrypt_keys(key);
+ }
+
if (watch_only)
account.forget_spend_key();
+
+ account.encrypt_keys(key);
+
bool r = epee::serialization::store_t_to_binary(account, account_data);
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys");
wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
@@ -2846,6 +2924,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(m_subaddress_lookahead_minor);
json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
+ value2.SetUint(1);
+ json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -2853,7 +2934,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
account_data = buffer.GetString();
// Encrypt the entire JSON object.
- crypto::chacha_key key;
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
std::string cipher;
cipher.resize(account_data.size());
@@ -2871,6 +2951,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
return true;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::setup_keys(const epee::wipeable_string &password)
+{
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
+
+ // re-encrypt, but keep viewkey unencrypted
+ if (m_ask_password && !m_rpc && !m_watch_only)
+ {
+ m_account.encrypt_keys(key);
+ m_account.decrypt_viewkey(key);
+ }
+
+ static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
+ epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
+ memcpy(cache_key_data.data(), &key, HASH_SIZE);
+ cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL;
+ cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
+ get_ringdb_key();
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
+{
+ if (m_ask_password && !m_rpc && !m_watch_only)
+ decrypt_keys(original_password);
+ setup_keys(new_password);
+ rewrite(filename, new_password);
+ store();
+}
+//----------------------------------------------------------------------------------------------------
/*!
* \brief Load wallet information from wallet file.
* \param keys_file_name Name of wallet file
@@ -2881,6 +2990,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
rapidjson::Document json;
wallet2::keys_file_data keys_file_data;
std::string buf;
+ bool encrypted_secret_keys = false;
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name);
@@ -2926,6 +3036,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_key_on_device = false;
+ encrypted_secret_keys = false;
}
else if(json.IsObject())
{
@@ -3055,6 +3166,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
+ encrypted_secret_keys = field_encrypted_secret_keys;
}
else
{
@@ -3071,12 +3184,39 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_account.set_device(hwdev);
LOG_PRINT_L0("Device inited...");
}
+
+ if (r)
+ {
+ if (encrypted_secret_keys)
+ {
+ m_account.decrypt_keys(key);
+ }
+ else
+ {
+ // rewrite with encrypted keys, ignore errors
+ if (m_ask_password && !m_rpc && !m_watch_only)
+ encrypt_keys(key);
+ bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
+ if (!saved_ret)
+ {
+ // just moan a bit, but not fatal
+ MERROR("Error saving keys file with encrypted keys, not fatal");
+ }
+ if (m_ask_password && !m_rpc && !m_watch_only)
+ decrypt_keys(key);
+ m_keys_file_locker.reset();
+ }
+ }
const cryptonote::account_keys& keys = m_account.get_keys();
hw::device &hwdev = m_account.get_device();
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
if(!m_watch_only && !m_multisig)
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
+
+ if (r)
+ setup_keys(password);
+
return true;
}
@@ -3117,6 +3257,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
rapidjson::Document json;
wallet2::keys_file_data keys_file_data;
std::string buf;
+ bool encrypted_secret_keys = false;
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name);
@@ -3140,19 +3281,50 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
{
account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
json["key_data"].GetStringLength());
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
+ encrypted_secret_keys = field_encrypted_secret_keys;
}
cryptonote::account_base account_data_check;
r = epee::serialization::load_t_from_binary(account_data_check, account_data);
- const cryptonote::account_keys& keys = account_data_check.get_keys();
+ if (encrypted_secret_keys)
+ account_data_check.decrypt_keys(key);
+
+ const cryptonote::account_keys& keys = account_data_check.get_keys();
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
if(!no_spend_key)
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
return r;
}
+void wallet2::encrypt_keys(const crypto::chacha_key &key)
+{
+ m_account.encrypt_keys(key);
+ m_account.decrypt_viewkey(key);
+}
+
+void wallet2::decrypt_keys(const crypto::chacha_key &key)
+{
+ m_account.encrypt_viewkey(key);
+ m_account.decrypt_keys(key);
+}
+
+void wallet2::encrypt_keys(const epee::wipeable_string &password)
+{
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
+ encrypt_keys(key);
+}
+
+void wallet2::decrypt_keys(const epee::wipeable_string &password)
+{
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
+ decrypt_keys(key);
+}
+
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -3161,7 +3333,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
* \param create_address_file Whether to create an address file
*/
void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
- const std::string& multisig_data, bool create_address_file)
+ const epee::wipeable_string& multisig_data, bool create_address_file)
{
clear();
prepare_file_names(wallet_);
@@ -3217,6 +3389,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
for (const auto &msk: multisig_keys)
sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed);
+ memwipe(&skey, sizeof(rct::key));
m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
m_account.finalize_multisig(spend_public_key);
@@ -3227,6 +3400,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig_threshold = threshold;
m_multisig_signers = multisig_signers;
m_key_on_device = false;
+ setup_keys(password);
if (!wallet_.empty())
{
@@ -3281,6 +3455,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_key_on_device = false;
+ setup_keys(password);
// calculate a starting refresh height
if(m_refresh_from_block_height == 0 && !recover){
@@ -3382,6 +3557,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_key_on_device = false;
+ setup_keys(password);
if (!wallet_.empty())
{
@@ -3435,6 +3611,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_key_on_device = false;
+ setup_keys(password);
if (!wallet_.empty())
{
@@ -3481,6 +3658,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
+ setup_keys(password);
if (!wallet_.empty()) {
bool r = store_keys(m_keys_file, password, false);
@@ -3520,6 +3698,17 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
clear();
+ // decrypt keys
+ epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
+ if (m_ask_password && !m_rpc && !m_watch_only)
+ {
+ crypto::chacha_key chacha_key;
+ crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
+ m_account.encrypt_viewkey(chacha_key);
+ m_account.decrypt_keys(chacha_key);
+ keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
+ }
+
MINFO("Creating spend key...");
std::vector<crypto::secret_key> multisig_keys;
rct::key spend_pkey, spend_skey;
@@ -3563,6 +3752,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
MINFO("Creating multisig address...");
CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(view_skey, rct::rct2sk(spend_skey), rct::rct2pk(spend_pkey), multisig_keys),
"Failed to create multisig wallet due to bad keys");
+ memwipe(&spend_skey, sizeof(rct::key));
m_account_public_address = m_account.get_keys().m_account_address;
m_watch_only = false;
@@ -3580,6 +3770,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
m_multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
}
+ // re-encrypt keys
+ keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
+
if (!m_wallet_file.empty())
{
bool r = store_keys(m_keys_file, password, false);
@@ -3662,6 +3855,17 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
{
CHECK_AND_ASSERT_THROW_MES(!pkeys.empty(), "empty pkeys");
+ // keys are decrypted
+ epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
+ if (m_ask_password && !m_rpc && !m_watch_only)
+ {
+ crypto::chacha_key chacha_key;
+ crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
+ m_account.encrypt_viewkey(chacha_key);
+ m_account.decrypt_keys(chacha_key);
+ keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
+ }
+
// add ours if not included
crypto::public_key local_signer;
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, local_signer),
@@ -3684,6 +3888,9 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
m_multisig_signers = signers;
std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
+ // keys are encrypted again
+ keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
+
if (!m_wallet_file.empty())
{
bool r = store_keys(m_keys_file, password, false);
@@ -3995,6 +4202,11 @@ bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) cons
return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds);
}
//----------------------------------------------------------------------------------------------------
+void wallet2::generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const
+{
+ crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds);
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password)
{
clear();
@@ -4015,6 +4227,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
lock_keys_file();
+ wallet_keys_unlocker unlocker(*this, m_ask_password && !m_rpc && !m_watch_only, password);
+
//keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem
if(!boost::filesystem::exists(m_wallet_file, e) || e)
@@ -4036,11 +4250,9 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
r = ::serialization::parse_binary(buf, cache_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
std::string cache_data;
cache_data.resize(cache_file_data.cache_data.size());
- crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]);
try {
std::stringstream iss;
@@ -4048,11 +4260,13 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
boost::archive::portable_binary_iarchive ar(iss);
ar >> *this;
}
- catch (...)
+ catch(...)
{
- crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
- try
- {
+ // try with previous scheme: direct from keys
+ crypto::chacha_key key;
+ generate_chacha_key_from_secret_keys(key);
+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
+ try {
std::stringstream iss;
iss << cache_data;
boost::archive::portable_binary_iarchive ar(iss);
@@ -4060,13 +4274,24 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
}
catch (...)
{
- LOG_PRINT_L0("Failed to open portable binary, trying unportable");
- boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
- std::stringstream iss;
- iss.str("");
- iss << cache_data;
- boost::archive::binary_iarchive ar(iss);
- ar >> *this;
+ crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
+ try
+ {
+ std::stringstream iss;
+ iss << cache_data;
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> *this;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L0("Failed to open portable binary, trying unportable");
+ boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
+ std::stringstream iss;
+ iss.str("");
+ iss << cache_data;
+ boost::archive::binary_iarchive ar(iss);
+ ar >> *this;
+ }
}
}
}
@@ -4218,12 +4443,10 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
cache_file_data.cache_data = oss.str();
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
std::string cipher;
cipher.resize(cache_file_data.cache_data.size());
cache_file_data.iv = crypto::rand<crypto::chacha_iv>();
- crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]);
+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cipher[0]);
cache_file_data.cache_data = cipher;
const std::string new_file = same_file ? m_wallet_file + ".new" : path;
@@ -4681,7 +4904,7 @@ size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::v
// returns:
// direct return: amount of money found
// modified reference: selected_transfers, a list of iterators/indices of input sources
-uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon) const
+uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const
{
uint64_t found_money = 0;
selected_transfers.reserve(unused_transfers_indices.size());
@@ -4725,17 +4948,17 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo
//----------------------------------------------------------------------------------------------------
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx)
{
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx, trusted_daemon);
+ transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx);
}
//----------------------------------------------------------------------------------------------------
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, bool trusted_daemon)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra)
{
cryptonote::transaction tx;
pending_tx ptx;
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx, trusted_daemon);
+ transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx);
}
namespace {
@@ -5721,9 +5944,9 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
//
// this function will make multiple calls to wallet2::transfer if multiple
// transactions will be required
-std::vector<wallet2::pending_tx> wallet2::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> wallet2::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)
{
- const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true, trusted_daemon);
+ const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true);
const uint64_t fee_per_kb = get_per_kb_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
@@ -5757,7 +5980,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
do
{
- transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx, trusted_daemon);
+ transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx);
auto txBlob = t_serializable_object_to_blob(ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
} while (ptx.fee < needed_fee);
@@ -5861,12 +6084,6 @@ crypto::chacha_key wallet2::get_ringdb_key()
return *m_ringdb_key;
}
-void wallet2::clear_ringdb_key()
-{
- MINFO("clearing ringdb key");
- m_ringdb_key = boost::none;
-}
-
bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
{
if (!m_ringdb)
@@ -5877,7 +6094,6 @@ bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transac
bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
{
- key_ref kref(*this);
try { return add_rings(get_ringdb_key(), tx); }
catch (const std::exception &e) { return false; }
}
@@ -5886,7 +6102,6 @@ bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
{
if (!m_ringdb)
return false;
- key_ref kref(*this);
try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
catch (const std::exception &e) { return false; }
}
@@ -5924,7 +6139,6 @@ bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::
bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{
- key_ref kref(*this);
try { return get_ring(get_ringdb_key(), key_image, outs); }
catch (const std::exception &e) { return false; }
}
@@ -5934,7 +6148,6 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uin
if (!m_ringdb)
return false;
- key_ref kref(*this);
try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); }
catch (const std::exception &e) { return false; }
}
@@ -5946,7 +6159,6 @@ bool wallet2::find_and_save_rings(bool force)
if (!m_ringdb)
return false;
- key_ref kref(*this);
COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
@@ -6267,6 +6479,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
req_t.from_height = std::max<uint64_t>(segregation_fork_height, RECENT_OUTPUT_BLOCKS) - RECENT_OUTPUT_BLOCKS;
req_t.to_height = segregation_fork_height + 1;
req_t.cumulative = true;
+ req_t.binary = true;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout * 1000);
m_daemon_rpc_mutex.unlock();
@@ -7742,7 +7955,7 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image,
// This system allows for sending (almost) the entire balance, since it does
// not generate spurious change in all txes, thus decreasing the instantaneous
// usable balance.
-std::vector<wallet2::pending_tx> wallet2::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, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon)
+std::vector<wallet2::pending_tx> wallet2::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, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
{
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
@@ -8267,7 +8480,7 @@ skip_tx:
return ptx_vector;
}
-std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon)
+std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
@@ -8318,10 +8531,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
}
}
- return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
+ return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra);
}
-std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, 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> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
@@ -8339,10 +8552,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt
break;
}
}
- return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
+ return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra);
}
-std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, 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> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
{
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
@@ -8525,7 +8738,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
throw_on_rpc_response_error(result, "get_hard_fork_info");
- bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
+ bool close_enough = height >= earliest_height - early_blocks; // start using the rules that many blocks beforehand
if (close_enough)
LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
else
@@ -8576,12 +8789,12 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const
return vector;
}
//----------------------------------------------------------------------------------------------------
-std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon)
+std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct)
{
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock();
- if (trusted_daemon)
+ if (is_trusted_daemon())
req_t.amounts = get_unspent_amounts_vector();
req_t.min_count = count;
req_t.max_count = 0;
@@ -8642,21 +8855,21 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const
return m_transfers[idx];
}
//----------------------------------------------------------------------------------------------------
-std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_daemon)
+std::vector<size_t> wallet2::select_available_unmixable_outputs()
{
// request all outputs with less than 3 instances
const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6
- return select_available_outputs_from_histogram(min_mixin + 1, false, true, false, trusted_daemon);
+ return select_available_outputs_from_histogram(min_mixin + 1, false, true, false);
}
//----------------------------------------------------------------------------------------------------
-std::vector<size_t> wallet2::select_available_mixable_outputs(bool trusted_daemon)
+std::vector<size_t> wallet2::select_available_mixable_outputs()
{
// request all outputs with at least 3 instances, so we can use mixin 2 with
const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6
- return select_available_outputs_from_histogram(min_mixin + 1, true, true, true, trusted_daemon);
+ return select_available_outputs_from_histogram(min_mixin + 1, true, true, true);
}
//----------------------------------------------------------------------------------------------------
-std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bool trusted_daemon)
+std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
{
// From hard fork 1, we don't consider small amounts to be dust anymore
const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
@@ -8665,7 +8878,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
const uint64_t fee_per_kb = get_per_kb_fee();
// may throw
- std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(trusted_daemon);
+ std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
size_t num_dust_outputs = unmixable_outputs.size();
if (num_dust_outputs == 0)
@@ -8683,13 +8896,13 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
unmixable_transfer_outputs.push_back(n);
}
- return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>(), trusted_daemon);
+ return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>());
}
//----------------------------------------------------------------------------------------------------
-void wallet2::discard_unmixable_outputs(bool trusted_daemon)
+void wallet2::discard_unmixable_outputs()
{
// may throw
- std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(trusted_daemon);
+ std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
for (size_t idx : unmixable_outputs)
{
m_transfers[idx].m_spent = true;
@@ -10662,6 +10875,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
m_multisig_rescan_info = &info;
try
{
+
refresh(false);
}
catch (...) {}
@@ -10671,14 +10885,14 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
return n_outputs;
}
//----------------------------------------------------------------------------------------------------
-std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
+std::string wallet2::encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated) const
{
crypto::chacha_key key;
crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
std::string ciphertext;
crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
- ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
- crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
+ ciphertext.resize(len + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
+ crypto::chacha20(plaintext, len, key, iv, &ciphertext[sizeof(iv)]);
memcpy(&ciphertext[0], &iv, sizeof(iv));
if (authenticated)
{
@@ -10692,12 +10906,28 @@ std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_
return ciphertext;
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::encrypt(const epee::span<char> &plaintext, const crypto::secret_key &skey, bool authenticated) const
+{
+ return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
+}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
+{
+ return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
+}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated) const
+{
+ return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
+}
+//----------------------------------------------------------------------------------------------------
std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated) const
{
return encrypt(plaintext, get_account().get_keys().m_view_secret_key, authenticated);
}
//----------------------------------------------------------------------------------------------------
-std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
+template<typename T>
+T wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
{
const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0);
THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size,
@@ -10706,8 +10936,6 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
crypto::chacha_key key;
crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0];
- std::string plaintext;
- plaintext.resize(ciphertext.size() - prefix_size);
if (authenticated)
{
crypto::hash hash;
@@ -10718,10 +10946,14 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature),
error::wallet_internal_error, "Failed to authenticate ciphertext");
}
- crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
- return plaintext;
+ std::unique_ptr<char[]> buffer{new char[ciphertext.size() - prefix_size]};
+ auto wiper = epee::misc_utils::create_scope_leave_handler([&]() { memwipe(buffer.get(), ciphertext.size() - prefix_size); });
+ crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, buffer.get());
+ return T(buffer.get(), ciphertext.size() - prefix_size);
}
//----------------------------------------------------------------------------------------------------
+template epee::wipeable_string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const;
+//----------------------------------------------------------------------------------------------------
std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated) const
{
return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
@@ -10983,6 +11215,7 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
throw_on_rpc_response_error(result, "get_info");
uint64_t full_reward_zone = block_size_limit / 2;
+ THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block size limit from daemon");
std::vector<std::pair<uint64_t, uint64_t>> blocks;
for (const auto &fee_level: fee_levels)
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index d04156461..556679f51 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -67,6 +67,19 @@ class Serialization_portability_wallet_Test;
namespace tools
{
class ringdb;
+ class wallet2;
+
+ class wallet_keys_unlocker
+ {
+ public:
+ wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password);
+ wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password);
+ ~wallet_keys_unlocker();
+ private:
+ wallet2 &w;
+ bool locked;
+ crypto::chacha_key key;
+ };
class i_wallet2_callback
{
@@ -77,6 +90,7 @@ namespace tools
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) {}
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
+ virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason) { return boost::none; }
// Light wallet callbacks
virtual void on_lw_new_block(uint64_t height) {}
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
@@ -133,9 +147,11 @@ namespace tools
std::deque<crypto::hash> m_blockchain;
};
+ class wallet_keys_unlocker;
class wallet2
{
friend class ::Serialization_portability_wallet_Test;
+ friend class wallet_keys_unlocker;
public:
static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
@@ -153,17 +169,17 @@ namespace tools
static void init_options(boost::program_options::options_description& desc_params);
//! Uses stdin and stdout. Returns a wallet2 if no errors.
- static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
static std::pair<std::unique_ptr<wallet2>, password_container>
- make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ make_from_file(const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
- static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Just parses variables.
- static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
@@ -477,16 +493,6 @@ namespace tools
std::vector<is_out_data> additional;
};
- struct key_ref
- {
- key_ref(tools::wallet2 &w): wallet(w) { ++refs; }
- ~key_ref() { if (!--refs) wallet.clear_ringdb_key(); }
-
- private:
- tools::wallet2 &wallet;
- static std::atomic<unsigned int> refs;
- };
-
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -495,7 +501,7 @@ namespace tools
* \param create_address_file Whether to create an address file
*/
void generate(const std::string& wallet_, const epee::wipeable_string& password,
- const std::string& multisig_data, bool create_address_file = false);
+ const epee::wipeable_string& multisig_data, bool create_address_file = false);
/*!
* \brief Generates a wallet or restores one.
@@ -613,6 +619,11 @@ namespace tools
cryptonote::account_base& get_account(){return m_account;}
const cryptonote::account_base& get_account()const{return m_account;}
+ void encrypt_keys(const crypto::chacha_key &key);
+ void encrypt_keys(const epee::wipeable_string &password);
+ void decrypt_keys(const crypto::chacha_key &key);
+ void decrypt_keys(const epee::wipeable_string &password);
+
void set_refresh_from_block_height(uint64_t height) {m_refresh_from_block_height = height;}
uint64_t get_refresh_from_block_height() const {return m_refresh_from_block_height;}
@@ -625,19 +636,22 @@ namespace tools
// into account the current median block size rather than
// the minimum block size.
bool deinit();
- bool init(std::string daemon_address = "http://localhost:8080",
- boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false);
+ bool init(bool rpc, std::string daemon_address = "http://localhost:8080",
+ boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false, bool trusted_daemon = false);
void stop() { m_run.store(false, std::memory_order_relaxed); }
i_wallet2_callback* callback() const { return m_callback; }
void callback(i_wallet2_callback* callback) { m_callback = callback; }
+ bool is_trusted_daemon() const { return m_trusted_daemon; }
+ void set_trusted_daemon(bool trusted) { m_trusted_daemon = trusted; }
+
/*!
* \brief Checks if deterministic wallet
*/
bool is_deterministic() const;
- bool get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
+ bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
/*!
* \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned.
@@ -691,7 +705,7 @@ namespace tools
bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const;
bool has_multisig_partial_key_images() const;
bool has_unknown_key_images() const;
- bool get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
+ bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
bool key_on_device() const { return m_key_on_device; }
// locked & unlocked balance of given or current subaddress account
@@ -704,11 +718,11 @@ namespace tools
uint64_t balance_all() const;
uint64_t unlocked_balance_all() const;
template<typename T>
- void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, bool trusted_daemon);
+ void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy);
template<typename T>
- void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon);
- void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, bool trusted_daemon);
- void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon);
+ void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx);
+ void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra);
+ void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx);
template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
@@ -737,18 +751,18 @@ namespace tools
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
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);
bool 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::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, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose
- std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon);
- std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, 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_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, 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<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);
+ 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, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); // pass subaddr_indices by value on purpose
+ std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
+ std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
+ std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func);
bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids);
bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids);
- std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
- void discard_unmixable_outputs(bool trusted_daemon);
+ std::vector<pending_tx> create_unmixable_sweep_transactions();
+ void discard_unmixable_outputs();
bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
@@ -985,10 +999,10 @@ namespace tools
*/
uint64_t get_approximate_blockchain_height() const;
uint64_t estimate_blockchain_height();
- std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon);
+ std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct);
std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const;
- std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon);
- std::vector<size_t> select_available_mixable_outputs(bool trusted_daemon);
+ std::vector<size_t> select_available_unmixable_outputs();
+ std::vector<size_t> select_available_mixable_outputs();
size_t pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_dust_indices, const std::vector<size_t>& selected_transfers, bool smallest = false) const;
size_t pop_best_value(std::vector<size_t> &unused_dust_indices, const std::vector<size_t>& selected_transfers, bool smallest = false) const;
@@ -1054,9 +1068,12 @@ namespace tools
void update_pool_state(bool refreshed = false);
void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes);
+ std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const;
+ std::string encrypt(const epee::span<char> &span, const crypto::secret_key &skey, bool authenticated = true) const;
std::string encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const;
+ std::string encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const;
std::string encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated = true) const;
- std::string decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated = true) const;
+ template<typename T=std::string> T decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated = true) const;
std::string decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated = true) const;
std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const;
@@ -1074,6 +1091,8 @@ namespace tools
uint64_t adjust_mixin(uint64_t mixin) const;
uint32_t adjust_priority(uint32_t priority);
+ bool is_rpc() const { return m_rpc; }
+
// Light wallet specific functions
// fetch unspent outs from lw node and store in m_transfers
void light_wallet_get_unspent_outs();
@@ -1150,6 +1169,9 @@ namespace tools
bool lock_keys_file();
bool unlock_keys_file();
bool is_keys_file_locked() const;
+
+ void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password);
+
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1176,7 +1198,7 @@ namespace tools
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false);
void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error);
void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added);
- uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon) const;
+ uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const;
bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
@@ -1184,6 +1206,7 @@ namespace tools
void generate_genesis(cryptonote::block& b) const;
void check_genesis(const crypto::hash& genesis_hash) const; //throws
bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const;
+ void generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
@@ -1201,7 +1224,7 @@ namespace tools
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
- void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs) const;
+ void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs);
void trim_hashchain();
crypto::key_image get_multisig_composite_key_image(size_t n) const;
rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const;
@@ -1213,8 +1236,7 @@ namespace tools
bool remove_rings(const cryptonote::transaction_prefix &tx);
bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
crypto::chacha_key get_ringdb_key();
- void cache_ringdb_key();
- void clear_ringdb_key();
+ void setup_keys(const epee::wipeable_string &password);
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
@@ -1255,6 +1277,7 @@ namespace tools
boost::mutex m_daemon_rpc_mutex;
+ bool m_trusted_daemon;
i_wallet2_callback* m_callback;
bool m_key_on_device;
cryptonote::network_type m_nettype;
@@ -1317,6 +1340,11 @@ namespace tools
uint64_t m_last_block_reward;
std::unique_ptr<tools::file_locker> m_keys_file_locker;
+
+ crypto::chacha_key m_cache_key;
+ boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
+
+ bool m_rpc;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 25)
@@ -1793,16 +1821,16 @@ namespace tools
//----------------------------------------------------------------------------------------------------
template<typename T>
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, bool trusted_daemon)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy)
{
pending_tx ptx;
cryptonote::transaction tx;
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx, trusted_daemon);
+ transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx);
}
template<typename T>
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, bool trusted_daemon)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
@@ -1825,7 +1853,7 @@ namespace tools
// randomly select inputs for transaction
// throw if requested send amount is greater than (unlocked) amount available to send
std::vector<size_t> selected_transfers;
- uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers, trusted_daemon);
+ uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers);
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee);
uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index e80652750..243953280 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -53,6 +53,7 @@ namespace tools
// wallet_not_initialized
// multisig_export_needed
// multisig_import_needed
+ // password_needed
// std::logic_error
// wallet_logic_error *
// file_exists
@@ -209,6 +210,14 @@ namespace tools
}
};
//----------------------------------------------------------------------------------------------------
+ struct password_needed : public wallet_runtime_error
+ {
+ explicit password_needed(std::string&& loc, const std::string &msg = "Password needed")
+ : wallet_runtime_error(std::move(loc), msg)
+ {
+ }
+ };
+ //----------------------------------------------------------------------------------------------------
const char* const file_error_messages[] = {
"file already exists",
"file not found",
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 510cb3e58..67f26c7a7 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -59,7 +59,6 @@ namespace
{
const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"};
- const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false};
const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false};
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
@@ -100,7 +99,7 @@ namespace tools
}
//------------------------------------------------------------------------------------------------------------------------------
- wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false), m_restricted(false), m_vm(NULL)
+ wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_restricted(false), m_vm(NULL)
{
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -120,7 +119,7 @@ namespace tools
m_stop = false;
m_net_server.add_idle_handler([this](){
try {
- if (m_wallet) m_wallet->refresh(m_trusted_daemon);
+ if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon());
} catch (const std::exception& ex) {
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
}
@@ -163,21 +162,12 @@ namespace tools
walvars = m_wallet;
else
{
- tmpwal = tools::wallet2::make_dummy(*m_vm, password_prompter);
+ tmpwal = tools::wallet2::make_dummy(*m_vm, true, password_prompter);
walvars = tmpwal.get();
}
boost::optional<epee::net_utils::http::login> http_login{};
std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port);
const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login);
- m_trusted_daemon = command_line::get_arg(*m_vm, arg_trusted_daemon);
- if (!command_line::has_arg(*m_vm, arg_trusted_daemon))
- {
- if (tools::is_local_address(walvars->get_daemon_address()))
- {
- MINFO(tr("Daemon is local, assuming trusted"));
- m_trusted_daemon = true;
- }
- }
m_restricted = command_line::get_arg(*m_vm, arg_restricted);
if (command_line::has_arg(*m_vm, arg_wallet_dir))
{
@@ -857,7 +847,7 @@ namespace tools
mixin = m_wallet->adjust_mixin(req.mixin);
}
uint32_t priority = m_wallet->adjust_priority(req.priority);
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
if (ptx_vector.empty())
{
@@ -918,7 +908,7 @@ namespace tools
}
uint32_t priority = m_wallet->adjust_priority(req.priority);
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
@@ -1079,7 +1069,7 @@ namespace tools
try
{
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions();
return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
@@ -1127,7 +1117,7 @@ namespace tools
mixin = m_wallet->adjust_mixin(req.mixin);
}
uint32_t priority = m_wallet->adjust_priority(req.priority);
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
@@ -1183,7 +1173,7 @@ namespace tools
mixin = m_wallet->adjust_mixin(req.mixin);
}
uint32_t priority = m_wallet->adjust_priority(req.priority);
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra);
if (ptx_vector.empty())
{
@@ -1574,11 +1564,13 @@ namespace tools
if (req.key_type.compare("mnemonic") == 0)
{
- if (!m_wallet->get_seed(res.key))
+ epee::wipeable_string seed;
+ if (!m_wallet->get_seed(seed))
{
er.message = "The wallet is non-deterministic. Cannot display seed.";
return false;
}
+ res.key = std::string(seed.data(), seed.size()); // send to the network, then wipe RAM :D
}
else if(req.key_type.compare("view_key") == 0)
{
@@ -2302,7 +2294,7 @@ namespace tools
er.message = "Command unavailable in restricted mode.";
return false;
}
- if (!m_trusted_daemon)
+ if (!m_wallet->is_trusted_daemon())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "This command requires a trusted daemon.";
@@ -2505,6 +2497,28 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ try
+ {
+ m_wallet->refresh(m_wallet->is_trusted_daemon(), req.start_height, res.blocks_fetched, res.received_money);
+ return true;
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -2530,7 +2544,7 @@ namespace tools
bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
- if (!m_trusted_daemon)
+ if (!m_wallet->is_trusted_daemon())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "This command requires a trusted daemon.";
@@ -2636,7 +2650,7 @@ namespace tools
command_line::add_arg(desc, arg_password);
po::store(po::parse_command_line(argc, argv, desc), vm2);
}
- std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, nullptr).first;
+ std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, true, nullptr).first;
if (!wal)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -2710,7 +2724,7 @@ namespace tools
}
std::unique_ptr<tools::wallet2> wal = nullptr;
try {
- wal = tools::wallet2::make_from_file(vm2, wallet_file, nullptr).first;
+ wal = tools::wallet2::make_from_file(vm2, true, wallet_file, nullptr).first;
}
catch (const std::exception& e)
{
@@ -2728,6 +2742,38 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (m_wallet->verify_password(req.old_password))
+ {
+ try
+ {
+ m_wallet->rewrite(m_wallet->get_wallet_file(), req.new_password);
+ m_wallet->store();
+ LOG_PRINT_L0("Wallet password changed.");
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ }
+ else
+ {
+ er.code = WALLET_RPC_ERROR_CODE_INVALID_PASSWORD;
+ er.message = "Invalid original password.";
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code) {
try
{
@@ -2969,7 +3015,7 @@ namespace tools
return false;
}
- if (m_trusted_daemon)
+ if (m_wallet->is_trusted_daemon())
{
try
{
@@ -3192,7 +3238,6 @@ int main(int argc, char** argv) {
tools::wallet2::init_options(desc_params);
command_line::add_arg(desc_params, arg_rpc_bind_port);
command_line::add_arg(desc_params, arg_disable_rpc_login);
- command_line::add_arg(desc_params, arg_trusted_daemon);
command_line::add_arg(desc_params, arg_restricted);
cryptonote::rpc_args::init_options(desc_params);
command_line::add_arg(desc_params, arg_wallet_file);
@@ -3259,13 +3304,13 @@ int main(int argc, char** argv) {
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
if(!wallet_file.empty())
{
- wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompt).first;
+ wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first;
}
else
{
try
{
- wal = tools::wallet2::make_from_json(*vm, from_json, password_prompt);
+ wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt);
}
catch (const std::exception &e)
{
@@ -3285,7 +3330,7 @@ int main(int argc, char** argv) {
wal->stop();
});
- wal->refresh(command_line::get_arg(*vm, arg_trusted_daemon));
+ wal->refresh(wal->is_trusted_daemon());
// if we ^C during potentially length load/refresh, there's no server loop yet
if (quit)
{
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 25eb01ba9..7525b9dd0 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -126,12 +126,14 @@ namespace tools
MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY)
+ MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH)
MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT)
MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING)
MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING)
MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES)
MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET)
MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET)
+ MAP_JON_RPC_WE("change_wallet_password", on_change_wallet_password, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD)
MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG)
MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG)
MAP_JON_RPC_WE("make_multisig", on_make_multisig, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG)
@@ -200,12 +202,14 @@ namespace tools
bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er);
bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er);
bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er);
+ bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er);
bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er);
bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er);
bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er);
bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er);
bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er);
bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er);
+ bool on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er);
bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er);
@@ -236,7 +240,6 @@ namespace tools
std::string m_wallet_dir;
tools::private_file rpc_login_file;
std::atomic<bool> m_stop;
- bool m_trusted_daemon;
bool m_restricted;
const boost::program_options::variables_map *m_vm;
};
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 48d881c4c..62fb98ebb 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 2
+#define WALLET_RPC_VERSION_MINOR 3
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -445,7 +445,6 @@ namespace wallet_rpc
{
std::string tx_hash;
std::string tx_key;
- std::list<std::string> amount_keys;
uint64_t amount;
uint64_t fee;
std::string tx_blob;
@@ -456,7 +455,6 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
- KV_SERIALIZE(amount_keys)
KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
@@ -1714,6 +1712,29 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_REFRESH
+ {
+ struct request
+ {
+ uint64_t start_height;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(start_height, (uint64_t) 0)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ uint64_t blocks_fetched;
+ bool received_money;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(blocks_fetched);
+ KV_SERIALIZE(received_money);
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_START_MINING
{
struct request
@@ -1808,6 +1829,25 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_CHANGE_WALLET_PASSWORD
+ {
+ struct request
+ {
+ std::string old_password;
+ std::string new_password;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(old_password)
+ KV_SERIALIZE(new_password)
+ END_KV_SERIALIZE_MAP()
+ };
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_IS_MULTISIG
{
struct request
diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp
index c36c53b89..7248efade 100644
--- a/tests/functional_tests/transactions_flow_test.cpp
+++ b/tests/functional_tests/transactions_flow_test.cpp
@@ -86,7 +86,7 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor,
{
tools::wallet2::pending_tx ptx;
std::vector<size_t> indices = w1.select_available_outputs([](const tools::wallet2::transfer_details&) { return true; });
- w1.transfer(dsts, mix_in_factor, indices, 0, TEST_FEE, std::vector<uint8_t>(), tools::detail::null_split_strategy, tools::tx_dust_policy(TEST_DUST_THRESHOLD), tx, ptx, true);
+ w1.transfer(dsts, mix_in_factor, indices, 0, TEST_FEE, std::vector<uint8_t>(), tools::detail::null_split_strategy, tools::tx_dust_policy(TEST_DUST_THRESHOLD), tx, ptx);
w1.commit_tx(ptx);
return true;
}
@@ -138,7 +138,7 @@ bool transactions_flow_test(std::string& working_folder,
return false;
}
- w1.init(daemon_addr_a);
+ w1.init(true, daemon_addr_a);
uint64_t blocks_fetched = 0;
bool received_money;
@@ -149,7 +149,7 @@ bool transactions_flow_test(std::string& working_folder,
return false;
}
- w2.init(daemon_addr_b);
+ w2.init(true, daemon_addr_b);
MGINFO_GREEN("Using wallets: " << ENDL
<< "Source: " << w1.get_account().get_public_address_str(MAINNET) << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL
diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp
index 7f22757b2..6dadf960d 100644
--- a/tests/fuzz/signature.cpp
+++ b/tests/fuzz/signature.cpp
@@ -64,6 +64,7 @@ int SignatureFuzzer::init()
std::cerr << "failed to parse address" << std::endl;
return 1;
}
+ address = info.address;
}
catch (const std::exception &e)
{
diff --git a/tests/performance_tests/equality.h b/tests/performance_tests/equality.h
new file mode 100644
index 000000000..8d24d7da7
--- /dev/null
+++ b/tests/performance_tests/equality.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2014-2018, 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 <string.h>
+#include <sodium/crypto_verify_32.h>
+
+struct memcmp32
+{
+ static const size_t loop_count = 1000000000;
+ static int call(const unsigned char *k0, const unsigned char *k1){ return memcmp(k0, k1, 32); }
+};
+
+struct verify32
+{
+ static const size_t loop_count = 10000000;
+ static int call(const unsigned char *k0, const unsigned char *k1){ return crypto_verify_32(k0, k1); }
+};
+
+template<typename f, bool equal>
+class test_equality
+{
+public:
+ static const size_t loop_count = f::loop_count;
+
+ bool init()
+ {
+ for (int n = 0; n < 32; ++n)
+ k0[n] = n;
+ for (int n = 0; n < 32; ++n)
+ k1[n] = equal ? n : n + 1;
+ return true;
+ }
+
+ bool test()
+ {
+ return equal == !f::call(k0, k1);
+ }
+
+private:
+ unsigned char k0[32];
+ unsigned char k1[32];
+};
+
diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp
index bc3622ea8..1733e3409 100644
--- a/tests/performance_tests/main.cpp
+++ b/tests/performance_tests/main.cpp
@@ -51,6 +51,7 @@
#include "sc_reduce32.h"
#include "cn_fast_hash.h"
#include "rct_mlsag.h"
+#include "equality.h"
namespace po = boost::program_options;
@@ -151,6 +152,11 @@ int main(int argc, char** argv)
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, true);
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, true);
+ TEST_PERFORMANCE2(filter, test_equality, memcmp32, true);
+ TEST_PERFORMANCE2(filter, test_equality, memcmp32, false);
+ TEST_PERFORMANCE2(filter, test_equality, verify32, false);
+ TEST_PERFORMANCE2(filter, test_equality, verify32, false);
+
std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl;
return 0;
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index 3c7414640..076a4a873 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -27,6 +27,7 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(unit_tests_sources
+ account.cpp
apply_permutation.cpp
address_from_url.cpp
ban.cpp
@@ -47,11 +48,14 @@ set(unit_tests_sources
epee_levin_protocol_handler_async.cpp
epee_utils.cpp
fee.cpp
+ json_serialization.cpp
get_xtype_from_string.cpp
hashchain.cpp
http.cpp
+ keccak.cpp
main.cpp
memwipe.cpp
+ mlocker.cpp
mnemonics.cpp
mul_div.cpp
multisig.cpp
@@ -72,7 +76,8 @@ set(unit_tests_sources
ringct.cpp
output_selection.cpp
vercmp.cpp
- ringdb.cpp)
+ ringdb.cpp
+ wipeable_string.cpp)
set(unit_tests_headers
unit_tests_utils.h)
@@ -87,6 +92,7 @@ target_link_libraries(unit_tests
cryptonote_core
blockchain_db
rpc
+ serialization
wallet
p2p
version
diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp
new file mode 100644
index 000000000..113622b5e
--- /dev/null
+++ b/tests/unit_tests/account.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2014-2018, 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 "gtest/gtest.h"
+
+#include "cryptonote_basic/account.h"
+
+TEST(account, encrypt_keys)
+{
+ cryptonote::keypair recovery_key = cryptonote::keypair::generate(hw::get_device("default"));
+ cryptonote::account_base account;
+ crypto::secret_key key = account.generate(recovery_key.sec);
+ const cryptonote::account_keys keys = account.get_keys();
+
+ ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
+ ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
+ ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+ ASSERT_EQ(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
+
+ crypto::chacha_key chacha_key;
+ crypto::generate_chacha_key(&recovery_key, sizeof(recovery_key), chacha_key, 1);
+
+ account.encrypt_keys(chacha_key);
+
+ ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
+ ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
+ ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+
+ account.decrypt_viewkey(chacha_key);
+
+ ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
+ ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
+ ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+
+ account.encrypt_viewkey(chacha_key);
+
+ ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
+ ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
+ ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+
+ account.decrypt_keys(chacha_key);
+
+ ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
+ ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
+ ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+}
diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp
index 9e1680568..29fa88f9d 100644
--- a/tests/unit_tests/crypto.cpp
+++ b/tests/unit_tests/crypto.cpp
@@ -81,3 +81,18 @@ TEST(Crypto, null_keys)
ASSERT_EQ(memcmp(crypto::null_skey.data, zero, 32), 0);
ASSERT_EQ(memcmp(crypto::null_pkey.data, zero, 32), 0);
}
+
+TEST(Crypto, verify_32)
+{
+ // all bytes are treated the same, so we can brute force just one byte
+ unsigned char k0[32] = {0}, k1[32] = {0};
+ for (unsigned int i0 = 0; i0 < 256; ++i0)
+ {
+ k0[0] = i0;
+ for (unsigned int i1 = 0; i1 < 256; ++i1)
+ {
+ k1[0] = i1;
+ ASSERT_EQ(!crypto_verify_32(k0, k1), i0 == i1);
+ }
+ }
+}
diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp
index 930aeb782..8155c65f9 100644
--- a/tests/unit_tests/hardfork.cpp
+++ b/tests/unit_tests/hardfork.cpp
@@ -93,7 +93,6 @@ public:
virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; }
virtual uint64_t get_indexing_base() const { return 0; }
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return output_data_t(); }
- virtual output_data_t get_output_key(const uint64_t& global_index) const { return output_data_t(); }
virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); }
virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return tx_out_index(); }
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices) const {}
diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp
new file mode 100644
index 000000000..ac6b60846
--- /dev/null
+++ b/tests/unit_tests/json_serialization.cpp
@@ -0,0 +1,217 @@
+
+#include <boost/optional/optional.hpp>
+#include <boost/range/adaptor/indexed.hpp>
+#include <gtest/gtest.h>
+#include <rapidjson/document.h>
+#include <vector>
+
+#include "crypto/hash.h"
+#include "cryptonote_basic/account.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_core/cryptonote_tx_utils.h"
+#include "serialization/json_object.h"
+
+
+namespace
+{
+ cryptonote::transaction
+ make_miner_transaction(cryptonote::account_public_address const& to)
+ {
+ cryptonote::transaction tx{};
+ if (!cryptonote::construct_miner_tx(0, 0, 5000, 500, 500, to, tx))
+ throw std::runtime_error{"transaction construction error"};
+
+ crypto::hash id{0};
+ if (!cryptonote::get_transaction_hash(tx, id))
+ throw std::runtime_error{"could not get transaction hash"};
+
+ return tx;
+ }
+
+ cryptonote::transaction
+ make_transaction(
+ cryptonote::account_keys const& from,
+ std::vector<cryptonote::transaction> const& sources,
+ std::vector<cryptonote::account_public_address> const& destinations,
+ bool rct,
+ bool bulletproof)
+ {
+ std::uint64_t source_amount = 0;
+ std::vector<cryptonote::tx_source_entry> actual_sources;
+ for (auto const& source : sources)
+ {
+ std::vector<cryptonote::tx_extra_field> extra_fields;
+ if (!cryptonote::parse_tx_extra(source.extra, extra_fields))
+ throw std::runtime_error{"invalid transaction"};
+
+ cryptonote::tx_extra_pub_key key_field{};
+ if (!cryptonote::find_tx_extra_field_by_type(extra_fields, key_field))
+ throw std::runtime_error{"invalid transaction"};
+
+ for (auto const& input : boost::adaptors::index(source.vout))
+ {
+ source_amount += input.value().amount;
+ auto const& key = boost::get<cryptonote::txout_to_key>(input.value().target);
+
+ actual_sources.push_back(
+ {{}, 0, key_field.pub_key, {}, std::size_t(input.index()), input.value().amount, rct, rct::identity()}
+ );
+
+ for (unsigned ring = 0; ring < 10; ++ring)
+ actual_sources.back().push_output(input.index(), key.key, input.value().amount);
+ }
+ }
+
+ std::vector<cryptonote::tx_destination_entry> to;
+ for (auto const& destination : destinations)
+ to.push_back({(source_amount / destinations.size()), destination, false});
+
+ cryptonote::transaction tx{};
+
+ crypto::secret_key tx_key{};
+ std::vector<crypto::secret_key> extra_keys{};
+
+ std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
+ subaddresses[from.m_account_address.m_spend_public_key] = {0,0};
+
+ if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, bulletproof))
+ throw std::runtime_error{"transaction construction error"};
+
+ return tx;
+ }
+} // anonymous
+
+TEST(JsonSerialization, MinerTransaction)
+{
+ cryptonote::account_base acct;
+ acct.generate();
+ const auto miner_tx = make_miner_transaction(acct.get_keys().m_account_address);
+
+ crypto::hash tx_hash{};
+ ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx, tx_hash));
+
+ rapidjson::Document doc;
+ cryptonote::json::toJsonValue(doc, miner_tx, doc);
+
+ cryptonote::transaction miner_tx_copy;
+ cryptonote::json::fromJsonValue(doc, miner_tx_copy);
+
+ crypto::hash tx_copy_hash{};
+ ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx_copy, tx_copy_hash));
+ EXPECT_EQ(tx_hash, tx_copy_hash);
+
+ cryptonote::blobdata tx_bytes{};
+ cryptonote::blobdata tx_copy_bytes{};
+
+ ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(miner_tx, tx_bytes));
+ ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(miner_tx_copy, tx_copy_bytes));
+
+ EXPECT_EQ(tx_bytes, tx_copy_bytes);
+}
+
+TEST(JsonSerialization, RegularTransaction)
+{
+ cryptonote::account_base acct1;
+ acct1.generate();
+
+ cryptonote::account_base acct2;
+ acct2.generate();
+
+ const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address);
+ const auto tx = make_transaction(
+ acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, false, false
+ );
+
+ crypto::hash tx_hash{};
+ ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash));
+
+ rapidjson::Document doc;
+ cryptonote::json::toJsonValue(doc, tx, doc);
+
+ cryptonote::transaction tx_copy;
+ cryptonote::json::fromJsonValue(doc, tx_copy);
+
+ crypto::hash tx_copy_hash{};
+ ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash));
+ EXPECT_EQ(tx_hash, tx_copy_hash);
+
+ cryptonote::blobdata tx_bytes{};
+ cryptonote::blobdata tx_copy_bytes{};
+
+ ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes));
+ ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes));
+
+ EXPECT_EQ(tx_bytes, tx_copy_bytes);
+}
+
+TEST(JsonSerialization, RingctTransaction)
+{
+ cryptonote::account_base acct1;
+ acct1.generate();
+
+ cryptonote::account_base acct2;
+ acct2.generate();
+
+ const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address);
+ const auto tx = make_transaction(
+ acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, false
+ );
+
+ crypto::hash tx_hash{};
+ ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash));
+
+ rapidjson::Document doc;
+ cryptonote::json::toJsonValue(doc, tx, doc);
+
+ cryptonote::transaction tx_copy;
+ cryptonote::json::fromJsonValue(doc, tx_copy);
+
+ crypto::hash tx_copy_hash{};
+ ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash));
+ EXPECT_EQ(tx_hash, tx_copy_hash);
+
+ cryptonote::blobdata tx_bytes{};
+ cryptonote::blobdata tx_copy_bytes{};
+
+ ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes));
+ ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes));
+
+ EXPECT_EQ(tx_bytes, tx_copy_bytes);
+}
+
+TEST(JsonSerialization, BulletproofTransaction)
+{
+ cryptonote::account_base acct1;
+ acct1.generate();
+
+ cryptonote::account_base acct2;
+ acct2.generate();
+
+ const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address);
+ const auto tx = make_transaction(
+ acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, true
+ );
+
+ crypto::hash tx_hash{};
+ ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash));
+
+ rapidjson::Document doc;
+ cryptonote::json::toJsonValue(doc, tx, doc);
+
+ cryptonote::transaction tx_copy;
+ cryptonote::json::fromJsonValue(doc, tx_copy);
+
+ crypto::hash tx_copy_hash{};
+ ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash));
+ EXPECT_EQ(tx_hash, tx_copy_hash);
+
+ cryptonote::blobdata tx_bytes{};
+ cryptonote::blobdata tx_copy_bytes{};
+
+ ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes));
+ ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes));
+
+ EXPECT_EQ(tx_bytes, tx_copy_bytes);
+}
+
diff --git a/tests/unit_tests/keccak.cpp b/tests/unit_tests/keccak.cpp
new file mode 100644
index 000000000..4276b0e1d
--- /dev/null
+++ b/tests/unit_tests/keccak.cpp
@@ -0,0 +1,150 @@
+// Copyright (c) 2018, 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 "gtest/gtest.h"
+
+extern "C" {
+#include "crypto/keccak.h"
+}
+
+#define KECCAK_BLOCKLEN 136
+
+#define TEST_KECCAK(sz, chunks) \
+ std::string data; \
+ data.resize(sz); \
+ for (size_t i = 0; i < sz; ++i) \
+ data[i] = i * 17; \
+ uint8_t md0[32], md1[32]; \
+ keccak((const uint8_t*)data.data(), data.size(), md0, 32); \
+ KECCAK_CTX ctx; \
+ keccak_init(&ctx); \
+ size_t offset = 0; \
+ for (size_t i = 0; i < sizeof(chunks) / sizeof(chunks[0]); ++i) \
+ { \
+ ASSERT_TRUE(offset + chunks[i] <= data.size()); \
+ keccak_update(&ctx, (const uint8_t*)data.data() + offset, chunks[i]); \
+ offset += chunks[i]; \
+ } \
+ ASSERT_TRUE(offset == data.size()); \
+ keccak_finish(&ctx, md1); \
+ ASSERT_EQ(memcmp(md0, md1, 32), 0);
+
+TEST(keccak, )
+{
+}
+
+TEST(keccak, 0_and_0)
+{
+ static const size_t chunks[] = {0};
+ TEST_KECCAK(0, chunks);
+}
+
+TEST(keccak, 1_and_1)
+{
+ static const size_t chunks[] = {1};
+ TEST_KECCAK(1, chunks);
+}
+
+TEST(keccak, 1_and_0_1_0)
+{
+ static const size_t chunks[] = {0, 1, 0};
+ TEST_KECCAK(1, chunks);
+}
+
+TEST(keccak, 2_and_1_1)
+{
+ static const size_t chunks[] = {1, 1};
+ TEST_KECCAK(2, chunks);
+}
+
+TEST(keccak, 4_and_0_0_1_0_2_1_0)
+{
+ static const size_t chunks[] = {0, 0, 1, 0, 2, 1, 0};
+ TEST_KECCAK(4, chunks);
+}
+
+TEST(keccak, 15_and_1_14)
+{
+ static const size_t chunks[] = {1, 14};
+ TEST_KECCAK(15, chunks);
+}
+
+TEST(keccak, 135_and_134_1)
+{
+ static const size_t chunks[] = {134, 1};
+ TEST_KECCAK(135, chunks);
+}
+
+TEST(keccak, 135_and_135_0)
+{
+ static const size_t chunks[] = {135, 0};
+ TEST_KECCAK(135, chunks);
+}
+
+TEST(keccak, 135_and_0_135)
+{
+ static const size_t chunks[] = {0, 135};
+ TEST_KECCAK(135, chunks);
+}
+
+TEST(keccak, 136_and_135_1)
+{
+ static const size_t chunks[] = {135, 1};
+ TEST_KECCAK(136, chunks);
+}
+
+TEST(keccak, 136_and_136_0)
+{
+ static const size_t chunks[] = {136, 0};
+ TEST_KECCAK(136, chunks);
+}
+
+TEST(keccak, 136_and_0_136)
+{
+ static const size_t chunks[] = {0, 136};
+ TEST_KECCAK(136, chunks);
+}
+
+TEST(keccak, 136_and_136)
+{
+ static const size_t chunks[] = {136};
+ TEST_KECCAK(136, chunks);
+}
+
+TEST(keccak, 137_and_136_1)
+{
+ static const size_t chunks[] = {136, 1};
+ TEST_KECCAK(137, chunks);
+}
+
+TEST(keccak, 137_and_1_136)
+{
+ static const size_t chunks[] = {1, 136};
+ TEST_KECCAK(137, chunks);
+}
+
diff --git a/tests/unit_tests/mlocker.cpp b/tests/unit_tests/mlocker.cpp
new file mode 100644
index 000000000..6e6048c6c
--- /dev/null
+++ b/tests/unit_tests/mlocker.cpp
@@ -0,0 +1,186 @@
+// Copyright (c) 2018, 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 "gtest/gtest.h"
+
+#include "misc_log_ex.h"
+#include "mlocker.h"
+
+#define BASE(data) (char*)(((uintptr_t)(data.get() + page_size - 1)) / page_size * page_size)
+
+TEST(mlocker, distinct_1)
+{
+ const size_t page_size = epee::mlocker::get_page_size();
+ ASSERT_TRUE(page_size > 0);
+ const size_t base_pages = epee::mlocker::get_num_locked_pages();
+ const size_t base_objects = epee::mlocker::get_num_locked_objects();
+ std::unique_ptr<char[]> data{new char[8 * page_size]};
+ epee::mlocker *m0 = new epee::mlocker(BASE(data), 1);
+ epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, 1);
+ epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
+ delete m0;
+ delete m1;
+ delete m2;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+}
+
+TEST(mlocker, distinct_full_page)
+{
+ const size_t page_size = epee::mlocker::get_page_size();
+ ASSERT_TRUE(page_size > 0);
+ const size_t base_pages = epee::mlocker::get_num_locked_pages();
+ const size_t base_objects = epee::mlocker::get_num_locked_objects();
+ std::unique_ptr<char[]> data{new char[8 * page_size]};
+ epee::mlocker *m0 = new epee::mlocker(BASE(data), page_size);
+ epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, page_size);
+ epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, page_size);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
+ delete m0;
+ delete m1;
+ delete m2;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+}
+
+TEST(mlocker, identical)
+{
+ const size_t page_size = epee::mlocker::get_page_size();
+ ASSERT_TRUE(page_size >= 32);
+ const size_t base_pages = epee::mlocker::get_num_locked_pages();
+ const size_t base_objects = epee::mlocker::get_num_locked_objects();
+ std::unique_ptr<char[]> data{new char[8 * page_size]};
+ epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, 32);
+ epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size, 32);
+ epee::mlocker *m2 = new epee::mlocker(BASE(data) + page_size, 32);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
+ delete m1;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
+ delete m0;
+ delete m2;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+}
+
+TEST(mlocker, overlapping_small)
+{
+ const size_t page_size = epee::mlocker::get_page_size();
+ ASSERT_TRUE(page_size >= 64);
+ const size_t base_pages = epee::mlocker::get_num_locked_pages();
+ const size_t base_objects = epee::mlocker::get_num_locked_objects();
+ std::unique_ptr<char[]> data{new char[8 * page_size]};
+ epee::mlocker *m0 = new epee::mlocker(BASE(data), 32);
+ epee::mlocker *m1 = new epee::mlocker(BASE(data) + 16, 32);
+ epee::mlocker *m2 = new epee::mlocker(BASE(data) + 8, 32);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
+ delete m1;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
+ delete m2;
+ delete m0;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+}
+
+TEST(mlocker, multi_page)
+{
+ const size_t page_size = epee::mlocker::get_page_size();
+ ASSERT_TRUE(page_size > 0);
+ const size_t base_pages = epee::mlocker::get_num_locked_pages();
+ const size_t base_objects = epee::mlocker::get_num_locked_objects();
+ std::unique_ptr<char[]> data{new char[8 * page_size]};
+ epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, page_size * 3);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
+ epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size * 7, page_size);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 4);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
+ delete m0;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
+ delete m1;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+}
+
+TEST(mlocker, cross_page)
+{
+ const size_t page_size = epee::mlocker::get_page_size();
+ ASSERT_TRUE(page_size > 32);
+ const size_t base_pages = epee::mlocker::get_num_locked_pages();
+ const size_t base_objects = epee::mlocker::get_num_locked_objects();
+ std::unique_ptr<char[]> data{new char[2 * page_size]};
+ epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size - 1, 2);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 2);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
+ delete m0;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+}
+
+TEST(mlocker, redundant)
+{
+ const size_t page_size = epee::mlocker::get_page_size();
+ const size_t base_pages = epee::mlocker::get_num_locked_pages();
+ const size_t base_objects = epee::mlocker::get_num_locked_objects();
+ std::unique_ptr<char[]> data{new char[2 * page_size]};
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+ epee::mlocker *m0 = new epee::mlocker(BASE(data), 32);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
+ epee::mlocker *m1 = new epee::mlocker(BASE(data), 32);
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
+ delete m1;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
+ delete m0;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+}
+
+TEST(mlocker, mlocked)
+{
+ const size_t base_pages = epee::mlocker::get_num_locked_pages();
+ const size_t base_objects = epee::mlocker::get_num_locked_objects();
+ {
+ struct Foo { uint64_t u; };
+ epee::mlocked<Foo> l;
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
+ }
+ ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
+ ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
+}
diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp
index 8fa3192b9..0b74a6b94 100644
--- a/tests/unit_tests/mnemonics.cpp
+++ b/tests/unit_tests/mnemonics.cpp
@@ -27,6 +27,8 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "gtest/gtest.h"
+#include "wipeable_string.h"
+#include "mnemonics/language_base.h"
#include "mnemonics/electrum-words.h"
#include "crypto/crypto.h"
#include <stdlib.h>
@@ -74,14 +76,16 @@ namespace
void test_language(const Language::Base &language)
{
const std::vector<std::string> &word_list = language.get_word_list();
- std::string seed = "", return_seed = "";
+ epee::wipeable_string w_seed = "", w_return_seed = "";
+ std::string seed, return_seed;
// Generate a random seed without checksum
crypto::secret_key randkey;
for (size_t ii = 0; ii < sizeof(randkey); ++ii)
{
randkey.data[ii] = rand();
}
- crypto::ElectrumWords::bytes_to_words(randkey, seed, language.get_language_name());
+ crypto::ElectrumWords::bytes_to_words(randkey, w_seed, language.get_language_name());
+ seed = std::string(w_seed.data(), w_seed.size());
// remove the checksum word
const char *space = strrchr(seed.c_str(), ' ');
ASSERT_TRUE(space != NULL);
@@ -103,7 +107,8 @@ namespace
ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
// Convert the secret key back to seed
- crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name());
+ crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name());
+ return_seed = std::string(w_return_seed.data(), w_return_seed.size());
ASSERT_EQ(true, res);
std::cout << "Returned seed:\n";
std::cout << return_seed << std::endl;
@@ -126,8 +131,9 @@ namespace
std::cout << "Detected language: " << language_name << std::endl;
ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
- return_seed = "";
- crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name());
+ w_return_seed = "";
+ crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name());
+ return_seed = std::string(w_return_seed.data(), w_return_seed.size());
ASSERT_EQ(true, res);
std::cout << "Returned seed:\n";
std::cout << return_seed << std::endl;
@@ -202,3 +208,17 @@ TEST(mnemonics, language_detection_with_bad_checksum)
ASSERT_EQ(true, res);
ASSERT_STREQ(language_name.c_str(), "Português");
}
+
+TEST(mnemonics, utf8prefix)
+{
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 0) == "");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 1) == "f");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 2) == "fo");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 3) == "foo");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 4) == "foo");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 0) == "");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 1) == "æ");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 2) == "æo");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 3) == "æon");
+ ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 4) == "æon");
+}
diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp
index 922299333..eb453b960 100644
--- a/tests/unit_tests/multisig.cpp
+++ b/tests/unit_tests/multisig.cpp
@@ -61,10 +61,13 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet)
try
{
- wallet.init("");
+ wallet.init(false, "");
wallet.set_subaddress_lookahead(1, 1);
wallet.generate("", "", spendkey, true, false);
ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET));
+ wallet.decrypt_keys("");
+ ASSERT_TRUE(test_addresses[idx].spendkey == epee::string_tools::pod_to_hex(wallet.get_account().get_keys().m_spend_secret_key));
+ wallet.encrypt_keys("");
}
catch (const std::exception &e)
{
@@ -83,8 +86,12 @@ static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, un
std::vector<crypto::secret_key> sk0(1), sk1(1);
std::vector<crypto::public_key> pk0(1), pk1(1);
+ wallet0.decrypt_keys("");
std::string mi0 = wallet0.get_multisig_info();
+ wallet0.encrypt_keys("");
+ wallet1.decrypt_keys("");
std::string mi1 = wallet1.get_multisig_info();
+ wallet1.encrypt_keys("");
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
@@ -118,9 +125,15 @@ static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, to
std::vector<crypto::secret_key> sk0(2), sk1(2), sk2(2);
std::vector<crypto::public_key> pk0(2), pk1(2), pk2(2);
+ wallet0.decrypt_keys("");
std::string mi0 = wallet0.get_multisig_info();
+ wallet0.encrypt_keys("");
+ wallet1.decrypt_keys("");
std::string mi1 = wallet1.get_multisig_info();
+ wallet1.encrypt_keys("");
+ wallet2.decrypt_keys("");
std::string mi2 = wallet2.get_multisig_info();
+ wallet2.encrypt_keys("");
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1]));
diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp
index 8b0ea10d4..9b842569a 100644
--- a/tests/unit_tests/ringdb.cpp
+++ b/tests/unit_tests/ringdb.cpp
@@ -47,7 +47,7 @@ static crypto::chacha_key generate_chacha_key()
{
crypto::chacha_key chacha_key;
uint64_t password = crypto::rand<uint64_t>();
- crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key);
+ crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key, 1);
return chacha_key;
}
diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp
index 5bec280b1..2f7b5aac7 100644
--- a/tests/unit_tests/serialization.cpp
+++ b/tests/unit_tests/serialization.cpp
@@ -824,7 +824,7 @@ TEST(Serialization, portability_outputs)
return {};
}
crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
- return std::move(plaintext);
+ return plaintext;
};
crypto::secret_key view_secret_key;
epee::string_tools::hex_to_pod("339673bb1187e2f73ba7841ab6841c5553f96e9f13f8fe6612e69318db4e9d0a", view_secret_key);
diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp
new file mode 100644
index 000000000..5ea1c1729
--- /dev/null
+++ b/tests/unit_tests/wipeable_string.cpp
@@ -0,0 +1,204 @@
+// Copyright (c) 2018, 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 <boost/optional/optional.hpp>
+#include <string.h>
+#include "gtest/gtest.h"
+
+#include "misc_log_ex.h"
+#include "wipeable_string.h"
+
+TEST(wipeable_string, ctor)
+{
+ epee::wipeable_string s0;
+ ASSERT_EQ(s0.size(), 0);
+
+ epee::wipeable_string s1(std::string("foo"));
+ ASSERT_EQ(s1.size(), 3);
+ ASSERT_TRUE(!memcmp(s1.data(), "foo", s1.size()));
+
+ epee::wipeable_string s2(std::string("bar"));
+ ASSERT_EQ(s2.size(), 3);
+ ASSERT_TRUE(!memcmp(s2.data(), "bar", s2.size()));
+
+ epee::wipeable_string s3(std::string("quux"));
+ ASSERT_EQ(s3.size(), 4);
+ ASSERT_TRUE(!memcmp(s3.data(), "quux", s3.size()));
+}
+
+TEST(wipeable_string, wipe)
+{
+ epee::wipeable_string s0(std::string("foo"));
+ ASSERT_EQ(s0.size(), 3);
+ s0.wipe();
+ ASSERT_EQ(s0.size(), 3);
+ ASSERT_TRUE(!memcmp(s0.data(), "\0\0\0", 3));
+}
+
+TEST(wipeable_string, clear)
+{
+ epee::wipeable_string s0(std::string("foo"));
+ ASSERT_EQ(s0.size(), 3);
+ s0.clear();
+ ASSERT_EQ(s0.size(), 0);
+}
+
+TEST(wipeable_string, push_back)
+{
+ epee::wipeable_string s0(std::string("fo"));
+ ASSERT_EQ(s0.size(), 2);
+ s0.push_back('o');
+ ASSERT_EQ(s0.size(), 3);
+ ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size()));
+}
+
+TEST(wipeable_string, append_char)
+{
+ epee::wipeable_string s0(std::string("fo"));
+ ASSERT_EQ(s0.size(), 2);
+ s0 += 'o';
+ ASSERT_EQ(s0.size(), 3);
+ ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size()));
+}
+
+TEST(wipeable_string, append_string)
+{
+ epee::wipeable_string s0(std::string("foo"));
+ ASSERT_EQ(s0.size(), 3);
+ s0 += "bar";
+ ASSERT_EQ(s0.size(), 6);
+ ASSERT_TRUE(!memcmp(s0.data(), "foobar", s0.size()));
+}
+
+TEST(wipeable_string, empty)
+{
+ epee::wipeable_string s0;
+ ASSERT_TRUE(s0.empty());
+ s0.push_back(' ');
+ ASSERT_FALSE(s0.empty());
+ ASSERT_EQ(s0.pop_back(), ' ');
+ ASSERT_TRUE(s0.empty());
+}
+
+TEST(wipeable_string, pop_back)
+{
+ epee::wipeable_string s = "test";
+ ASSERT_EQ(s.size(), 4);
+ ASSERT_EQ(s.pop_back(), 't');
+ ASSERT_EQ(s.size(), 3);
+ ASSERT_TRUE(!memcmp(s.data(), "tes", s.size()));
+}
+
+TEST(wipeable_string, equal)
+{
+ epee::wipeable_string s0 = "foo";
+ epee::wipeable_string s1 = "bar";
+ epee::wipeable_string s0_2 = "foo";
+ ASSERT_TRUE(s0 == s0);
+ ASSERT_TRUE(s0 == s0_2);
+ ASSERT_TRUE(s1 == s1);
+ ASSERT_FALSE(s1 == s0);
+ ASSERT_FALSE(s1 == s0_2);
+}
+
+TEST(wipeable_string, not_equal)
+{
+ epee::wipeable_string s0 = "foo";
+ epee::wipeable_string s1 = "bar";
+ epee::wipeable_string s0_2 = "foo";
+ ASSERT_FALSE(s0 != s0);
+ ASSERT_FALSE(s0 != s0_2);
+ ASSERT_FALSE(s1 != s1);
+ ASSERT_TRUE(s1 != s0);
+ ASSERT_TRUE(s1 != s0_2);
+}
+
+static epee::wipeable_string trimmed(const char *s)
+{
+ epee::wipeable_string str(s);
+ str.trim();
+ return str;
+}
+
+TEST(wipeable_string, trim)
+{
+ ASSERT_TRUE(trimmed("") == "");
+ ASSERT_TRUE(trimmed(" ") == "");
+ ASSERT_TRUE(trimmed(" ") == "");
+ ASSERT_TRUE(trimmed("a") == "a");
+ ASSERT_TRUE(trimmed(" a") == "a");
+ ASSERT_TRUE(trimmed(" a") == "a");
+ ASSERT_TRUE(trimmed("a ") == "a");
+ ASSERT_TRUE(trimmed("a ") == "a");
+ ASSERT_TRUE(trimmed(" a ") == "a");
+ ASSERT_TRUE(trimmed(" a ") == "a");
+ ASSERT_TRUE(trimmed(" ab ") == "ab");
+ ASSERT_TRUE(trimmed(" a b ") == "a b");
+ ASSERT_TRUE(trimmed(" a b ") == "a b");
+}
+
+static bool check_split(const char *s, const std::vector<epee::wipeable_string> &v)
+{
+ epee::wipeable_string str(s);
+ std::vector<epee::wipeable_string> fields;
+ str.split(fields);
+ return v == fields;
+}
+
+TEST(wipeable_string, split)
+{
+ ASSERT_TRUE(check_split("", {}));
+ ASSERT_TRUE(check_split("foo", {"foo"}));
+ ASSERT_TRUE(check_split(" foo ", {"foo"}));
+ ASSERT_TRUE(check_split("foo bar", {"foo", "bar"}));
+ ASSERT_TRUE(check_split("foo bar", {"foo", "bar"}));
+ ASSERT_TRUE(check_split("foo bar baz", {"foo", "bar", "baz"}));
+ ASSERT_TRUE(check_split(" foo bar baz ", {"foo", "bar", "baz"}));
+ ASSERT_TRUE(check_split(" foo bar baz", {"foo", "bar", "baz"}));
+ ASSERT_TRUE(check_split("foo bar baz ", {"foo", "bar", "baz"}));
+}
+
+TEST(wipeable_string, parse_hexstr)
+{
+ boost::optional<epee::wipeable_string> s;
+
+ ASSERT_EQ(boost::none, epee::wipeable_string("x").parse_hexstr());
+ ASSERT_EQ(boost::none, epee::wipeable_string("x0000000000000000").parse_hexstr());
+ ASSERT_EQ(boost::none, epee::wipeable_string("0000000000000000x").parse_hexstr());
+ ASSERT_EQ(boost::none, epee::wipeable_string("0").parse_hexstr());
+ ASSERT_EQ(boost::none, epee::wipeable_string("000").parse_hexstr());
+
+ ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr()));
+ ASSERT_EQ(*s, "");
+ ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr()));
+ ASSERT_EQ(*s, epee::wipeable_string("", 1));
+ ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr()));
+ ASSERT_EQ(*s, epee::wipeable_string("A"));
+ ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr()));
+ ASSERT_EQ(*s, epee::wipeable_string("ABC"));
+}