diff options
56 files changed, 3516 insertions, 375 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ceebd49ef..d584e41c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,9 +80,10 @@ if (ARM_TEST STREQUAL "arm") endif() endif() -if (ARM_ID STREQUAL "aarch64" OR ARM_ID STREQUAL "arm64") +if (ARM_ID STREQUAL "aarch64" OR ARM_ID STREQUAL "arm64" OR ARM_ID STREQUAL "armv8-a") set(ARM 1) set(ARM8 1) + set(ARCH "armv8-a") endif() if(WIN32 OR ARM) diff --git a/CONTRIBUTING b/CONTRIBUTING.md index 55fcedccd..78d78f7bf 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING.md @@ -1,8 +1,17 @@ -A good way to help is to test, and report bugs. -See http://www.chiark.greenend.org.uk/~sgtatham/bugs.html if you -want to help that way. Testing is invaluable in making a piece +# Contributing to Monero + +A good way to help is to test, and report bugs. See +[How to Report Bugs Effectively (by Simon Tatham)](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) +if you want to help that way. Testing is invaluable in making a piece of software solid and usable. + +## General Guidelines + +* Comments are encouraged. +* If modifying code for which Doxygen headers exist, that header must be modified to match. +* Tests would be nice to have if you're adding functionality. + Patches are preferably to be sent via a github pull request. If that can't be done, patches in "git format-patch" format can be sent (eg, posted to fpaste.org with a long enough timeout and a link @@ -16,15 +25,12 @@ modifying is encourgaged. Proper squashing should be done (eg, if you're making a buggy patch, then a later patch to fix the bug, both patches should be merged). +## Commits and Pull Requests + Commit messages should be sensible. That means a subject line that describes the patch, with an optional longer body that gives details, documentation, etc. -Comments are encouraged. - -If modifying code for which Doxygen headers exist, that header must -be modified to match. - When submitting a pull request on github, make sure your branch is rebased. No merge commits nor stray commits from other people in your submitted branch, please. You may be asked to rebase if there @@ -32,5 +38,3 @@ are conflicts (even trivially resolvable ones). PGP signing commits is strongly encouraged. That should explain why the previous paragraph is here. - -Tests would be nice to have if you're adding functionality. @@ -50,7 +50,7 @@ This is the core implementation of Monero. It is open source and completely free As with many development projects, the repository on Github is considered to be the "staging" area for the latest changes. Before changes are merged into that branch on the main repository, they are tested by individual developers in their own branches, submitted as a pull request, and then subsequently tested by contributors who focus on testing and code reviews. That having been said, the repository should be carefully considered before using it in a production environment, unless there is a patch in the repository for a particular show-stopping issue you are experiencing. It is generally a better idea to use a tagged release for stability. -**Anyone is welcome to contribute to Monero's codebase!** If you have a fix or code change, feel free to submit is as a pull request directly to the "master" branch. In cases where the change is relatively small or does not affect other parts of the codebase it may be merged in immediately by any one of the collaborators. On the other hand, if the change is particularly large or complex, it is expected that it will be discussed at length either well in advance of the pull request being submitted, or even directly on the pull request. +**Anyone is welcome to contribute to Monero's codebase!** If you have a fix or code change, feel free to submit it as a pull request directly to the "master" branch. In cases where the change is relatively small or does not affect other parts of the codebase it may be merged in immediately by any one of the collaborators. On the other hand, if the change is particularly large or complex, it is expected that it will be discussed at length either well in advance of the pull request being submitted, or even directly on the pull request. ## Supporting the Project @@ -80,6 +80,14 @@ There are also several mining pools that kindly donate a portion of their fees, See [LICENSE](LICENSE). +# Contributing + +If you want to help out, see [CONTRIBUTING](CONTRIBUTING.md) for a set of guidelines. + +## Vulnerability Response Process + +See [Vulnerability Response Process](VULNERABILITY_RESPONSE_PROCESS.md). + ## Monero software updates and consensus protocol changes (hard fork schedule) Monero uses a fixed-schedule hard fork mechanism to implement new features. This means that users of Monero (end users and service providers) need to run current versions and update their software on a regular schedule. Here is the current schedule, versions, and compatibility. @@ -378,10 +386,6 @@ While monerod and monero-wallet-cli do not use readline directly, most of the fu Note: rlwrap will save things like your seed and private keys, if you supply them on prompt. You may want to not use rlwrap when you use simplewallet to restore from seed, etc. -# Contributing - -If you want to help out, see CONTRIBUTING for a set of guidelines. - # Debugging This section contains general instructions for debugging failed installs or problems encountered with Monero. First ensure you are running the latest version built from the github repo. diff --git a/VULNERABILITY_RESPONSE_PROCESS.md b/VULNERABILITY_RESPONSE_PROCESS.md new file mode 100644 index 000000000..eea3a06e7 --- /dev/null +++ b/VULNERABILITY_RESPONSE_PROCESS.md @@ -0,0 +1,143 @@ +# Monero Vulnerability Response Process + +## Preamble + +Researchers/Hackers: while you research/hack, we ask that you please refrain from committing the following: +- Denial of Service / Active exploiting against the network +- Social Engineering of Monero staff or contractors +- Any physical or electronic attempts against Monero community property and/or data centers + +## I. Point of Contacts for Security Issues + +``` +ric@getmonero.org +BDA6 BD70 42B7 21C4 67A9 759D 7455 C5E3 C0CD CEB9 + +luigi1111@getmonero.org +8777 AB8F 778E E894 87A2 F8E7 F4AC A018 3641 E010 + +moneromooo.monero@gmail.com +48B0 8161 FBDA DFE3 93AD FC3E 686F 0745 4D6C EFC3 +``` + +## II. Security Response Team + +- fluffypony +- luigi1111 +- moneromooo + +## III. Incident Response + +1. Researcher submits report via one or both of two methods: + - a. Email + - b. [HackerOne](https://hackerone.com/monero) + +2. Response Team designates a Response Manager who is in charge of the particular report based on availability and/or knowledge-set + +3. In no more than 3 working days, Response Team should gratefully respond to researcher using only encrypted, secure channels + +4. Response Manager makes inquiries to satisfy any needed information to confirm if submission is indeed a vulnerability + - a. If submission proves to be vulnerable, proceed to next step + - b. If not vulnerable: + - i. Response Manager responds with reasons why submission is not a vulnerability + - ii. Response Manager moves discussion to a new or existing ticket on GitHub if necessary + +5. If over email, Response Manager opens a HackerOne issue for new submission + +6. Establish severity of vulnerability: + - a. HIGH: impacts network as a whole, has potential to break entire network, results in the loss of monero, or is on a scale of great catastrophe + - b. MEDIUM: impacts individual nodes, wallets, or must be carefully exploited + - c. LOW: is not easily exploitable + +7. Respond according to the severity of the vulnerability: + - a. HIGH severities must be notified on website and reddit /r/Monero within 3 working days of classification + - i. The notification should list appropriate steps for users to take, if any + - ii. The notification must not include any details that could suggest an exploitation path + - iii. The latter takes precedence over the former + - b. MEDIUM and HIGH severities will require a Point Release + - c. LOW severities will be addressed in the next Regular Release + +8. Response Team applies appropriate patch(es) + - a. Response Manager designates a PRIVATE git "hotfix branch" to work in + - b. Patches are reviewed with the researcher + - c. Any messages associated with PUBLIC commits during the time of review should not make reference to the security nature of the PRIVATE branch or its commits + - d. Vulnerability announcement is drafted + - i. Include the severity of the vulnerability + - ii. Include all vulnerable systems/apps/code + - iii. Include solutions (if any) if patch cannot be applied + - e. Release date is discussed + +9. At release date, Response Team coordinates with developers to finalize update: + - a. Response Manager propagates the "hotfix branch" to trunk + - b. Response Manager includes vulnerability announcement draft in release notes + - c. Proceed with the Point or Regular Release + +## IV. Post-release Disclosure Process + +1. Response Team has 90 days to fulfill all points within section III + +2. If the Incident Response process in section III is successfully completed: + - a. Response Manager contacts researcher and asks if researcher wishes for credit + - b. Finalize vulnerability announcement draft and include the following: + - i. Project name and URL + - ii. Versions known to be affected + - iii. Versions known to be not affected (for example, the vulnerable code was introduced in a recent version, and older versions are therefore unaffected) + - iv. Versions not checked + - v. Type of vulnerability and its impact + - vi. If already obtained or applicable, a CVE-ID + - vii. The planned, coordinated release date + - viii. Mitigating factors (for example, the vulnerability is only exposed in uncommon, non-default configurations) + - ix. Workarounds (configuration changes users can make to reduce their exposure to the vulnerability) + - x. If applicable, credits to the original reporter + - c. Release finalized vulnerability announcement on website and reddit /r/Monero + - d. For HIGH severities, release finalized vulnerability announcement on well-known mailing lists: + - i. oss-security@lists.openwall.com + - ii. bugtraq@securityfocus.com + - e. If applicable, developers request a CVE-ID + - i. The commit that applied the fix is made reference too in a future commit and includes a CVE-ID + +3. If the Incident Response process in section III is *not* successfully completed: + - a. Response Team and developers organize an IRC meeting to discuss why/what points in section III were not resolved and how the team can resolve them in the future + - b. Any developer meetings immediately following the incident should include points made in section V + - c. If disputes arise about whether or when to disclose information about a vulnerability, the Response Team will publicly discuss the issue via IRC and attempt to reach consensus + - d. If consensus on a timely disclosure is not met (no later than 90 days), the researcher (after 90 days) has every right to expose the vulnerability to the public + +## V. Incident Analysis + +1. Isolate codebase + - a. Response Team and developers should coordinate to work on the following: + - i. Problematic implementation of classes/libraries/functions, etc. + - ii. Focus on apps/distro packaging, etc. + - iii. Operator/config error, etc. + +2. Auditing + - a. Response Team and developers should coordinate to work on the following: + - i. Auditing of problem area(s) as discussed in point 1 + - ii. Generate internal reports and store for future reference + - iii. If results are not sensitive, share with the public via IRC or GitHub + +3. Response Team has 45 days following completion of section III to ensure completion of section V + +## VI. Resolutions + +Any further questions or resolutions regarding the incident(s) between the researcher and response + development team after public disclosure can be addressed via the following: + +- [GitHub](https://github.com/monero-project/monero/issues/) +- [HackerOne](https://hackerone.com/monero) +- [Reddit /r/Monero](https://reddit.com/r/Monero/) +- IRC +- Email + +## VII. Continuous Improvement + +1. Response Team and developers should hold annual meetings to review the previous year's incidents + +2. Response Team or designated person(s) should give a brief presentation, including: + - a. Areas of Monero affected by the incidents + - b. Any network downtime or monetary cost (if any) of the incidents + - c. Ways in which the incidents could have been avoided (if any) + - d. How effective this process was in dealing with the incidents + +3. After the presentation, Response Team and developers should discuss: + - a. Potential changes to development processes to reduce future incidents + - b. Potential changes to this process to improve future responses diff --git a/contrib/epee/include/hex.h b/contrib/epee/include/hex.h new file mode 100644 index 000000000..f8d6be048 --- /dev/null +++ b/contrib/epee/include/hex.h @@ -0,0 +1,65 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <array> +#include <cstdint> +#include <iosfwd> +#include <string> + +#include "span.h" + +namespace epee +{ + struct to_hex + { + //! \return A std::string containing hex of `src`. + static std::string string(const span<const std::uint8_t> src); + + //! \return An array containing hex of `src`. + template<std::size_t N> + static std::array<char, N * 2> array(const std::array<std::uint8_t, N>& src) noexcept + { + std::array<char, N * 2> out{{}}; + static_assert(N <= 128, "keep the stack size down"); + buffer_unchecked(out.data(), {src.data(), src.size()}); + return out; + } + + //! Append `src` as hex to `out`. + static void buffer(std::ostream& out, const span<const std::uint8_t> src); + + //! Append `< + src + >` as hex to `out`. + static void formatted(std::ostream& out, const span<const std::uint8_t> src); + + private: + //! 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/span.h b/contrib/epee/include/span.h new file mode 100644 index 000000000..ea4ba63dd --- /dev/null +++ b/contrib/epee/include/span.h @@ -0,0 +1,130 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <cstdint> +#include <memory> +#include <type_traits> + +namespace epee +{ + /*! + \brief Non-owning sequence of data. Does not deep copy + + Inspired by `gsl::span` and/or `boost::iterator_range`. This class is + intended to be used as a parameter type for functions that need to take a + writable or read-only sequence of data. Most common cases are `span<char>` + and `span<std::uint8_t>`. Using as a class member is only recommended if + clearly documented as not doing a deep-copy. C-arrays are easily convertible + to this type. + + \note Conversion from C string literal to `span<const char>` will include + the NULL-terminator. + \note Never allows derived-to-base pointer conversion; an array of derived + types is not an array of base types. + */ + template<typename T> + class span + { + /* Supporting class types is tricky - the {ptr,len} constructor will allow + derived-to-base conversions. This is NOT desireable because an array of + derived types is not an array of base types. It is possible to handle + this case, implement when/if needed. */ + static_assert(!std::is_class<T>(), "no class types are currently allowed"); + public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = pointer; + using const_iterator = const_pointer; + + constexpr span() noexcept : ptr(nullptr), len(0) {} + constexpr span(std::nullptr_t) noexcept : span() {} + + constexpr span(T* const src_ptr, const std::size_t count) noexcept + : ptr(src_ptr), len(count) {} + + //! Conversion from C-array. Prevents common bugs with sizeof + arrays. + template<std::size_t N> + constexpr span(T (&src)[N]) noexcept : span(src, N) {} + + constexpr span(const span&) noexcept = default; + span& operator=(const span&) noexcept = default; + + constexpr iterator begin() const noexcept { return ptr; } + constexpr const_iterator cbegin() const noexcept { return ptr; } + + constexpr iterator end() const noexcept { return begin() + size(); } + constexpr const_iterator cend() const noexcept { return cbegin() + size(); } + + constexpr bool empty() const noexcept { return size() == 0; } + constexpr pointer data() const noexcept { return ptr; } + constexpr std::size_t size() const noexcept { return len; } + constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(value_type); } + + private: + T* ptr; + std::size_t len; + }; + + //! \return `span<const T::value_type>` from a STL compatible `src`. + template<typename T> + constexpr span<const typename T::value_type> to_span(const T& src) + { + // compiler provides diagnostic if size() is not size_t. + return {src.data(), src.size()}; + } + + template<typename T> + constexpr bool has_padding() noexcept + { + return !std::is_pod<T>() || alignof(T) != 1; + } + + //! \return Cast data from `src` as `span<const std::uint8_t>`. + template<typename T> + span<const std::uint8_t> to_byte_span(const span<const T> src) noexcept + { + static_assert(!has_padding<T>(), "source type may have padding"); + return {reinterpret_cast<const std::uint8_t*>(src.data()), src.size_bytes()}; + } + + //! \return `span<const std::uint8_t>` which represents the bytes at `&src`. + template<typename T> + span<const std::uint8_t> as_byte_span(const T& src) noexcept + { + static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1"); + static_assert(!has_padding<T>(), "source type may have padding"); + return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)}; + } +} diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 530000028..67984b9e4 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -45,6 +45,8 @@ #include <boost/uuid/uuid_io.hpp> #include <boost/lexical_cast.hpp> #include <boost/algorithm/string.hpp> +#include "hex.h" +#include "span.h" #include "warnings.h" @@ -114,33 +116,10 @@ namespace string_tools return false; } } - //---------------------------------------------------------------------------- - template<class CharT> - std::basic_string<CharT> buff_to_hex(const std::basic_string<CharT>& s) - { - using namespace std; - basic_stringstream<CharT> hexStream; - hexStream << hex << noshowbase << setw(2); - - for(typename std::basic_string<CharT>::const_iterator it = s.begin(); it != s.end(); it++) - { - hexStream << "0x"<< static_cast<unsigned int>(static_cast<unsigned char>(*it)) << " "; - } - return hexStream.str(); - } //---------------------------------------------------------------------------- - template<class CharT> - std::basic_string<CharT> buff_to_hex_nodelimer(const std::basic_string<CharT>& s) + inline std::string buff_to_hex_nodelimer(const std::string& src) { - using namespace std; - basic_stringstream<CharT> hexStream; - hexStream << hex << noshowbase; - - for(typename std::basic_string<CharT>::const_iterator it = s.begin(); it != s.end(); it++) - { - hexStream << setw(2) << setfill('0') << static_cast<unsigned int>(static_cast<unsigned char>(*it)); - } - return hexStream.str(); + return to_hex::string(to_byte_span(to_span(src))); } //---------------------------------------------------------------------------- template<class CharT> @@ -559,9 +538,7 @@ POP_WARNINGS std::string pod_to_hex(const t_pod_type& s) { static_assert(std::is_pod<t_pod_type>::value, "expected pod type"); - std::string buff; - buff.assign(reinterpret_cast<const char*>(&s), sizeof(s)); - return buff_to_hex_nodelimer(buff); + return to_hex::string(as_byte_span(s)); } //---------------------------------------------------------------------------- template<class t_pod_type> diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 9db4c46ce..1d5fa0394 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -26,7 +26,7 @@ # 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. -add_library(epee STATIC http_auth.cpp mlog.cpp string_tools.cpp) +add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp string_tools.cpp) # Build and install libepee if we're building for GUI if (BUILD_GUI_DEPS) if(IOS) diff --git a/contrib/epee/src/hex.cpp b/contrib/epee/src/hex.cpp new file mode 100644 index 000000000..cfbd3cf87 --- /dev/null +++ b/contrib/epee/src/hex.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "hex.h" + +#include <iterator> +#include <limits> +#include <ostream> +#include <stdexcept> + +namespace epee +{ + namespace + { + template<typename T> + void write_hex(T&& out, const span<const std::uint8_t> src) + { + static constexpr const char hex[] = u8"0123456789abcdef"; + static_assert(sizeof(hex) == 17, "bad string size"); + for (const std::uint8_t byte : src) + { + *out = hex[byte >> 4]; + ++out; + *out = hex[byte & 0x0F]; + ++out; + } + } + } + + std::string to_hex::string(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{}; + out.resize(src.size() * 2); + buffer_unchecked(std::addressof(out[0]), src); + return out; + } + + void to_hex::buffer(std::ostream& out, const span<const std::uint8_t> src) + { + write_hex(std::ostreambuf_iterator<char>{out}, src); + } + + void to_hex::formatted(std::ostream& out, const span<const std::uint8_t> src) + { + out.put('<'); + buffer(out, src); + out.put('>'); + } + + void to_hex::buffer_unchecked(char* out, const span<const std::uint8_t> src) noexcept + { + return write_hex(out, src); + } +} diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp index 5a1c2142a..30e562700 100644 --- a/contrib/epee/src/http_auth.cpp +++ b/contrib/epee/src/http_auth.cpp @@ -67,6 +67,7 @@ #include <type_traits> #include "crypto/crypto.h" +#include "hex.h" #include "md5_l.h" #include "string_coding.h" @@ -104,25 +105,6 @@ namespace //// Digest Algorithms - template<std::size_t N> - std::array<char, N * 2> to_hex(const std::array<std::uint8_t, N>& digest) noexcept - { - static constexpr const char alphabet[] = u8"0123456789abcdef"; - static_assert(sizeof(alphabet) == 17, "bad alphabet size"); - - // TODO upgrade (improve performance) of to hex in epee string tools - std::array<char, N * 2> out{{}}; - auto current = out.begin(); - for (const std::uint8_t byte : digest) - { - *current = alphabet[byte >> 4]; - ++current; - *current = alphabet[byte & 0x0F]; - ++current; - } - return out; - } - struct md5_ { static constexpr const boost::string_ref name = ceref(u8"MD5"); @@ -156,7 +138,7 @@ namespace std::array<std::uint8_t, 16> digest{{}}; md5::MD5Final(digest.data(), std::addressof(ctx)); - return to_hex(digest); + return epee::to_hex::array(digest); } }; constexpr const boost::string_ref md5_::name; diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c index c2408f917..3552bd2a9 100644 --- a/external/db_drivers/liblmdb/mdb.c +++ b/external/db_drivers/liblmdb/mdb.c @@ -137,7 +137,7 @@ typedef SSIZE_T ssize_t; #include <unistd.h> #endif -#if defined(__sun) || defined(ANDROID) +#if defined(__sun) || defined(__ANDROID__) /* Most platforms have posix_memalign, older may only have memalign */ #define HAVE_MEMALIGN 1 #include <malloc.h> @@ -153,7 +153,7 @@ typedef SSIZE_T ssize_t; # define MDB_USE_SYSV_SEM 1 # endif # define MDB_FDATASYNC fsync -#elif defined(ANDROID) +#elif defined(__ANDROID__) # define MDB_FDATASYNC fsync #endif @@ -298,7 +298,7 @@ union semun { */ #ifndef MDB_USE_ROBUST /* Android currently lacks Robust Mutex support. So does glibc < 2.4. */ -# if defined(MDB_USE_POSIX_MUTEX) && (defined(ANDROID) || \ +# if defined(MDB_USE_POSIX_MUTEX) && (defined(__ANDROID__) || \ (defined(__GLIBC__) && GLIBC_VER < 0x020004)) # define MDB_USE_ROBUST 0 # else diff --git a/external/easylogging++/ea_config.h b/external/easylogging++/ea_config.h index 3e6ed6e8c..2524d3477 100644 --- a/external/easylogging++/ea_config.h +++ b/external/easylogging++/ea_config.h @@ -4,8 +4,7 @@ #define ELPP_DEFAULT_LOG_FILE "" #if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__ #else -#define ELPP_STACKTRACE_ON_CRASH 1 +#define ELPP_FEATURE_CRASH_LOG 1 #endif #define ELPP_DISABLE_DEFAULT_CRASH_HANDLING -#define ELPP_FEATURE_CRASH_LOG 1 #define ELPP_NO_CHECK_MACROS diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 3e8463fdf..efa876664 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -990,7 +990,7 @@ std::string OS::getProperty(const char* prop) { return ret == 0 ? std::string() : std::string(propVal); } -static std::string OS::getDeviceName(void) { +std::string OS::getDeviceName(void) { std::stringstream ss; std::string manufacturer = getProperty("ro.product.manufacturer"); std::string model = getProperty("ro.product.model"); @@ -2095,6 +2095,7 @@ Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : sysLogLogger->reconfigure(); #endif // defined(ELPP_SYSLOG) addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); + addFlag(LoggingFlag::CreateLoggerAutomatically); #if ELPP_ASYNC_LOGGING installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); #else diff --git a/external/unbound/validator/val_secalgo.c b/external/unbound/validator/val_secalgo.c index 7c8d7b287..256c3ff92 100644 --- a/external/unbound/validator/val_secalgo.c +++ b/external/unbound/validator/val_secalgo.c @@ -345,7 +345,11 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, "EVP_PKEY_assign_DSA failed"); return 0; } +#ifdef HAVE_EVP_DSS1 *digest_type = EVP_dss1(); +#else + *digest_type = EVP_sha1(); +#endif break; case LDNS_RSASHA1: diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 117f158ee..6afa28934 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -722,32 +722,24 @@ union cn_slow_hash_state * key schedule. Don't try to use this for vanilla AES. */ static void aes_expand_key(const uint8_t *key, uint8_t *expandedKey) { -__asm__("mov x2, %1\n\t" : : "r"(key), "r"(expandedKey)); +static const int rcon[] = { + 0x01,0x01,0x01,0x01, + 0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d, // rotate-n-splat + 0x1b,0x1b,0x1b,0x1b }; __asm__( -" adr x3,Lrcon\n" -"\n" " eor v0.16b,v0.16b,v0.16b\n" -" ld1 {v3.16b},[x0],#16\n" -" ld1 {v1.4s,v2.4s},[x3],#32\n" -" b L256\n" -".align 5\n" -"Lrcon:\n" -".long 0x01,0x01,0x01,0x01\n" -".long 0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d // rotate-n-splat\n" -".long 0x1b,0x1b,0x1b,0x1b\n" -"\n" -".align 4\n" -"L256:\n" -" ld1 {v4.16b},[x0]\n" -" mov w1,#5\n" -" st1 {v3.4s},[x2],#16\n" +" ld1 {v3.16b},[%0],#16\n" +" ld1 {v1.4s,v2.4s},[%2],#32\n" +" ld1 {v4.16b},[%0]\n" +" mov w2,#5\n" +" st1 {v3.4s},[%1],#16\n" "\n" -"Loop256:\n" +"1:\n" " tbl v6.16b,{v4.16b},v2.16b\n" " ext v5.16b,v0.16b,v3.16b,#12\n" -" st1 {v4.4s},[x2],#16\n" +" st1 {v4.4s},[%1],#16\n" " aese v6.16b,v0.16b\n" -" subs w1,w1,#1\n" +" subs w2,w2,#1\n" "\n" " eor v3.16b,v3.16b,v5.16b\n" " ext v5.16b,v0.16b,v5.16b,#12\n" @@ -757,8 +749,8 @@ __asm__( " eor v3.16b,v3.16b,v5.16b\n" " shl v1.16b,v1.16b,#1\n" " eor v3.16b,v3.16b,v6.16b\n" -" st1 {v3.4s},[x2],#16\n" -" b.eq Ldone\n" +" st1 {v3.4s},[%1],#16\n" +" b.eq 2f\n" "\n" " dup v6.4s,v3.s[3] // just splat\n" " ext v5.16b,v0.16b,v4.16b,#12\n" @@ -771,9 +763,9 @@ __asm__( " eor v4.16b,v4.16b,v5.16b\n" "\n" " eor v4.16b,v4.16b,v6.16b\n" -" b Loop256\n" +" b 1b\n" "\n" -"Ldone:\n"); +"2:\n" : : "r"(key), "r"(expandedKey), "r"(rcon)); } /* An ordinary AES round is a sequence of SubBytes, ShiftRows, MixColumns, AddRoundKey. There diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 15ace2b0b..c4adf1fcb 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -44,7 +44,6 @@ #include "serialization/debug_archive.h" #include "serialization/crypto.h" #include "serialization/keyvalue_serialization.h" // eepe named serialization -#include "string_tools.h" #include "cryptonote_config.h" #include "crypto/crypto.h" #include "crypto/hash.h" diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 4e1468510..14c03ac4c 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -33,6 +33,8 @@ #include "cryptonote_basic.h" #include "crypto/crypto.h" #include "crypto/hash.h" +#include "hex.h" +#include "span.h" namespace cryptonote { @@ -123,23 +125,28 @@ namespace cryptonote { bool operator ==(const cryptonote::block& a, const cryptonote::block& b); } -template <class T> -std::ostream &print256(std::ostream &o, const T &v) { - return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; -} -template <class T> -std::ostream &print64(std::ostream &o, const T &v) { - return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; -} - bool parse_hash256(const std::string str_hash, crypto::hash& hash); namespace crypto { - inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { return print64(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } } diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 9f2da8a94..eeb7b6094 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -122,13 +122,15 @@ namespace cryptonote block bl = AUTO_VAL_INIT(bl); difficulty_type di = AUTO_VAL_INIT(di); uint64_t height = AUTO_VAL_INIT(height); + uint64_t expected_reward; //only used for RPC calls - could possibly be useful here too? + cryptonote::blobdata extra_nonce; if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) { extra_nonce = m_extra_messages[m_config.current_extra_message_index]; } - if(!m_phandler->get_block_template(bl, m_mine_address, di, height, extra_nonce)) + if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce)) { LOG_ERROR("Failed to get_block_template(), stopping mining"); return false; diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 8ab4b2855..964ee6a36 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -51,7 +51,7 @@ namespace cryptonote struct i_miner_handler { virtual bool handle_block_found(block& b) = 0; - virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0; + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) = 0; protected: ~i_miner_handler(){}; }; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index abfa665ff..8dbf1b53b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -109,6 +109,7 @@ #define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 +#define P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT 2 #define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour #define P2P_IP_BLOCKTIME (60*60*24) //24 hour diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0d5a8d46f..74c5b3b83 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1072,7 +1072,7 @@ uint64_t Blockchain::get_current_cumulative_blocksize_limit() const // in a lot of places. That flag is not referenced in any of the code // nor any of the makefiles, howeve. Need to look into whether or not it's // necessary at all. -bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) +bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) { LOG_PRINT_L3("Blockchain::" << __func__); size_t median_size; @@ -1096,7 +1096,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m size_t txs_size; uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, m_hardfork->get_current_version())) + if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version())) { return false; } @@ -3665,6 +3665,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads); std::vector < std::vector < block >> blocks(threads); auto it = blocks_entry.begin(); + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); for (uint64_t i = 0; i < threads; i++) { @@ -3724,7 +3726,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e m_blocks_longhash_table.clear(); for (uint64_t i = 0; i < threads; i++) { - thread_list.push_back(new boost::thread(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i]))); + thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i])))); } for (size_t j = 0; j < thread_list.size(); j++) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 9dacba363..46f7ac682 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -323,11 +323,12 @@ namespace cryptonote * @param miner_address address new coins for the block will go to * @param di return-by-reference tells the miner what the difficulty target is * @param height return-by-reference tells the miner what height it's mining against + * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param ex_nonce extra data to be added to the miner transaction's extra * * @return true if block template filled in successfully, else false */ - bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); + bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); /** * @brief checks if a block is known about with a given hash diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 4d4b9d4d2..9c122f511 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -827,9 +827,9 @@ namespace cryptonote m_mempool.set_relayed(txs); } //----------------------------------------------------------------------------------------------- - bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) + bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) { - return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); + return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce); } //----------------------------------------------------------------------------------------------- bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 24b0f0614..e56c2dcf1 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -180,7 +180,7 @@ namespace cryptonote * * @note see Blockchain::create_block_template */ - virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); /** * @brief called when a transaction is relayed diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 37b6fb4b0..f78f673c7 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -613,7 +613,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint8_t version) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version) { // Warning: This function takes already_generated_ // coins as an argument and appears to do nothing @@ -705,6 +705,7 @@ namespace cryptonote LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); } + expected_reward = best_coinbase; LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase) << " (including " << print_money(fee) << " in fees)"); diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index d19f83b2e..f68bc0bb9 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -220,11 +220,12 @@ namespace cryptonote * @param already_generated_coins the current total number of coins "minted" * @param total_size return-by-reference the total size of the new block * @param fee return-by-reference the total of fees from the included transactions + * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param version hard fork version to use for consensus rules * * @return true */ - bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint8_t version); + bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version); /** * @brief get a list of all transactions in the pool diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt index 6aaad2534..4db19d195 100644 --- a/src/mnemonics/CMakeLists.txt +++ b/src/mnemonics/CMakeLists.txt @@ -33,6 +33,7 @@ set(mnemonics_headers) set(mnemonics_private_headers electrum-words.h + chinese_simplified.h english.h dutch.h french.h diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h new file mode 100644 index 000000000..413186733 --- /dev/null +++ b/src/mnemonics/chinese_simplified.h @@ -0,0 +1,1709 @@ +// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*!
+ * \file chinese_simplified.h
+ *
+ * \brief Simplified Chinese word list and map.
+ */
+
+#ifndef CHINESE_SIMPLIFIED_H
+#define CHINESE_SIMPLIFIED_H
+
+#include <vector>
+#include <unordered_map>
+#include "language_base.h"
+#include <string>
+
+/*!
+ * \namespace Language
+ * \brief Mnemonic language related namespace.
+ */
+namespace Language
+{
+ class Chinese_Simplified: public Base
+ {
+ public:
+ Chinese_Simplified(): Base("Chinese (Simplified)", std::vector<std::string>({
+ "的",
+ "一",
+ "是",
+ "在",
+ "不",
+ "了",
+ "有",
+ "和",
+ "人",
+ "这",
+ "中",
+ "大",
+ "为",
+ "上",
+ "个",
+ "国",
+ "我",
+ "以",
+ "要",
+ "他",
+ "时",
+ "来",
+ "用",
+ "们",
+ "生",
+ "到",
+ "作",
+ "地",
+ "于",
+ "出",
+ "就",
+ "分",
+ "对",
+ "成",
+ "会",
+ "可",
+ "主",
+ "发",
+ "年",
+ "动",
+ "同",
+ "工",
+ "也",
+ "能",
+ "下",
+ "过",
+ "子",
+ "说",
+ "产",
+ "种",
+ "面",
+ "而",
+ "方",
+ "后",
+ "多",
+ "定",
+ "行",
+ "学",
+ "法",
+ "所",
+ "民",
+ "得",
+ "经",
+ "十",
+ "三",
+ "之",
+ "进",
+ "着",
+ "等",
+ "部",
+ "度",
+ "家",
+ "电",
+ "力",
+ "里",
+ "如",
+ "水",
+ "化",
+ "高",
+ "自",
+ "二",
+ "理",
+ "起",
+ "小",
+ "物",
+ "现",
+ "实",
+ "加",
+ "量",
+ "都",
+ "两",
+ "体",
+ "制",
+ "机",
+ "当",
+ "使",
+ "点",
+ "从",
+ "业",
+ "本",
+ "去",
+ "把",
+ "性",
+ "好",
+ "应",
+ "开",
+ "它",
+ "合",
+ "还",
+ "因",
+ "由",
+ "其",
+ "些",
+ "然",
+ "前",
+ "外",
+ "天",
+ "政",
+ "四",
+ "日",
+ "那",
+ "社",
+ "义",
+ "事",
+ "平",
+ "形",
+ "相",
+ "全",
+ "表",
+ "间",
+ "样",
+ "与",
+ "关",
+ "各",
+ "重",
+ "新",
+ "线",
+ "内",
+ "数",
+ "正",
+ "心",
+ "反",
+ "你",
+ "明",
+ "看",
+ "原",
+ "又",
+ "么",
+ "利",
+ "比",
+ "或",
+ "但",
+ "质",
+ "气",
+ "第",
+ "向",
+ "道",
+ "命",
+ "此",
+ "变",
+ "条",
+ "只",
+ "没",
+ "结",
+ "解",
+ "问",
+ "意",
+ "建",
+ "月",
+ "公",
+ "无",
+ "系",
+ "军",
+ "很",
+ "情",
+ "者",
+ "最",
+ "立",
+ "代",
+ "想",
+ "已",
+ "通",
+ "并",
+ "提",
+ "直",
+ "题",
+ "党",
+ "程",
+ "展",
+ "五",
+ "果",
+ "料",
+ "象",
+ "员",
+ "革",
+ "位",
+ "入",
+ "常",
+ "文",
+ "总",
+ "次",
+ "品",
+ "式",
+ "活",
+ "设",
+ "及",
+ "管",
+ "特",
+ "件",
+ "长",
+ "求",
+ "老",
+ "头",
+ "基",
+ "资",
+ "边",
+ "流",
+ "路",
+ "级",
+ "少",
+ "图",
+ "山",
+ "统",
+ "接",
+ "知",
+ "较",
+ "将",
+ "组",
+ "见",
+ "计",
+ "别",
+ "她",
+ "手",
+ "角",
+ "期",
+ "根",
+ "论",
+ "运",
+ "农",
+ "指",
+ "几",
+ "九",
+ "区",
+ "强",
+ "放",
+ "决",
+ "西",
+ "被",
+ "干",
+ "做",
+ "必",
+ "战",
+ "先",
+ "回",
+ "则",
+ "任",
+ "取",
+ "据",
+ "处",
+ "队",
+ "南",
+ "给",
+ "色",
+ "光",
+ "门",
+ "即",
+ "保",
+ "治",
+ "北",
+ "造",
+ "百",
+ "规",
+ "热",
+ "领",
+ "七",
+ "海",
+ "口",
+ "东",
+ "导",
+ "器",
+ "压",
+ "志",
+ "世",
+ "金",
+ "增",
+ "争",
+ "济",
+ "阶",
+ "油",
+ "思",
+ "术",
+ "极",
+ "交",
+ "受",
+ "联",
+ "什",
+ "认",
+ "六",
+ "共",
+ "权",
+ "收",
+ "证",
+ "改",
+ "清",
+ "美",
+ "再",
+ "采",
+ "转",
+ "更",
+ "单",
+ "风",
+ "切",
+ "打",
+ "白",
+ "教",
+ "速",
+ "花",
+ "带",
+ "安",
+ "场",
+ "身",
+ "车",
+ "例",
+ "真",
+ "务",
+ "具",
+ "万",
+ "每",
+ "目",
+ "至",
+ "达",
+ "走",
+ "积",
+ "示",
+ "议",
+ "声",
+ "报",
+ "斗",
+ "完",
+ "类",
+ "八",
+ "离",
+ "华",
+ "名",
+ "确",
+ "才",
+ "科",
+ "张",
+ "信",
+ "马",
+ "节",
+ "话",
+ "米",
+ "整",
+ "空",
+ "元",
+ "况",
+ "今",
+ "集",
+ "温",
+ "传",
+ "土",
+ "许",
+ "步",
+ "群",
+ "广",
+ "石",
+ "记",
+ "需",
+ "段",
+ "研",
+ "界",
+ "拉",
+ "林",
+ "律",
+ "叫",
+ "且",
+ "究",
+ "观",
+ "越",
+ "织",
+ "装",
+ "影",
+ "算",
+ "低",
+ "持",
+ "音",
+ "众",
+ "书",
+ "布",
+ "复",
+ "容",
+ "儿",
+ "须",
+ "际",
+ "商",
+ "非",
+ "验",
+ "连",
+ "断",
+ "深",
+ "难",
+ "近",
+ "矿",
+ "千",
+ "周",
+ "委",
+ "素",
+ "技",
+ "备",
+ "半",
+ "办",
+ "青",
+ "省",
+ "列",
+ "习",
+ "响",
+ "约",
+ "支",
+ "般",
+ "史",
+ "感",
+ "劳",
+ "便",
+ "团",
+ "往",
+ "酸",
+ "历",
+ "市",
+ "克",
+ "何",
+ "除",
+ "消",
+ "构",
+ "府",
+ "称",
+ "太",
+ "准",
+ "精",
+ "值",
+ "号",
+ "率",
+ "族",
+ "维",
+ "划",
+ "选",
+ "标",
+ "写",
+ "存",
+ "候",
+ "毛",
+ "亲",
+ "快",
+ "效",
+ "斯",
+ "院",
+ "查",
+ "江",
+ "型",
+ "眼",
+ "王",
+ "按",
+ "格",
+ "养",
+ "易",
+ "置",
+ "派",
+ "层",
+ "片",
+ "始",
+ "却",
+ "专",
+ "状",
+ "育",
+ "厂",
+ "京",
+ "识",
+ "适",
+ "属",
+ "圆",
+ "包",
+ "火",
+ "住",
+ "调",
+ "满",
+ "县",
+ "局",
+ "照",
+ "参",
+ "红",
+ "细",
+ "引",
+ "听",
+ "该",
+ "铁",
+ "价",
+ "严",
+ "首",
+ "底",
+ "液",
+ "官",
+ "德",
+ "随",
+ "病",
+ "苏",
+ "失",
+ "尔",
+ "死",
+ "讲",
+ "配",
+ "女",
+ "黄",
+ "推",
+ "显",
+ "谈",
+ "罪",
+ "神",
+ "艺",
+ "呢",
+ "席",
+ "含",
+ "企",
+ "望",
+ "密",
+ "批",
+ "营",
+ "项",
+ "防",
+ "举",
+ "球",
+ "英",
+ "氧",
+ "势",
+ "告",
+ "李",
+ "台",
+ "落",
+ "木",
+ "帮",
+ "轮",
+ "破",
+ "亚",
+ "师",
+ "围",
+ "注",
+ "远",
+ "字",
+ "材",
+ "排",
+ "供",
+ "河",
+ "态",
+ "封",
+ "另",
+ "施",
+ "减",
+ "树",
+ "溶",
+ "怎",
+ "止",
+ "案",
+ "言",
+ "士",
+ "均",
+ "武",
+ "固",
+ "叶",
+ "鱼",
+ "波",
+ "视",
+ "仅",
+ "费",
+ "紧",
+ "爱",
+ "左",
+ "章",
+ "早",
+ "朝",
+ "害",
+ "续",
+ "轻",
+ "服",
+ "试",
+ "食",
+ "充",
+ "兵",
+ "源",
+ "判",
+ "护",
+ "司",
+ "足",
+ "某",
+ "练",
+ "差",
+ "致",
+ "板",
+ "田",
+ "降",
+ "黑",
+ "犯",
+ "负",
+ "击",
+ "范",
+ "继",
+ "兴",
+ "似",
+ "余",
+ "坚",
+ "曲",
+ "输",
+ "修",
+ "故",
+ "城",
+ "夫",
+ "够",
+ "送",
+ "笔",
+ "船",
+ "占",
+ "右",
+ "财",
+ "吃",
+ "富",
+ "春",
+ "职",
+ "觉",
+ "汉",
+ "画",
+ "功",
+ "巴",
+ "跟",
+ "虽",
+ "杂",
+ "飞",
+ "检",
+ "吸",
+ "助",
+ "升",
+ "阳",
+ "互",
+ "初",
+ "创",
+ "抗",
+ "考",
+ "投",
+ "坏",
+ "策",
+ "古",
+ "径",
+ "换",
+ "未",
+ "跑",
+ "留",
+ "钢",
+ "曾",
+ "端",
+ "责",
+ "站",
+ "简",
+ "述",
+ "钱",
+ "副",
+ "尽",
+ "帝",
+ "射",
+ "草",
+ "冲",
+ "承",
+ "独",
+ "令",
+ "限",
+ "阿",
+ "宣",
+ "环",
+ "双",
+ "请",
+ "超",
+ "微",
+ "让",
+ "控",
+ "州",
+ "良",
+ "轴",
+ "找",
+ "否",
+ "纪",
+ "益",
+ "依",
+ "优",
+ "顶",
+ "础",
+ "载",
+ "倒",
+ "房",
+ "突",
+ "坐",
+ "粉",
+ "敌",
+ "略",
+ "客",
+ "袁",
+ "冷",
+ "胜",
+ "绝",
+ "析",
+ "块",
+ "剂",
+ "测",
+ "丝",
+ "协",
+ "诉",
+ "念",
+ "陈",
+ "仍",
+ "罗",
+ "盐",
+ "友",
+ "洋",
+ "错",
+ "苦",
+ "夜",
+ "刑",
+ "移",
+ "频",
+ "逐",
+ "靠",
+ "混",
+ "母",
+ "短",
+ "皮",
+ "终",
+ "聚",
+ "汽",
+ "村",
+ "云",
+ "哪",
+ "既",
+ "距",
+ "卫",
+ "停",
+ "烈",
+ "央",
+ "察",
+ "烧",
+ "迅",
+ "境",
+ "若",
+ "印",
+ "洲",
+ "刻",
+ "括",
+ "激",
+ "孔",
+ "搞",
+ "甚",
+ "室",
+ "待",
+ "核",
+ "校",
+ "散",
+ "侵",
+ "吧",
+ "甲",
+ "游",
+ "久",
+ "菜",
+ "味",
+ "旧",
+ "模",
+ "湖",
+ "货",
+ "损",
+ "预",
+ "阻",
+ "毫",
+ "普",
+ "稳",
+ "乙",
+ "妈",
+ "植",
+ "息",
+ "扩",
+ "银",
+ "语",
+ "挥",
+ "酒",
+ "守",
+ "拿",
+ "序",
+ "纸",
+ "医",
+ "缺",
+ "雨",
+ "吗",
+ "针",
+ "刘",
+ "啊",
+ "急",
+ "唱",
+ "误",
+ "训",
+ "愿",
+ "审",
+ "附",
+ "获",
+ "茶",
+ "鲜",
+ "粮",
+ "斤",
+ "孩",
+ "脱",
+ "硫",
+ "肥",
+ "善",
+ "龙",
+ "演",
+ "父",
+ "渐",
+ "血",
+ "欢",
+ "械",
+ "掌",
+ "歌",
+ "沙",
+ "刚",
+ "攻",
+ "谓",
+ "盾",
+ "讨",
+ "晚",
+ "粒",
+ "乱",
+ "燃",
+ "矛",
+ "乎",
+ "杀",
+ "药",
+ "宁",
+ "鲁",
+ "贵",
+ "钟",
+ "煤",
+ "读",
+ "班",
+ "伯",
+ "香",
+ "介",
+ "迫",
+ "句",
+ "丰",
+ "培",
+ "握",
+ "兰",
+ "担",
+ "弦",
+ "蛋",
+ "沉",
+ "假",
+ "穿",
+ "执",
+ "答",
+ "乐",
+ "谁",
+ "顺",
+ "烟",
+ "缩",
+ "征",
+ "脸",
+ "喜",
+ "松",
+ "脚",
+ "困",
+ "异",
+ "免",
+ "背",
+ "星",
+ "福",
+ "买",
+ "染",
+ "井",
+ "概",
+ "慢",
+ "怕",
+ "磁",
+ "倍",
+ "祖",
+ "皇",
+ "促",
+ "静",
+ "补",
+ "评",
+ "翻",
+ "肉",
+ "践",
+ "尼",
+ "衣",
+ "宽",
+ "扬",
+ "棉",
+ "希",
+ "伤",
+ "操",
+ "垂",
+ "秋",
+ "宜",
+ "氢",
+ "套",
+ "督",
+ "振",
+ "架",
+ "亮",
+ "末",
+ "宪",
+ "庆",
+ "编",
+ "牛",
+ "触",
+ "映",
+ "雷",
+ "销",
+ "诗",
+ "座",
+ "居",
+ "抓",
+ "裂",
+ "胞",
+ "呼",
+ "娘",
+ "景",
+ "威",
+ "绿",
+ "晶",
+ "厚",
+ "盟",
+ "衡",
+ "鸡",
+ "孙",
+ "延",
+ "危",
+ "胶",
+ "屋",
+ "乡",
+ "临",
+ "陆",
+ "顾",
+ "掉",
+ "呀",
+ "灯",
+ "岁",
+ "措",
+ "束",
+ "耐",
+ "剧",
+ "玉",
+ "赵",
+ "跳",
+ "哥",
+ "季",
+ "课",
+ "凯",
+ "胡",
+ "额",
+ "款",
+ "绍",
+ "卷",
+ "齐",
+ "伟",
+ "蒸",
+ "殖",
+ "永",
+ "宗",
+ "苗",
+ "川",
+ "炉",
+ "岩",
+ "弱",
+ "零",
+ "杨",
+ "奏",
+ "沿",
+ "露",
+ "杆",
+ "探",
+ "滑",
+ "镇",
+ "饭",
+ "浓",
+ "航",
+ "怀",
+ "赶",
+ "库",
+ "夺",
+ "伊",
+ "灵",
+ "税",
+ "途",
+ "灭",
+ "赛",
+ "归",
+ "召",
+ "鼓",
+ "播",
+ "盘",
+ "裁",
+ "险",
+ "康",
+ "唯",
+ "录",
+ "菌",
+ "纯",
+ "借",
+ "糖",
+ "盖",
+ "横",
+ "符",
+ "私",
+ "努",
+ "堂",
+ "域",
+ "枪",
+ "润",
+ "幅",
+ "哈",
+ "竟",
+ "熟",
+ "虫",
+ "泽",
+ "脑",
+ "壤",
+ "碳",
+ "欧",
+ "遍",
+ "侧",
+ "寨",
+ "敢",
+ "彻",
+ "虑",
+ "斜",
+ "薄",
+ "庭",
+ "纳",
+ "弹",
+ "饲",
+ "伸",
+ "折",
+ "麦",
+ "湿",
+ "暗",
+ "荷",
+ "瓦",
+ "塞",
+ "床",
+ "筑",
+ "恶",
+ "户",
+ "访",
+ "塔",
+ "奇",
+ "透",
+ "梁",
+ "刀",
+ "旋",
+ "迹",
+ "卡",
+ "氯",
+ "遇",
+ "份",
+ "毒",
+ "泥",
+ "退",
+ "洗",
+ "摆",
+ "灰",
+ "彩",
+ "卖",
+ "耗",
+ "夏",
+ "择",
+ "忙",
+ "铜",
+ "献",
+ "硬",
+ "予",
+ "繁",
+ "圈",
+ "雪",
+ "函",
+ "亦",
+ "抽",
+ "篇",
+ "阵",
+ "阴",
+ "丁",
+ "尺",
+ "追",
+ "堆",
+ "雄",
+ "迎",
+ "泛",
+ "爸",
+ "楼",
+ "避",
+ "谋",
+ "吨",
+ "野",
+ "猪",
+ "旗",
+ "累",
+ "偏",
+ "典",
+ "馆",
+ "索",
+ "秦",
+ "脂",
+ "潮",
+ "爷",
+ "豆",
+ "忽",
+ "托",
+ "惊",
+ "塑",
+ "遗",
+ "愈",
+ "朱",
+ "替",
+ "纤",
+ "粗",
+ "倾",
+ "尚",
+ "痛",
+ "楚",
+ "谢",
+ "奋",
+ "购",
+ "磨",
+ "君",
+ "池",
+ "旁",
+ "碎",
+ "骨",
+ "监",
+ "捕",
+ "弟",
+ "暴",
+ "割",
+ "贯",
+ "殊",
+ "释",
+ "词",
+ "亡",
+ "壁",
+ "顿",
+ "宝",
+ "午",
+ "尘",
+ "闻",
+ "揭",
+ "炮",
+ "残",
+ "冬",
+ "桥",
+ "妇",
+ "警",
+ "综",
+ "招",
+ "吴",
+ "付",
+ "浮",
+ "遭",
+ "徐",
+ "您",
+ "摇",
+ "谷",
+ "赞",
+ "箱",
+ "隔",
+ "订",
+ "男",
+ "吹",
+ "园",
+ "纷",
+ "唐",
+ "败",
+ "宋",
+ "玻",
+ "巨",
+ "耕",
+ "坦",
+ "荣",
+ "闭",
+ "湾",
+ "键",
+ "凡",
+ "驻",
+ "锅",
+ "救",
+ "恩",
+ "剥",
+ "凝",
+ "碱",
+ "齿",
+ "截",
+ "炼",
+ "麻",
+ "纺",
+ "禁",
+ "废",
+ "盛",
+ "版",
+ "缓",
+ "净",
+ "睛",
+ "昌",
+ "婚",
+ "涉",
+ "筒",
+ "嘴",
+ "插",
+ "岸",
+ "朗",
+ "庄",
+ "街",
+ "藏",
+ "姑",
+ "贸",
+ "腐",
+ "奴",
+ "啦",
+ "惯",
+ "乘",
+ "伙",
+ "恢",
+ "匀",
+ "纱",
+ "扎",
+ "辩",
+ "耳",
+ "彪",
+ "臣",
+ "亿",
+ "璃",
+ "抵",
+ "脉",
+ "秀",
+ "萨",
+ "俄",
+ "网",
+ "舞",
+ "店",
+ "喷",
+ "纵",
+ "寸",
+ "汗",
+ "挂",
+ "洪",
+ "贺",
+ "闪",
+ "柬",
+ "爆",
+ "烯",
+ "津",
+ "稻",
+ "墙",
+ "软",
+ "勇",
+ "像",
+ "滚",
+ "厘",
+ "蒙",
+ "芳",
+ "肯",
+ "坡",
+ "柱",
+ "荡",
+ "腿",
+ "仪",
+ "旅",
+ "尾",
+ "轧",
+ "冰",
+ "贡",
+ "登",
+ "黎",
+ "削",
+ "钻",
+ "勒",
+ "逃",
+ "障",
+ "氨",
+ "郭",
+ "峰",
+ "币",
+ "港",
+ "伏",
+ "轨",
+ "亩",
+ "毕",
+ "擦",
+ "莫",
+ "刺",
+ "浪",
+ "秘",
+ "援",
+ "株",
+ "健",
+ "售",
+ "股",
+ "岛",
+ "甘",
+ "泡",
+ "睡",
+ "童",
+ "铸",
+ "汤",
+ "阀",
+ "休",
+ "汇",
+ "舍",
+ "牧",
+ "绕",
+ "炸",
+ "哲",
+ "磷",
+ "绩",
+ "朋",
+ "淡",
+ "尖",
+ "启",
+ "陷",
+ "柴",
+ "呈",
+ "徒",
+ "颜",
+ "泪",
+ "稍",
+ "忘",
+ "泵",
+ "蓝",
+ "拖",
+ "洞",
+ "授",
+ "镜",
+ "辛",
+ "壮",
+ "锋",
+ "贫",
+ "虚",
+ "弯",
+ "摩",
+ "泰",
+ "幼",
+ "廷",
+ "尊",
+ "窗",
+ "纲",
+ "弄",
+ "隶",
+ "疑",
+ "氏",
+ "宫",
+ "姐",
+ "震",
+ "瑞",
+ "怪",
+ "尤",
+ "琴",
+ "循",
+ "描",
+ "膜",
+ "违",
+ "夹",
+ "腰",
+ "缘",
+ "珠",
+ "穷",
+ "森",
+ "枝",
+ "竹",
+ "沟",
+ "催",
+ "绳",
+ "忆",
+ "邦",
+ "剩",
+ "幸",
+ "浆",
+ "栏",
+ "拥",
+ "牙",
+ "贮",
+ "礼",
+ "滤",
+ "钠",
+ "纹",
+ "罢",
+ "拍",
+ "咱",
+ "喊",
+ "袖",
+ "埃",
+ "勤",
+ "罚",
+ "焦",
+ "潜",
+ "伍",
+ "墨",
+ "欲",
+ "缝",
+ "姓",
+ "刊",
+ "饱",
+ "仿",
+ "奖",
+ "铝",
+ "鬼",
+ "丽",
+ "跨",
+ "默",
+ "挖",
+ "链",
+ "扫",
+ "喝",
+ "袋",
+ "炭",
+ "污",
+ "幕",
+ "诸",
+ "弧",
+ "励",
+ "梅",
+ "奶",
+ "洁",
+ "灾",
+ "舟",
+ "鉴",
+ "苯",
+ "讼",
+ "抱",
+ "毁",
+ "懂",
+ "寒",
+ "智",
+ "埔",
+ "寄",
+ "届",
+ "跃",
+ "渡",
+ "挑",
+ "丹",
+ "艰",
+ "贝",
+ "碰",
+ "拔",
+ "爹",
+ "戴",
+ "码",
+ "梦",
+ "芽",
+ "熔",
+ "赤",
+ "渔",
+ "哭",
+ "敬",
+ "颗",
+ "奔",
+ "铅",
+ "仲",
+ "虎",
+ "稀",
+ "妹",
+ "乏",
+ "珍",
+ "申",
+ "桌",
+ "遵",
+ "允",
+ "隆",
+ "螺",
+ "仓",
+ "魏",
+ "锐",
+ "晓",
+ "氮",
+ "兼",
+ "隐",
+ "碍",
+ "赫",
+ "拨",
+ "忠",
+ "肃",
+ "缸",
+ "牵",
+ "抢",
+ "博",
+ "巧",
+ "壳",
+ "兄",
+ "杜",
+ "讯",
+ "诚",
+ "碧",
+ "祥",
+ "柯",
+ "页",
+ "巡",
+ "矩",
+ "悲",
+ "灌",
+ "龄",
+ "伦",
+ "票",
+ "寻",
+ "桂",
+ "铺",
+ "圣",
+ "恐",
+ "恰",
+ "郑",
+ "趣",
+ "抬",
+ "荒",
+ "腾",
+ "贴",
+ "柔",
+ "滴",
+ "猛",
+ "阔",
+ "辆",
+ "妻",
+ "填",
+ "撤",
+ "储",
+ "签",
+ "闹",
+ "扰",
+ "紫",
+ "砂",
+ "递",
+ "戏",
+ "吊",
+ "陶",
+ "伐",
+ "喂",
+ "疗",
+ "瓶",
+ "婆",
+ "抚",
+ "臂",
+ "摸",
+ "忍",
+ "虾",
+ "蜡",
+ "邻",
+ "胸",
+ "巩",
+ "挤",
+ "偶",
+ "弃",
+ "槽",
+ "劲",
+ "乳",
+ "邓",
+ "吉",
+ "仁",
+ "烂",
+ "砖",
+ "租",
+ "乌",
+ "舰",
+ "伴",
+ "瓜",
+ "浅",
+ "丙",
+ "暂",
+ "燥",
+ "橡",
+ "柳",
+ "迷",
+ "暖",
+ "牌",
+ "秧",
+ "胆",
+ "详",
+ "簧",
+ "踏",
+ "瓷",
+ "谱",
+ "呆",
+ "宾",
+ "糊",
+ "洛",
+ "辉",
+ "愤",
+ "竞",
+ "隙",
+ "怒",
+ "粘",
+ "乃",
+ "绪",
+ "肩",
+ "籍",
+ "敏",
+ "涂",
+ "熙",
+ "皆",
+ "侦",
+ "悬",
+ "掘",
+ "享",
+ "纠",
+ "醒",
+ "狂",
+ "锁",
+ "淀",
+ "恨",
+ "牲",
+ "霸",
+ "爬",
+ "赏",
+ "逆",
+ "玩",
+ "陵",
+ "祝",
+ "秒",
+ "浙",
+ "貌"
+ }), 1)
+ {
+ populate_maps();
+ }
+ };
+}
+
+#endif
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index fd2c6a836..501495f0b 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -51,6 +51,7 @@ #include <boost/crc.hpp> #include <boost/algorithm/string/join.hpp> +#include "chinese_simplified.h" #include "english.h" #include "dutch.h" #include "french.h" @@ -84,6 +85,7 @@ namespace { // If there's a new language added, add an instance of it here. std::vector<Language::Base*> language_instances({ + Language::Singleton<Language::Chinese_Simplified>::instance(), Language::Singleton<Language::English>::instance(), Language::Singleton<Language::Dutch>::instance(), Language::Singleton<Language::French>::instance(), @@ -240,7 +242,7 @@ namespace crypto std::vector<std::string> seed; boost::algorithm::trim(words); - boost::split(seed, words, boost::is_any_of(" ")); + boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); // error on non-compliant word list if (seed.size() != seed_length/2 && seed.size() != seed_length && @@ -348,6 +350,10 @@ namespace crypto { language = Language::Singleton<Language::Russian>::instance(); } + else if (language_name == "Chinese (Simplified)") + { + language = Language::Singleton<Language::Chinese_Simplified>::instance(); + } else { return false; @@ -393,6 +399,7 @@ namespace crypto void get_language_list(std::vector<std::string> &languages) { std::vector<Language::Base*> language_instances({ + Language::Singleton<Language::Chinese_Simplified>::instance(), Language::Singleton<Language::English>::instance(), Language::Singleton<Language::Dutch>::instance(), Language::Singleton<Language::French>::instance(), diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index 09d05d132..d5ab4bc84 100644 --- a/src/mnemonics/japanese.h +++ b/src/mnemonics/japanese.h @@ -1,23 +1,43 @@ -// Word list originally created by dabura667
-//
-// Copyright (c) 2014-2017, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index 09d7132f5..f9d66afc4 100644 --- a/src/mnemonics/portuguese.h +++ b/src/mnemonics/portuguese.h @@ -1,21 +1,44 @@ -// Copyright (c) 2014-2017, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index 4b386a968..117890ada 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -1,23 +1,44 @@ -// Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
-//
-// Copyright (c) 2014-2017, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -1678,11 +1699,11 @@ namespace Language "risa",
"ritmo",
"rito"
- }), 4) - { + }), 4)
+ {
populate_maps(ALLOW_SHORT_WORDS);
}
};
}
-#endif +#endif
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 4582a3236..13cd3f5b0 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -155,6 +155,8 @@ namespace nodetool CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) END_INVOKE_MAP2() + enum PeerType { anchor = 0, white, gray }; + //----------------- commands handlers ---------------------------------------------- int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context); int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context); @@ -205,15 +207,17 @@ namespace nodetool bool do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context, bool just_take_peerlist = false); bool do_peer_timed_sync(const epee::net_utils::connection_context_base& context, peerid_type peer_id); + bool make_new_connection_from_anchor_peerlist(const std::vector<anchor_peerlist_entry>& anchor_peerlist); bool make_new_connection_from_peerlist(bool use_white_list); - bool try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, bool white = true); + bool try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, PeerType peer_type = white, uint64_t first_seen_stamp = 0); size_t get_random_index_with_fixed_probability(size_t max_index); bool is_peer_used(const peerlist_entry& peer); + bool is_peer_used(const anchor_peerlist_entry& peer); bool is_addr_connected(const net_address& peer); template<class t_callback> bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f); - bool make_expected_connections_count(bool white_list, size_t expected_connections); + bool make_expected_connections_count(PeerType peer_type, size_t expected_connections); void cache_connect_fail_info(const net_address& addr); bool is_addr_recently_failed(const net_address& addr); bool is_priority_node(const net_address& na); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 2e2dffab8..5c903b1f5 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -874,6 +874,30 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::is_peer_used(const anchor_peerlist_entry& peer) + { + if(m_config.m_peer_id == peer.id) { + return true;//dont make connections to ourself + } + + bool used = false; + + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) + { + used = true; + + return false;//stop enumerating + } + + return true; + }); + + return used; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::is_addr_connected(const net_address& peer) { bool connected = false; @@ -900,7 +924,7 @@ namespace nodetool } while(0) template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) + bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) { if (m_current_number_of_out_peers == m_config.m_net_config.connections_count) // out peers limit { @@ -913,7 +937,7 @@ namespace nodetool return false; } MDEBUG("Connecting to " << epee::string_tools::get_ip_string_from_int32(na.ip) << ":" - << epee::string_tools::num_to_string_fast(na.port) << "(white=" << white << ", last_seen: " + << epee::string_tools::num_to_string_fast(na.port) << "(peer_type=" << peer_type << ", last_seen: " << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") << ")..."); @@ -963,6 +987,13 @@ namespace nodetool m_peerlist.append_with_peer_white(pe_local); //update last seen and push it to peerlist manager + anchor_peerlist_entry ape = AUTO_VAL_INIT(ape); + ape.adr = na; + ape.id = pi; + ape.first_seen = first_seen_stamp ? first_seen_stamp : time(nullptr); + + m_peerlist.append_with_peer_anchor(ape); + LOG_DEBUG_CC(con, "CONNECTION HANDSHAKED OK."); return true; } @@ -1029,6 +1060,41 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::make_new_connection_from_anchor_peerlist(const std::vector<anchor_peerlist_entry>& anchor_peerlist) + { + for (const auto& pe: anchor_peerlist) { + _note("Considering connecting (out) to peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast<std::string>(pe.adr.port)); + + if(is_peer_used(pe)) { + _note("Peer is used"); + continue; + } + + if(!is_remote_ip_allowed(pe.adr.ip)) { + continue; + } + + if(is_addr_recently_failed(pe.adr)) { + continue; + } + + MDEBUG("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) + << ":" << boost::lexical_cast<std::string>(pe.adr.port) + << "[peer_type=" << anchor + << "] first_seen: " << epee::misc_utils::get_time_interval_string(time(NULL) - pe.first_seen)); + + if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, 0, anchor, pe.first_seen)) { + _note("Handshake failed"); + continue; + } + + return true; + } + + return false; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::make_new_connection_from_peerlist(bool use_white_list) { size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count(); @@ -1079,10 +1145,10 @@ namespace nodetool MDEBUG("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast<std::string>(pe.adr.port) - << "[white=" << use_white_list + << "[peer_list=" << (use_white_list ? white : gray) << "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); - if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list)) { + if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list ? white : gray)) { _note("Handshake failed"); continue; } @@ -1144,19 +1210,22 @@ namespace nodetool { if(conn_count < expected_white_connections) { - //start from white list - if(!make_expected_connections_count(true, expected_white_connections)) + //start from anchor list + if(!make_expected_connections_count(anchor, P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT)) return false; - //and then do grey list - if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + //then do white list + if(!make_expected_connections_count(white, expected_white_connections)) + return false; + //then do grey list + if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) return false; }else { //start from grey list - if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) return false; //and then do white list - if(!make_expected_connections_count(true, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(white, m_config.m_net_config.connections_count)) return false; } } @@ -1165,11 +1234,17 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::make_expected_connections_count(bool white_list, size_t expected_connections) + bool node_server<t_payload_net_handler>::make_expected_connections_count(PeerType peer_type, size_t expected_connections) { if (m_offline) return true; + std::vector<anchor_peerlist_entry> apl; + + if (peer_type == anchor) { + m_peerlist.get_and_empty_anchor_peerlist(apl); + } + size_t conn_count = get_outgoing_connections_count(); //add new connections from white peers while(conn_count < expected_connections) @@ -1177,8 +1252,18 @@ namespace nodetool if(m_net_server.is_stop_signal_sent()) return false; - if(!make_new_connection_from_peerlist(white_list)) + if (peer_type == anchor && !make_new_connection_from_anchor_peerlist(apl)) { + break; + } + + if (peer_type == white && !make_new_connection_from_peerlist(true)) { + break; + } + + if (peer_type == gray && !make_new_connection_from_peerlist(false)) { break; + } + conn_count = get_outgoing_connections_count(); } return true; @@ -1660,6 +1745,14 @@ namespace nodetool template<class t_payload_net_handler> void node_server<t_payload_net_handler>::on_connection_close(p2p_connection_context& context) { + if (!m_net_server.is_stop_signal_sent() && !context.m_is_income) { + nodetool::net_address na = AUTO_VAL_INIT(na); + na.ip = context.m_remote_ip; + na.port = context.m_remote_port; + + m_peerlist.remove_from_peer_anchor(na); + } + MINFO("["<< epee::net_utils::print_connection_context(context) << "] CLOSE CONNECTION"); } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 11d995995..de0f51cc3 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -54,7 +54,7 @@ #include "net_peerlist_boost_serialization.h" -#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 4 +#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 5 namespace nodetool { @@ -77,13 +77,15 @@ namespace nodetool bool get_gray_peer_by_index(peerlist_entry& p, size_t i); bool append_with_peer_white(const peerlist_entry& pr); bool append_with_peer_gray(const peerlist_entry& pr); + bool append_with_peer_anchor(const anchor_peerlist_entry& ple); bool set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port); bool set_peer_just_seen(peerid_type peer, const net_address& addr); bool set_peer_unreachable(const peerlist_entry& pr); bool is_ip_allowed(uint32_t ip); bool get_random_gray_peer(peerlist_entry& pe); bool remove_from_peer_gray(const peerlist_entry& pe); - + bool get_and_empty_anchor_peerlist(std::vector<anchor_peerlist_entry>& apl); + bool remove_from_peer_anchor(const net_address& addr); private: struct by_time{}; @@ -145,6 +147,16 @@ namespace nodetool boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<peerlist_entry,int64_t,&peerlist_entry::last_seen> > > > peers_indexed_old; + + typedef boost::multi_index_container< + anchor_peerlist_entry, + boost::multi_index::indexed_by< + // access by anchor_peerlist_entry::net_adress + boost::multi_index::ordered_unique<boost::multi_index::tag<by_addr>, boost::multi_index::member<anchor_peerlist_entry,net_address,&anchor_peerlist_entry::adr> >, + // sort by anchor_peerlist_entry::first_seen + boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<anchor_peerlist_entry,int64_t,&anchor_peerlist_entry::first_seen> > + > + > anchor_peers_indexed; public: template <class Archive, class t_version_type> @@ -161,8 +173,15 @@ namespace nodetool peers_indexed_from_old(pio, m_peers_white); return; } + a & m_peers_white; a & m_peers_gray; + + if(ver < 5) { + return; + } + + a & m_peers_anchor; } private: @@ -178,6 +197,7 @@ namespace nodetool peers_indexed m_peers_gray; peers_indexed m_peers_white; + anchor_peers_indexed m_peers_anchor; }; //-------------------------------------------------------------------------------------------------- inline @@ -398,6 +418,24 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline + bool peerlist_manager::append_with_peer_anchor(const anchor_peerlist_entry& ple) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + auto by_addr_it_anchor = m_peers_anchor.get<by_addr>().find(ple.adr); + + if(by_addr_it_anchor == m_peers_anchor.get<by_addr>().end()) { + m_peers_anchor.insert(ple); + } + + return true; + + CATCH_ENTRY_L0("peerlist_manager::append_with_peer_anchor()", false); + } + //-------------------------------------------------------------------------------------------------- + inline bool peerlist_manager::get_random_gray_peer(peerlist_entry& pe) { TRY_ENTRY(); @@ -435,7 +473,46 @@ namespace nodetool CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_gray()", false); } - //-------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_and_empty_anchor_peerlist(std::vector<anchor_peerlist_entry>& apl) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + auto begin = m_peers_anchor.get<by_time>().begin(); + auto end = m_peers_anchor.get<by_time>().end(); + + std::for_each(begin, end, [&apl](const anchor_peerlist_entry &a) { + apl.push_back(a); + }); + + m_peers_anchor.get<by_time>().clear(); + + return true; + + CATCH_ENTRY_L0("peerlist_manager::get_and_empty_anchor_peerlist()", false); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::remove_from_peer_anchor(const net_address& addr) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + anchor_peers_indexed::index_iterator<by_addr>::type iterator = m_peers_anchor.get<by_addr>().find(addr); + + if (iterator != m_peers_anchor.get<by_addr>().end()) { + m_peers_anchor.erase(iterator); + } + + return true; + + CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_anchor()", false); + } + //-------------------------------------------------------------------------------------------------- } BOOST_CLASS_VERSION(nodetool::peerlist_manager, CURRENT_PEERLIST_STORAGE_ARCHIVE_VER) diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 260f16919..6891ac80c 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -49,6 +49,14 @@ namespace boost a & pl.adr; a & pl.id; a & pl.last_seen; - } + } + + template <class Archive, class ver_type> + inline void serialize(Archive &a, nodetool::anchor_peerlist_entry& pl, const ver_type ver) + { + a & pl.adr; + a & pl.id; + a & pl.first_seen; + } } } diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 97e75f5ca..5ec012714 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -56,6 +56,13 @@ namespace nodetool int64_t last_seen; }; + struct anchor_peerlist_entry + { + net_address adr; + peerid_type id; + int64_t first_seen; + }; + struct connection_entry { net_address adr; diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 0c27745e1..c820fb297 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -47,6 +47,8 @@ extern "C" { #include "crypto/generic-ops.h" #include "crypto/crypto.h" +#include "hex.h" +#include "span.h" #include "serialization/serialization.h" #include "serialization/debug_archive.h" #include "serialization/binary_archive.h" @@ -443,8 +445,9 @@ namespace cryptonote { static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } } -template<typename T> std::ostream &print256(std::ostream &o, const T &v); -inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { return print256(o, v); } +inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; +} BLOB_SERIALIZER(rct::key); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 7cce0bf04..76a69fbf6 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -883,7 +883,7 @@ namespace cryptonote block b = AUTO_VAL_INIT(b); cryptonote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); - if(!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) + if(!m_core.get_block_template(b, acc, res.difficulty, res.height, res.expected_reward, blob_reserve)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: failed to create block template"; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 3991fc27b..9bdadf0d1 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 9 +#define CORE_RPC_VERSION_MINOR 10 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -692,6 +692,7 @@ namespace cryptonote uint64_t difficulty; uint64_t height; uint64_t reserved_offset; + uint64_t expected_reward; std::string prev_hash; blobdata blocktemplate_blob; blobdata blockhashing_blob; @@ -701,6 +702,7 @@ namespace cryptonote KV_SERIALIZE(difficulty) KV_SERIALIZE(height) KV_SERIALIZE(reserved_offset) + KV_SERIALIZE(expected_reward) KV_SERIALIZE(prev_hash) KV_SERIALIZE(blocktemplate_blob) KV_SERIALIZE(blockhashing_blob) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e925e81b5..6c2df4b22 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -378,7 +378,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); - return false; + return true; } // prompts for a new password, pass true to verify the password @@ -392,7 +392,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) catch (const tools::error::wallet_logic_error& e) { fail_msg_writer() << tr("Error with wallet rewrite: ") << e.what(); - return false; + return true; } return true; @@ -684,6 +684,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [<mixin_count>] <addr> <amount> <lockblocks>(Number of blocks to lock the transaction for, max 1000000) [<payment_id>]")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [mixin] address [payment_id] - Send all unlocked balance to an address")); + m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [mixin] address [payment_id] - Send all unlocked outputs below the threshold to an address")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [<mixin_count>] <amount> [payment_id] - Donate <amount> to the development team (donate.getmonero.org)")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file")); @@ -891,6 +892,15 @@ void simple_wallet::print_seed(std::string seed) std::cout << seed << std::endl; } //---------------------------------------------------------------------------------------------------- +static bool might_be_partial_seed(std::string words) +{ + std::vector<std::string> seed; + + boost::algorithm::trim(words); + boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); + return seed.size() < 24; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { if (!handle_command_line(vm)) @@ -920,14 +930,20 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (m_electrum_seed.empty()) { - m_electrum_seed = command_line::input_line("Specify Electrum seed: "); - if (std::cin.eof()) - return false; - if (m_electrum_seed.empty()) + m_electrum_seed = ""; + do { - fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\""); - return false; - } + const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: "; + std::string electrum_seed = command_line::input_line(prompt); + if (std::cin.eof()) + return false; + if (electrum_seed.empty()) + { + fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\""); + return false; + } + m_electrum_seed += electrum_seed + " "; + } while (might_be_partial_seed(m_electrum_seed)); } if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language)) @@ -2199,8 +2215,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2294,8 +2308,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2474,8 +2486,6 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } @@ -2591,7 +2601,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::sweep_all(const std::vector<std::string> &args_) +bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &args_) { if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } if (!try_connect_to_daemon()) @@ -2692,8 +2702,6 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2701,7 +2709,7 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_all(address, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + auto ptx_vector = m_wallet->create_transactions_all(below, address, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); if (ptx_vector.empty()) { @@ -2739,8 +2747,6 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } @@ -2856,6 +2862,27 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_all(const std::vector<std::string> &args_) +{ + return sweep_main(0, args_); +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_below(const std::vector<std::string> &args_) +{ + uint64_t below = 0; + if (args_.size() < 1) + { + fail_msg_writer() << tr("missing amount threshold"); + return true; + } + if (!cryptonote::parse_amount(below, args_[0])) + { + fail_msg_writer() << tr("invalid amount threshold"); + return true; + } + return sweep_main(below, std::vector<std::string>(++args_.begin(), args_.end())); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector<std::string> &args_) { std::vector<std::string> local_args = args_; @@ -3159,7 +3186,7 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_) if(!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -3844,7 +3871,7 @@ bool simple_wallet::set_tx_note(const std::vector<std::string> &args) if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -3872,7 +3899,7 @@ bool simple_wallet::get_tx_note(const std::vector<std::string> &args) if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -4172,7 +4199,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 14722e942..1b58ff32a 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -134,7 +134,9 @@ namespace cryptonote bool transfer(const std::vector<std::string> &args); bool transfer_new(const std::vector<std::string> &args); bool locked_transfer(const std::vector<std::string> &args); + bool sweep_main(uint64_t below, const std::vector<std::string> &args); bool sweep_all(const std::vector<std::string> &args); + bool sweep_below(const std::vector<std::string> &args); bool sweep_unmixable(const std::vector<std::string> &args); bool donate(const std::vector<std::string> &args); bool sign_transfer(const std::vector<std::string> &args); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index fc2d6e755..21760ac49 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -952,7 +952,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const static_cast<uint32_t>(priority), extra, m_trustedDaemon); } else { - transaction->m_pending_tx = m_wallet->create_transactions_all(addr, fake_outs_count, 0 /* unlock_time */, + transaction->m_pending_tx = m_wallet->create_transactions_all(0, addr, fake_outs_count, 0 /* unlock_time */, static_cast<uint32_t>(priority), extra, m_trustedDaemon); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 941ee8afb..9069789ca 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -82,8 +82,8 @@ using namespace cryptonote; #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003" #define SIGNED_TX_PREFIX "Monero signed tx set\003" -#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone -#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone +#define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone +#define RECENT_OUTPUT_ZONE ((time_t)(1.8 * 86400)) // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al) #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks @@ -105,7 +105,7 @@ 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<std::string> password = {"password", tools::wallet2::tr("Wallet password"), "", true}; + 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}; const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true}; @@ -213,8 +213,10 @@ boost::optional<tools::password_container> get_password(const boost::program_opt return tools::wallet2::password_prompt(verify); } -std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, bool testnet, bool restricted) +std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts) { + const bool testnet = command_line::get_arg(vm, opts.testnet); + /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly fails. This large wrapper is for the use of that macro */ @@ -355,7 +357,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, return false; } - wallet.reset(new tools::wallet2(testnet, restricted)); + wallet.reset(make_basic(vm, opts).release()); wallet->set_refresh_from_block_height(field_scan_from_height); try @@ -467,7 +469,7 @@ boost::optional<password_container> wallet2::password_prompt(const bool new_pass std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file) { const options opts{}; - return generate_from_json(json_file, command_line::get_arg(vm, opts.testnet), command_line::get_arg(vm, opts.restricted)); + return generate_from_json(json_file, vm, opts); } std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file( @@ -498,6 +500,12 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const return {make_basic(vm, opts), std::move(*pwd)}; } +std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm) +{ + const options opts{}; + return make_basic(vm, opts); +} + //---------------------------------------------------------------------------------------------------- bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit) { @@ -1453,7 +1461,7 @@ void wallet2::update_pool_state() std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); while (uit != m_unconfirmed_payments.end()) { - const crypto::hash &txid = uit->first; + const crypto::hash &txid = uit->second.m_tx_hash; bool found = false; for (const auto &it2: res.tx_hashes) { @@ -3643,7 +3651,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> uint64_t i; if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with { - // equiprobable distribution over the recent outs + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53); double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs; @@ -4252,13 +4260,23 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp pending_tx ptx; size_t bytes; - void add(const account_public_address &addr, uint64_t amount, bool merge_destinations) { - std::vector<cryptonote::tx_destination_entry>::iterator i; - i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); }); - if (!merge_destinations || i == dsts.end()) - dsts.push_back(tx_destination_entry(amount,addr)); - else + void add(const account_public_address &addr, uint64_t amount, unsigned int original_output_index, bool merge_destinations) { + if (merge_destinations) + { + std::vector<cryptonote::tx_destination_entry>::iterator i; + i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); }); + if (i == dsts.end()) + dsts.push_back(tx_destination_entry(0,addr)); i->amount += amount; + } + else + { + THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error, "original_output_index too large"); + if (original_output_index == dsts.size()) + dsts.push_back(tx_destination_entry(0,addr)); + THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &addr, sizeof(addr)), error::wallet_internal_error, "Mismatched destination address"); + dsts[original_output_index].amount += amount; + } } }; std::vector<TX> txes; @@ -4347,6 +4365,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // - we have something to send // - or we need to gather more fee // - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2) + unsigned int original_output_index = 0; while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), unused_transfers_indices, unused_dust_indices)) { TX &tx = txes.back(); @@ -4422,17 +4441,18 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // we can fully pay that destination LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].addr) << " for " << print_money(dsts[0].amount)); - tx.add(dsts[0].addr, dsts[0].amount, m_merge_destinations); + tx.add(dsts[0].addr, dsts[0].amount, original_output_index, m_merge_destinations); available_amount -= dsts[0].amount; dsts[0].amount = 0; pop_index(dsts, 0); + ++original_output_index; } if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); - tx.add(dsts[0].addr, available_amount, m_merge_destinations); + tx.add(dsts[0].addr, available_amount, original_output_index, m_merge_destinations); dsts[0].amount -= available_amount; available_amount = 0; } @@ -4561,7 +4581,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp return ptx_vector; } -std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon) +std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon) { std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_dust_indices; @@ -4573,10 +4593,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono const transfer_details& td = m_transfers[i]; if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) { - if (td.is_rct() || is_valid_decomposed_amount(td.amount())) - unused_transfers_indices.push_back(i); - else - unused_dust_indices.push_back(i); + if (below == 0 || td.amount() < below) + { + if (td.is_rct() || is_valid_decomposed_amount(td.amount())) + unused_transfers_indices.push_back(i); + else + unused_dust_indices.push_back(i); + } } } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 6d7bca2a5..03c6a431b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -126,6 +126,9 @@ namespace tools //! 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); + //! Just parses variables. + static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm); + wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} struct transfer_details @@ -411,7 +414,7 @@ namespace tools bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL); std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); - std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); + std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, 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_unmixable_sweep_transactions(bool trusted_daemon); bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index f96b1f51e..f2b3dcaf5 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -45,6 +45,7 @@ using namespace epee; #include "string_coding.h" #include "string_tools.h" #include "crypto/hash.h" +#include "mnemonics/electrum-words.h" #include "rpc/rpc_args.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -56,6 +57,7 @@ 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<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; constexpr const char default_rpc_username[] = "monero"; } @@ -68,8 +70,9 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w), rpc_login_filename(), m_stop(false), m_trusted_daemon(false) - {} + wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_filename(), m_stop(false), m_trusted_daemon(false) + { + } //------------------------------------------------------------------------------------------------------------------------------ wallet_rpc_server::~wallet_rpc_server() { @@ -81,12 +84,17 @@ namespace tools catch (...) {} } //------------------------------------------------------------------------------------------------------------------------------ + void wallet_rpc_server::set_wallet(wallet2 *cr) + { + m_wallet = cr; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run() { m_stop = false; m_net_server.add_idle_handler([this](){ try { - m_wallet.refresh(); + if (m_wallet) m_wallet->refresh(); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } @@ -105,24 +113,55 @@ namespace tools return epee::http_server_impl_base<wallet_rpc_server, connection_context>::run(1, true); } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) + void wallet_rpc_server::stop() { - auto rpc_config = cryptonote::rpc_args::process(vm); + if (m_wallet) + { + m_wallet->store(); + delete m_wallet; + m_wallet = NULL; + } + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::init(const boost::program_options::variables_map *vm) + { + auto rpc_config = cryptonote::rpc_args::process(*vm); if (!rpc_config) return false; + m_vm = vm; + tools::wallet2 *walvars; + std::unique_ptr<tools::wallet2> tmpwal; + + if (m_wallet) + walvars = m_wallet; + else + { + tmpwal = tools::wallet2::make_dummy(*m_vm); + walvars = tmpwal.get(); + } boost::optional<epee::net_utils::http::login> http_login{}; - std::string bind_port = command_line::get_arg(vm, arg_rpc_bind_port); - const bool disable_auth = command_line::get_arg(vm, arg_disable_rpc_login); - m_trusted_daemon = command_line::get_arg(vm, arg_trusted_daemon); - if (!command_line::has_arg(vm, arg_trusted_daemon)) + 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(m_wallet.get_daemon_address())) + if (tools::is_local_address(walvars->get_daemon_address())) { MINFO(tr("Daemon is local, assuming trusted")); m_trusted_daemon = true; } } + if (command_line::has_arg(*m_vm, arg_wallet_dir)) + { + m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir); +#ifdef _WIN32 +#define MKDIR(path, mode) mkdir(path) +#else +#define MKDIR(path, mode) mkdir(path, mode) +#endif + MKDIR(m_wallet_dir.c_str(), 0700); + } if (disable_auth) { @@ -173,7 +212,7 @@ namespace tools LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp); } // end auth enabled - m_http_client.set_server(m_wallet.get_daemon_address(), m_wallet.get_daemon_login()); + m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login()); m_net_server.set_threads_prefix("RPC"); return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init( @@ -181,6 +220,13 @@ namespace tools ); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::not_open(epee::json_rpc::error& er) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_OPEN; + er.message = "No wallet file"; + return false; + } + //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd) { entry.txid = string_tools::pod_to_hex(pd.m_tx_hash); @@ -191,7 +237,7 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; entry.fee = 0; // TODO - entry.note = m_wallet.get_tx_note(pd.m_tx_hash); + entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = "in"; } //------------------------------------------------------------------------------------------------------------------------------ @@ -206,13 +252,13 @@ namespace tools entry.fee = pd.m_amount_in - pd.m_amount_out; uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known entry.amount = pd.m_amount_in - change - entry.fee; - entry.note = m_wallet.get_tx_note(txid); + entry.note = m_wallet->get_tx_note(txid); for (const auto &d: pd.m_dests) { entry.destinations.push_back(wallet_rpc::transfer_destination()); wallet_rpc::transfer_destination &td = entry.destinations.back(); td.amount = d.amount; - td.address = get_account_address_as_str(m_wallet.testnet(), d.addr); + td.address = get_account_address_as_str(m_wallet->testnet(), d.addr); } entry.type = "out"; @@ -230,7 +276,7 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.fee = pd.m_amount_in - pd.m_amount_out; entry.amount = pd.m_amount_in - pd.m_change - entry.fee; - entry.note = m_wallet.get_tx_note(txid); + entry.note = m_wallet->get_tx_note(txid); entry.type = is_failed ? "failed" : "pending"; } //------------------------------------------------------------------------------------------------------------------------------ @@ -244,16 +290,17 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; entry.fee = 0; // TODO - entry.note = m_wallet.get_tx_note(pd.m_tx_hash); + entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = "pool"; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.balance = m_wallet.balance(); - res.unlocked_balance = m_wallet.unlocked_balance(); + res.balance = m_wallet->balance(); + res.unlocked_balance = m_wallet->unlocked_balance(); } catch (const std::exception& e) { @@ -266,9 +313,10 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.address = m_wallet.get_account().get_public_address_str(m_wallet.testnet()); + res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); } catch (const std::exception& e) { @@ -281,9 +329,10 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.height = m_wallet.get_blockchain_current_height(); + res.height = m_wallet->get_blockchain_current_height(); } catch (const std::exception& e) { @@ -303,7 +352,7 @@ namespace tools cryptonote::tx_destination_entry de; bool has_payment_id; crypto::hash8 new_payment_id; - if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet.testnet(), it->address, false)) + if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; @@ -374,7 +423,8 @@ namespace tools std::vector<uint8_t> extra; LOG_PRINT_L3("on_transfer_split starts"); - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -390,11 +440,11 @@ namespace tools try { uint64_t mixin = req.mixin; - if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) { + if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) { LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2"); mixin = 2; } - std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); // reject proposed transactions if there are more than one. see on_transfer_split below. if (ptx_vector.size() != 1) @@ -404,7 +454,7 @@ namespace tools return false; } - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hash res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx)); @@ -442,7 +492,8 @@ namespace tools std::vector<cryptonote::tx_destination_entry> dsts; std::vector<uint8_t> extra; - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -458,17 +509,17 @@ namespace tools try { uint64_t mixin = req.mixin; - if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) { + if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) { LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2"); mixin = 2; } std::vector<wallet2::pending_tx> ptx_vector; LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); - ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); LOG_PRINT_L2("on_transfer_split calling commit_txyy"); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); LOG_PRINT_L2("on_transfer_split called commit_txyy"); // populate response with tx hashes @@ -507,7 +558,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -516,9 +568,9 @@ 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(m_trusted_daemon); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -559,7 +611,8 @@ namespace tools std::vector<cryptonote::tx_destination_entry> dsts; std::vector<uint8_t> extra; - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -578,9 +631,9 @@ namespace tools try { - std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_all(dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -618,6 +671,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { crypto::hash8 payment_id; @@ -635,7 +689,7 @@ namespace tools } } - res.integrated_address = m_wallet.get_account().get_public_integrated_address_str(payment_id, m_wallet.testnet()); + res.integrated_address = m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet()); res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } @@ -650,13 +704,14 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { cryptonote::account_public_address address; crypto::hash8 payment_id; bool has_payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet.testnet(), req.integrated_address)) + if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), req.integrated_address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = "Invalid address"; @@ -668,7 +723,7 @@ namespace tools er.message = "Address is not an integrated address"; return false; } - res.standard_address = get_account_address_as_str(m_wallet.testnet(),address); + res.standard_address = get_account_address_as_str(m_wallet->testnet(),address); res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } @@ -683,7 +738,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -692,7 +748,7 @@ namespace tools try { - m_wallet.store(); + m_wallet->store(); } catch (const std::exception& e) { @@ -705,6 +761,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); crypto::hash payment_id; crypto::hash8 payment_id8; cryptonote::blobdata payment_id_blob; @@ -734,7 +791,7 @@ namespace tools res.payments.clear(); std::list<wallet2::payment_details> payment_list; - m_wallet.get_payments(payment_id, payment_list); + m_wallet->get_payments(payment_id, payment_list); for (auto & payment : payment_list) { wallet_rpc::payment_details rpc_payment; @@ -752,12 +809,13 @@ namespace tools bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er) { res.payments.clear(); + if (!m_wallet) return not_open(er); /* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */ if (req.payment_ids.empty()) { std::list<std::pair<crypto::hash,wallet2::payment_details>> payment_list; - m_wallet.get_payments(payment_list, req.min_block_height); + m_wallet->get_payments(payment_list, req.min_block_height); for (auto & payment : payment_list) { @@ -806,7 +864,7 @@ namespace tools } std::list<wallet2::payment_details> payment_list; - m_wallet.get_payments(payment_id, payment_list, req.min_block_height); + m_wallet->get_payments(payment_id, payment_list, req.min_block_height); for (auto & payment : payment_list) { @@ -825,6 +883,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); if(req.transfer_type.compare("all") != 0 && req.transfer_type.compare("available") != 0 && req.transfer_type.compare("unavailable") != 0) { er.code = WALLET_RPC_ERROR_CODE_TRANSFER_TYPE; @@ -846,7 +905,7 @@ namespace tools } wallet2::transfer_container transfers; - m_wallet.get_transfers(transfers); + m_wallet->get_transfers(transfers); bool transfers_found = false; for (const auto& td : transfers) @@ -873,7 +932,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -882,7 +942,7 @@ namespace tools if (req.key_type.compare("mnemonic") == 0) { - if (!m_wallet.get_seed(res.key)) + if (!m_wallet->get_seed(res.key)) { er.message = "The wallet is non-deterministic. Cannot display seed."; return false; @@ -890,7 +950,7 @@ namespace tools } else if(req.key_type.compare("view_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet.get_account().get_keys().m_view_secret_key); + res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); } else { @@ -903,7 +963,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -912,7 +973,7 @@ namespace tools try { - m_wallet.rescan_blockchain(); + m_wallet->rescan_blockchain(); } catch (const std::exception& e) { @@ -925,20 +986,22 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; return false; } - res.signature = m_wallet.sign(req.data); + res.signature = m_wallet->sign(req.data); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -948,20 +1011,21 @@ namespace tools cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet.testnet(), req.address, false)) + if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = ""; return false; } - res.good = m_wallet.verify(req.data, address, req.signature); + res.good = m_wallet->verify(req.data, address, req.signature); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -970,7 +1034,7 @@ namespace tools try { - m_wallet.store(); + m_wallet->store(); m_stop.store(true, std::memory_order_relaxed); } catch (const std::exception& e) @@ -984,7 +1048,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1018,7 +1083,7 @@ namespace tools std::list<std::string>::const_iterator in = req.notes.begin(); while (il != txids.end()) { - m_wallet.set_tx_note(*il++, *in++); + m_wallet->set_tx_note(*il++, *in++); } return true; @@ -1027,6 +1092,7 @@ namespace tools bool wallet_rpc_server::on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er) { res.notes.clear(); + if (!m_wallet) return not_open(er); std::list<crypto::hash> txids; std::list<std::string>::const_iterator i = req.txids.begin(); @@ -1047,14 +1113,15 @@ namespace tools std::list<crypto::hash>::const_iterator il = txids.begin(); while (il != txids.end()) { - res.notes.push_back(m_wallet.get_tx_note(*il++)); + res.notes.push_back(m_wallet->get_tx_note(*il++)); } return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1071,7 +1138,7 @@ namespace tools if (req.in) { std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_payments(payments, min_height, max_height); + m_wallet->get_payments(payments, min_height, max_height); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.in.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second); @@ -1081,7 +1148,7 @@ namespace tools if (req.out) { std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments; - m_wallet.get_payments_out(payments, min_height, max_height); + m_wallet->get_payments_out(payments, min_height, max_height); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.out.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.out.back(), i->first, i->second); @@ -1090,7 +1157,7 @@ namespace tools if (req.pending || req.failed) { std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments; - m_wallet.get_unconfirmed_payments_out(upayments); + m_wallet->get_unconfirmed_payments_out(upayments); for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { const tools::wallet2::unconfirmed_transfer_details &pd = i->second; bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; @@ -1104,10 +1171,10 @@ namespace tools if (req.pool) { - m_wallet.update_pool_state(); + m_wallet->update_pool_state(); std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_unconfirmed_payments(payments); + m_wallet->get_unconfirmed_payments(payments); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.pool.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.pool.back(), i->first, i->second); @@ -1119,7 +1186,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1147,7 +1215,7 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_payments(payments, 0); + m_wallet->get_payments(payments, 0); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { if (i->second.m_tx_hash == txid) { @@ -1157,7 +1225,7 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out; - m_wallet.get_payments_out(payments_out, 0); + m_wallet->get_payments_out(payments_out, 0); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) { if (i->first == txid) { @@ -1167,7 +1235,7 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments; - m_wallet.get_unconfirmed_payments_out(upayments); + m_wallet->get_unconfirmed_payments_out(upayments); for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { if (i->first == txid) { @@ -1176,10 +1244,10 @@ namespace tools } } - m_wallet.update_pool_state(); + m_wallet->update_pool_state(); std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments; - m_wallet.get_unconfirmed_payments(pool_payments); + m_wallet->get_unconfirmed_payments(pool_payments); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { if (i->second.m_tx_hash == txid) { @@ -1195,9 +1263,10 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet.export_key_images(); + std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet->export_key_images(); res.signed_key_images.resize(ski.size()); for (size_t n = 0; n < ski.size(); ++n) { @@ -1218,7 +1287,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1250,7 +1320,7 @@ namespace tools ski[n].second = *reinterpret_cast<const crypto::signature*>(bd.data()); } uint64_t spent = 0, unspent = 0; - uint64_t height = m_wallet.import_key_images(ski, spent, unspent); + uint64_t height = m_wallet->import_key_images(ski, spent, unspent); res.spent = spent; res.unspent = unspent; res.height = height; @@ -1269,7 +1339,7 @@ namespace tools bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er) { std::string error; - std::string uri = m_wallet.make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error); + std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error); if (uri.empty()) { er.code = WALLET_RPC_ERROR_CODE_WRONG_URI; @@ -1283,8 +1353,9 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); std::string error; - if (!m_wallet.parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error)) + if (!m_wallet->parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_URI; er.message = "Error parsing URI: " + error; @@ -1295,12 +1366,13 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::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) { - const auto ab = m_wallet.get_address_book(); + if (!m_wallet) return not_open(er); + const auto ab = m_wallet->get_address_book(); if (req.entries.empty()) { uint64_t idx = 0; for (const auto &entry: ab) - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet.testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); } else { @@ -1313,7 +1385,7 @@ namespace tools return false; } const auto &entry = ab[idx]; - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet.testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); } } return true; @@ -1321,7 +1393,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::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) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1332,7 +1405,7 @@ namespace tools bool has_payment_id; crypto::hash8 payment_id8; crypto::hash payment_id = cryptonote::null_hash; - if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet.testnet(), req.address, false)) + if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address; @@ -1370,33 +1443,34 @@ namespace tools } } } - if (!m_wallet.add_address_book_row(address, payment_id, req.description)) + if (!m_wallet->add_address_book_row(address, payment_id, req.description)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to add address book entry"; return false; } - res.index = m_wallet.get_address_book().size(); + res.index = m_wallet->get_address_book().size(); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::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) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; return false; } - const auto ab = m_wallet.get_address_book(); + const auto ab = m_wallet->get_address_book(); if (req.index >= ab.size()) { er.code = WALLET_RPC_ERROR_CODE_WRONG_INDEX; er.message = "Index out of range: " + std::to_string(req.index); return false; } - if (!m_wallet.delete_address_book_row(req.index)) + if (!m_wallet->delete_address_book_row(req.index)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to delete address book entry"; @@ -1407,7 +1481,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ 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.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1415,7 +1490,7 @@ namespace tools } try { - m_wallet.rescan_spent(); + m_wallet->rescan_spent(); return true; } catch (const std::exception &e) @@ -1429,6 +1504,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) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -1445,7 +1521,7 @@ namespace tools } cryptonote::COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req); - daemon_req.miner_address = m_wallet.get_account().get_public_address_str(m_wallet.testnet()); + daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); daemon_req.threads_count = req.threads_count; daemon_req.do_background_mining = req.do_background_mining; daemon_req.ignore_battery = req.ignore_battery; @@ -1475,6 +1551,152 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::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) + { + crypto::ElectrumWords::get_language_list(res.languages); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::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) + { + if (m_wallet_dir.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "No wallet dir configured"; + return false; + } + + namespace po = boost::program_options; + po::variables_map vm2; + const char *ptr = strchr(req.filename.c_str(), '/'); +#ifdef _WIN32 + if (!ptr) + ptr = strchr(req.filename.c_str(), '\\'); + if (!ptr) + ptr = strchr(req.filename.c_str(), ':'); +#endif + if (ptr) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Invalid filename"; + return false; + } + std::string wallet_file = m_wallet_dir + "/" + req.filename; + { + std::vector<std::string> languages; + crypto::ElectrumWords::get_language_list(languages); + std::vector<std::string>::iterator it; + std::string wallet_file; + char *ptr; + + it = std::find(languages.begin(), languages.end(), req.language); + if (it == languages.end()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Unknown language"; + return false; + } + } + { + po::options_description desc("dummy"); + const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"}; + const char *argv[4]; + int argc = 3; + argv[0] = "wallet-rpc"; + argv[1] = "--password"; + argv[2] = req.password.c_str(); + argv[3] = NULL; + vm2 = *m_vm; + 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).first; + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to create wallet"; + return false; + } + wal->set_seed_language(req.language); + cryptonote::COMMAND_RPC_GET_HEIGHT::request hreq; + cryptonote::COMMAND_RPC_GET_HEIGHT::response hres; + hres.height = 0; + bool r = net_utils::invoke_http_json("/getheight", hreq, hres, m_http_client); + wal->set_refresh_from_block_height(hres.height); + crypto::secret_key dummy_key; + try { + wal->generate(wallet_file, req.password, dummy_key, false, false); + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to generate wallet"; + return false; + } + if (m_wallet) + delete m_wallet; + m_wallet = wal.release(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::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) + { + if (m_wallet_dir.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "No wallet dir configured"; + return false; + } + + namespace po = boost::program_options; + po::variables_map vm2; + const char *ptr = strchr(req.filename.c_str(), '/'); +#ifdef _WIN32 + if (!ptr) + ptr = strchr(req.filename.c_str(), '\\'); + if (!ptr) + ptr = strchr(req.filename.c_str(), ':'); +#endif + if (ptr) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Invalid filename"; + return false; + } + std::string wallet_file = m_wallet_dir + "/" + req.filename; + { + po::options_description desc("dummy"); + const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"}; + const char *argv[4]; + int argc = 3; + argv[0] = "wallet-rpc"; + argv[1] = "--password"; + argv[2] = req.password.c_str(); + argv[3] = NULL; + vm2 = *m_vm; + command_line::add_arg(desc, arg_password); + po::store(po::parse_command_line(argc, argv, desc), vm2); + } + std::unique_ptr<tools::wallet2> wal; + try { + wal = tools::wallet2::make_from_file(vm2, wallet_file).first; + } + catch (const std::exception& e) + { + wal = nullptr; + } + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to open wallet"; + return false; + } + if (m_wallet) + delete m_wallet; + m_wallet = wal.release(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ } int main(int argc, char** argv) { @@ -1491,11 +1713,12 @@ int main(int argc, char** argv) { cryptonote::rpc_args::init_options(desc_params); command_line::add_arg(desc_params, arg_wallet_file); command_line::add_arg(desc_params, arg_from_json); + command_line::add_arg(desc_params, arg_wallet_dir); const auto vm = wallet_args::main( argc, argv, - "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>] [--rpc-bind-port=<port>]", + "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]", desc_params, po::positional_options_description(), "monero-wallet-rpc.log", @@ -1511,6 +1734,7 @@ int main(int argc, char** argv) { { const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file); const auto from_json = command_line::get_arg(*vm, arg_from_json); + const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir); if(!wallet_file.empty() && !from_json.empty()) { @@ -1518,9 +1742,15 @@ int main(int argc, char** argv) { return 1; } + if (!wallet_dir.empty()) + { + wal = NULL; + goto just_dir; + } + if (wallet_file.empty() && from_json.empty()) { - LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json")); + LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); return 1; } @@ -1561,8 +1791,10 @@ int main(int argc, char** argv) { LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); return 1; } - tools::wallet_rpc_server wrpc(*wal); - bool r = wrpc.init(*vm); +just_dir: + tools::wallet_rpc_server wrpc; + if (wal) wrpc.set_wallet(wal.release()); + bool r = wrpc.init(&(vm.get())); CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet rpc server")); tools::signal_handler::install([&wrpc, &wal](int) { wrpc.send_stop_signal(); @@ -1573,7 +1805,7 @@ int main(int argc, char** argv) { try { LOG_PRINT_L0(tools::wallet_rpc_server::tr("Storing wallet...")); - wal->store(); + wrpc.stop(); LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stored ok")); } catch (const std::exception& e) diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index eafe3fc87..230dcee5b 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -52,11 +52,14 @@ namespace tools static const char* tr(const char* str); - wallet_rpc_server(wallet2& cr); + wallet_rpc_server(); ~wallet_rpc_server(); - bool init(const boost::program_options::variables_map& vm); + bool init(const boost::program_options::variables_map *vm); bool run(); + void stop(); + void set_wallet(wallet2 *cr); + private: CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map @@ -95,6 +98,9 @@ namespace tools 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) END_JSON_RPC_MAP() END_URI_MAP2() @@ -131,6 +137,9 @@ namespace tools 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); //json rpc v2 bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er); @@ -140,11 +149,14 @@ namespace tools void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd); + bool not_open(epee::json_rpc::error& er); - wallet2& m_wallet; + wallet2 *m_wallet; + std::string m_wallet_dir; std::string rpc_login_filename; std::atomic<bool> m_stop; bool m_trusted_daemon; epee::net_utils::http::http_simple_client m_http_client; + 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 49d1c59fb..3c10dc41f 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -234,6 +234,7 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_keys; + uint64_t below_amount; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) @@ -242,6 +243,7 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) + KV_SERIALIZE(below_amount) END_KV_SERIALIZE_MAP() }; @@ -909,5 +911,61 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_GET_LANGUAGES + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + struct response + { + std::vector<std::string> languages; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(languages) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_CREATE_WALLET + { + struct request + { + std::string filename; + std::string password; + std::string language; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(filename) + KV_SERIALIZE(password) + KV_SERIALIZE(language) + END_KV_SERIALIZE_MAP() + }; + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_OPEN_WALLET + { + struct request + { + std::string filename; + std::string password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(filename) + KV_SERIALIZE(password) + END_KV_SERIALIZE_MAP() + }; + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; } } diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 6f1c3bbbd..3c79c0ac3 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -43,3 +43,4 @@ #define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE -10 #define WALLET_RPC_ERROR_CODE_WRONG_URI -11 #define WALLET_RPC_ERROR_CODE_WRONG_INDEX -12 +#define WALLET_RPC_ERROR_CODE_NOT_OPEN -13 diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 3c0e8067e..da95a97a9 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -36,6 +36,7 @@ set(unit_tests_sources chacha8.cpp checkpoints.cpp command_line.cpp + crypto.cpp decompose_amount_into_digits.cpp dns_resolver.cpp epee_boosted_tcp_server.cpp diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp new file mode 100644 index 000000000..3a8e787ec --- /dev/null +++ b/tests/unit_tests/crypto.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <cstdint> +#include <gtest/gtest.h> +#include <memory> +#include <sstream> +#include <string> + +#include "cryptonote_basic/cryptonote_basic_impl.h" + +namespace +{ + static constexpr const std::uint8_t source[] = { + 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, + 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94, + 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94, + 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea + }; + + static constexpr const char expected[] = + "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94" + "6c7251d54154cfa92c173a0dd39c1f948b655970153799af2aeadc9ff1add0ea"; + + template<typename T> + bool is_formatted() + { + T value{}; + + static_assert(alignof(T) == 1, "T must have 1 byte alignment"); + static_assert(sizeof(T) <= sizeof(source), "T is too large for source"); + static_assert(sizeof(T) * 2 <= sizeof(expected), "T is too large for destination"); + std::memcpy(std::addressof(value), source, sizeof(T)); + + std::stringstream out; + out << "BEGIN" << value << "END"; + return out.str() == "BEGIN<" + std::string{expected, sizeof(T) * 2} + ">END"; + } +} + +TEST(Crypto, Ostream) +{ + EXPECT_TRUE(is_formatted<crypto::hash8>()); + EXPECT_TRUE(is_formatted<crypto::hash>()); + EXPECT_TRUE(is_formatted<crypto::public_key>()); + EXPECT_TRUE(is_formatted<crypto::secret_key>()); + EXPECT_TRUE(is_formatted<crypto::signature>()); + EXPECT_TRUE(is_formatted<crypto::key_derivation>()); + EXPECT_TRUE(is_formatted<crypto::key_image>()); +} diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 25b2bddd9..e8ddbe3f5 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -27,18 +27,342 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <array> +#include <boost/range/algorithm/equal.hpp> +#include <boost/range/algorithm_ext/iota.hpp> +#include <cstdint> +#include <gtest/gtest.h> +#include <iterator> +#include <string> +#include <sstream> +#include <vector> + #ifdef _WIN32 # include <winsock.h> #else # include <arpa/inet.h> #endif -#include <cstdint> -#include <gtest/gtest.h> -#include <string> - +#include "hex.h" +#include "span.h" #include "string_tools.h" +namespace +{ + template<typename Destination, typename Source> + bool can_construct() + { + const unsigned count = + unsigned(std::is_constructible<Destination, Source>()) + + unsigned(std::is_constructible<Destination, Source&>()) + + unsigned(std::is_convertible<Source, Destination>()) + + unsigned(std::is_convertible<Source&, Destination>()) + + unsigned(std::is_assignable<Destination, Source>()) + + unsigned(std::is_assignable<Destination, Source&>()); + EXPECT_TRUE(count == 6 || count == 0) << + "Mismatch on construction results - " << count << " were true"; + return count == 6; + } + + // This is probably stressing the compiler more than the implementation ... + constexpr const epee::span<const char> test_string("a string"); + static_assert(!test_string.empty(), "test failure"); + static_assert(test_string.size() == 9, "test failure"); + static_assert(test_string.size_bytes() == 9, "test_failure"); + static_assert(test_string.begin() == test_string.cbegin(), "test failure"); + static_assert(test_string.end() == test_string.cend(), "test failure"); + static_assert(test_string.cend() - test_string.cbegin() == 9, "test failure"); + static_assert(*test_string.cbegin() == 'a', "test failure"); + static_assert(*(test_string.cend() - 2) == 'g', "test failure"); + static_assert( + epee::span<const char>(test_string).cbegin() + 3 == test_string.cbegin() + 3, + "test failure" + ); + + static_assert(epee::span<char>().empty(), "test failure"); + static_assert(epee::span<char>(nullptr).empty(), "test failure"); + static_assert(epee::span<const char>("foo", 2).size() == 2, "test failure"); + + std::string std_to_hex(const std::vector<unsigned char>& source) + { + std::stringstream out; + out << std::hex; + for (const unsigned char byte : source) + { + out << std::setw(2) << std::setfill('0') << int(byte); + } + return out.str(); + } + + std::vector<unsigned char> get_all_bytes() + { + std::vector<unsigned char> out; + out.resize(256); + boost::range::iota(out, 0); + return out; + } +} + +TEST(Span, Traits) +{ + EXPECT_TRUE((std::is_same<std::size_t, typename epee::span<char>::size_type>())); + EXPECT_TRUE((std::is_same<std::ptrdiff_t, typename epee::span<char>::difference_type>())); + EXPECT_TRUE((std::is_same<char, typename epee::span<char>::value_type>())); + EXPECT_TRUE((std::is_same<char*, typename epee::span<char>::pointer>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<char>::const_pointer>())); + EXPECT_TRUE((std::is_same<char*, typename epee::span<char>::iterator>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<char>::const_iterator>())); + EXPECT_TRUE((std::is_same<char&, typename epee::span<char>::reference>())); + EXPECT_TRUE((std::is_same<const char&, typename epee::span<char>::const_reference>())); + + EXPECT_TRUE((std::is_same<std::size_t, typename epee::span<const char>::size_type>())); + EXPECT_TRUE((std::is_same<std::ptrdiff_t, typename epee::span<const char>::difference_type>())); + EXPECT_TRUE((std::is_same<const char, typename epee::span<const char>::value_type>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<const char>::pointer>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<const char>::const_pointer>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<const char>::iterator>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<const char>::const_iterator>())); + EXPECT_TRUE((std::is_same<const char&, typename epee::span<const char>::reference>())); + EXPECT_TRUE((std::is_same<const char&, typename epee::span<const char>::const_reference>())); +} + +TEST(Span, MutableConstruction) +{ + struct no_conversion{}; + + EXPECT_TRUE(std::is_constructible<epee::span<char>>()); + EXPECT_TRUE((std::is_constructible<epee::span<char>, char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible<epee::span<char>, const char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible<epee::span<char>, unsigned char*, std::size_t>())); + + EXPECT_TRUE((can_construct<epee::span<char>, std::nullptr_t>())); + EXPECT_TRUE((can_construct<epee::span<char>, char(&)[1]>())); + + EXPECT_FALSE((can_construct<epee::span<char>, std::vector<char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, std::array<char, 1>>())); + + EXPECT_FALSE((can_construct<epee::span<char>, std::wstring>())); + EXPECT_FALSE((can_construct<epee::span<char>, const std::vector<char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, std::vector<unsigned char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, const std::array<char, 1>>())); + EXPECT_FALSE((can_construct<epee::span<char>, std::array<unsigned char, 1>>())); + EXPECT_FALSE((can_construct<epee::span<char>, const char[1]>())); + EXPECT_FALSE((can_construct<epee::span<char>, unsigned char[1]>())); + EXPECT_FALSE((can_construct<epee::span<char>, epee::span<const char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, epee::span<unsigned char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, no_conversion>())); +} + +TEST(Span, ImmutableConstruction) +{ + struct no_conversion{}; + + EXPECT_TRUE(std::is_constructible<epee::span<const char>>()); + EXPECT_TRUE((std::is_constructible<epee::span<const char>, char*, std::size_t>())); + EXPECT_TRUE((std::is_constructible<epee::span<const char>, const char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible<epee::span<const char>, unsigned char*, std::size_t>())); + + EXPECT_FALSE((can_construct<epee::span<const char>, std::string>())); + EXPECT_FALSE((can_construct<epee::span<const char>, std::vector<char>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, const std::vector<char>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, std::array<char, 1>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, const std::array<char, 1>>())); + + EXPECT_TRUE((can_construct<epee::span<const char>, std::nullptr_t>())); + EXPECT_TRUE((can_construct<epee::span<const char>, char[1]>())); + EXPECT_TRUE((can_construct<epee::span<const char>, const char[1]>())); + EXPECT_TRUE((can_construct<epee::span<const char>, epee::span<const char>>())); + + EXPECT_FALSE((can_construct<epee::span<const char>, std::wstring>())); + EXPECT_FALSE((can_construct<epee::span<const char>, std::vector<unsigned char>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, std::array<unsigned char, 1>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, unsigned char[1]>())); + EXPECT_FALSE((can_construct<epee::span<const char>, epee::span<unsigned char>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, no_conversion>())); +} + +TEST(Span, NoExcept) +{ + EXPECT_TRUE(std::is_nothrow_default_constructible<epee::span<char>>()); + EXPECT_TRUE(std::is_nothrow_move_constructible<epee::span<char>>()); + EXPECT_TRUE(std::is_nothrow_copy_constructible<epee::span<char>>()); + EXPECT_TRUE(std::is_move_assignable<epee::span<char>>()); + EXPECT_TRUE(std::is_copy_assignable<epee::span<char>>()); + + char data[10]; + epee::span<char> lvalue(data); + const epee::span<char> clvalue(data); + EXPECT_TRUE(noexcept(epee::span<char>())); + EXPECT_TRUE(noexcept(epee::span<char>(nullptr))); + EXPECT_TRUE(noexcept(epee::span<char>(nullptr, 0))); + EXPECT_TRUE(noexcept(epee::span<char>(data))); + EXPECT_TRUE(noexcept(epee::span<char>(lvalue))); + EXPECT_TRUE(noexcept(epee::span<char>(clvalue))); + + // conversion from mutable to immutable not yet implemented + // EXPECT_TRUE(noexcept(epee::span<const char>(lvalue))); + // EXPECT_TRUE(noexcept(epee::span<const char>(clvalue))); + + EXPECT_TRUE(noexcept(epee::span<char>(epee::span<char>(lvalue)))); + EXPECT_TRUE(noexcept(lvalue = lvalue)); + EXPECT_TRUE(noexcept(lvalue = clvalue)); + EXPECT_TRUE(noexcept(lvalue = epee::span<char>(lvalue))); +} + +TEST(Span, Nullptr) +{ + const auto check_empty = [](epee::span<const char> data) + { + EXPECT_TRUE(data.empty()); + EXPECT_EQ(data.cbegin(), data.begin()); + EXPECT_EQ(data.cend(), data.end()); + EXPECT_EQ(data.cend(), data.cbegin()); + EXPECT_EQ(0, data.size()); + EXPECT_EQ(0, data.size_bytes()); + }; + check_empty({}); + check_empty(nullptr); +} + +TEST(Span, Writing) +{ + const int expected[] = {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector<int> source; + + epee::span<int> span; + EXPECT_TRUE(span.empty()); + EXPECT_EQ(0, span.size()); + EXPECT_EQ(0, span.size_bytes()); + + source.resize(15); + span = {source.data(), source.size()}; + EXPECT_FALSE(span.empty()); + EXPECT_EQ(15, span.size()); + EXPECT_EQ(15 * 4, span.size_bytes()); + + boost::range::iota(span, -5); + EXPECT_EQ(span.begin(), span.cbegin()); + EXPECT_EQ(span.end(), span.cend()); + EXPECT_TRUE(boost::range::equal(expected, source)); + EXPECT_TRUE(boost::range::equal(expected, span)); +} + +TEST(Span, ToByteSpan) +{ + const char expected[] = {56, 44, 11, 5}; + EXPECT_TRUE( + boost::range::equal( + std::array<std::uint8_t, 4>{{56, 44, 11, 5}}, + epee::to_byte_span<char>(expected) + ) + ); + EXPECT_TRUE( + boost::range::equal( + std::array<char, 4>{{56, 44, 11, 5}}, + epee::to_byte_span(epee::span<const char>{expected}) + ) + ); +} + +TEST(Span, AsByteSpan) +{ + struct some_pod { char value[4]; }; + const some_pod immutable {{ 5, 10, 12, 127 }}; + EXPECT_TRUE( + boost::range::equal( + std::array<unsigned char, 4>{{5, 10, 12, 127}}, + epee::as_byte_span(immutable) + ) + ); + EXPECT_TRUE( + boost::range::equal( + std::array<std::uint8_t, 3>{{'a', 'y', 0x00}}, epee::as_byte_span("ay") + ) + ); +} + +TEST(ToHex, String) +{ + EXPECT_TRUE(epee::to_hex::string(nullptr).empty()); + EXPECT_EQ( + std::string{"ffab0100"}, + epee::to_hex::string(epee::as_byte_span("\xff\xab\x01")) + ); + + const std::vector<unsigned char> all_bytes = get_all_bytes(); + EXPECT_EQ( + std_to_hex(all_bytes), epee::to_hex::string(epee::to_span(all_bytes)) + ); +} + +TEST(ToHex, Array) +{ + EXPECT_EQ( + (std::array<char, 8>{{'f', 'f', 'a', 'b', '0', '1', '0', '0'}}), + (epee::to_hex::array(std::array<unsigned char, 4>{{0xFF, 0xAB, 0x01, 0x00}})) + ); +} + +TEST(ToHex, Ostream) +{ + std::stringstream out; + epee::to_hex::buffer(out, nullptr); + EXPECT_TRUE(out.str().empty()); + + { + const std::uint8_t source[] = {0xff, 0xab, 0x01, 0x00}; + epee::to_hex::buffer(out, source); + } + + std::string expected{"ffab0100"}; + EXPECT_EQ(expected, out.str()); + + const std::vector<unsigned char> all_bytes = get_all_bytes(); + + expected.append(std_to_hex(all_bytes)); + epee::to_hex::buffer(out, epee::to_span(all_bytes)); + EXPECT_EQ(expected, out.str()); +} + +TEST(ToHex, Formatted) +{ + std::stringstream out; + std::string expected{"<>"}; + + epee::to_hex::formatted(out, nullptr); + EXPECT_EQ(expected, out.str()); + + expected.append("<ffab0100>"); + epee::to_hex::formatted(out, epee::as_byte_span("\xFF\xAB\x01")); + EXPECT_EQ(expected, out.str()); + + const std::vector<unsigned char> all_bytes = get_all_bytes(); + + expected.append("<").append(std_to_hex(all_bytes)).append(">"); + epee::to_hex::formatted(out, epee::to_span(all_bytes)); + EXPECT_EQ(expected, out.str()); +} + +TEST(StringTools, BuffToHex) +{ + const std::vector<unsigned char> all_bytes = get_all_bytes(); + + EXPECT_EQ( + std_to_hex(all_bytes), + (epee::string_tools::buff_to_hex_nodelimer( + std::string{reinterpret_cast<const char*>(all_bytes.data()), all_bytes.size()} + )) + ); +} + +TEST(StringTools, PodToHex) +{ + struct some_pod { unsigned char data[4]; }; + EXPECT_EQ( + std::string{"ffab0100"}, + (epee::string_tools::pod_to_hex(some_pod{{0xFF, 0xAB, 0x01, 0x00}})) + ); +} + TEST(StringTools, GetIpString) { EXPECT_EQ( diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 94e226247..e00b87e30 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -34,6 +34,7 @@ #include <time.h> #include <iostream> #include <boost/algorithm/string.hpp> +#include "mnemonics/chinese_simplified.h" #include "mnemonics/english.h" #include "mnemonics/spanish.h" #include "mnemonics/portuguese.h" @@ -155,6 +156,7 @@ TEST(mnemonics, all_languages) { srand(time(NULL)); std::vector<Language::Base*> languages({ + Language::Singleton<Language::Chinese_Simplified>::instance(), Language::Singleton<Language::English>::instance(), Language::Singleton<Language::Spanish>::instance(), Language::Singleton<Language::Portuguese>::instance(), @@ -163,7 +165,7 @@ TEST(mnemonics, all_languages) Language::Singleton<Language::Italian>::instance(), Language::Singleton<Language::Russian>::instance(), Language::Singleton<Language::French>::instance(), - Language::Singleton<Language::Dutch>::instance(), + Language::Singleton<Language::Dutch>::instance() }); for (std::vector<Language::Base*>::iterator it = languages.begin(); it != languages.end(); it++) diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 0e2cb3903..af6afa636 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -32,6 +32,7 @@ #include <cstdint> #include <algorithm> +#include <sstream> #include "ringct/rctTypes.h" #include "ringct/rctSigs.h" @@ -1048,3 +1049,13 @@ TEST(ringct, reject_gen_non_simple_ver_simple) rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true); ASSERT_FALSE(rct::verRctSimple(sig)); } + +TEST(ringct, key_ostream) +{ + std::stringstream out; + out << "BEGIN" << rct::H << "END"; + EXPECT_EQ( + std::string{"BEGIN<8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94>END"}, + out.str() + ); +} diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 7afc0d60c..6f9fe7d11 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1000,8 +1000,6 @@ TEST(Serialization, portability_unsigned_tx) ASSERT_TRUE(tcd.selected_transfers.front() == 2); // tcd.extra ASSERT_TRUE(tcd.extra.size() == 68); - string tcd_extra_str = epee::string_tools::buff_to_hex(string(reinterpret_cast<char*>(tcd.extra.data()), tcd.extra.size())); - ASSERT_TRUE(tcd_extra_str == "0x2 0x21 0x0 0xf8 0xd 0xbc 0xfc 0xa2 0x2d 0x84 0x1e 0xa0 0x46 0x18 0x7a 0x5b 0x19 0xea 0x4d 0xd1 0xa2 0x8a 0x58 0xa8 0x72 0x9 0xd5 0xdf 0x2 0x30 0x60 0xac 0x9e 0x48 0x84 0x1 0xb2 0xfd 0x5d 0x4e 0x45 0x8b 0xf1 0x28 0xa0 0xc8 0x30 0xd1 0x35 0x4f 0x47 0xb9 0xed 0xc9 0x82 0x8c 0x83 0x37 0x7d 0xb6 0xb5 0xe5 0x3d 0xff 0x64 0xb0 0xde 0x7f "); // tcd.{unlock_time, use_rct} ASSERT_TRUE(tcd.unlock_time == 0); ASSERT_TRUE(tcd.use_rct); @@ -1157,8 +1155,6 @@ TEST(Serialization, portability_signed_tx) ASSERT_TRUE(tcd.selected_transfers.front() == 2); // ptx.construction_data.extra ASSERT_TRUE(tcd.extra.size() == 68); - string tcd_extra_str = epee::string_tools::buff_to_hex(string(reinterpret_cast<char*>(tcd.extra.data()), tcd.extra.size())); - ASSERT_TRUE(tcd_extra_str == "0x2 0x21 0x0 0xf8 0xd 0xbc 0xfc 0xa2 0x2d 0x84 0x1e 0xa0 0x46 0x18 0x7a 0x5b 0x19 0xea 0x4d 0xd1 0xa2 0x8a 0x58 0xa8 0x72 0x9 0xd5 0xdf 0x2 0x30 0x60 0xac 0x9e 0x48 0x84 0x1 0xb2 0xfd 0x5d 0x4e 0x45 0x8b 0xf1 0x28 0xa0 0xc8 0x30 0xd1 0x35 0x4f 0x47 0xb9 0xed 0xc9 0x82 0x8c 0x83 0x37 0x7d 0xb6 0xb5 0xe5 0x3d 0xff 0x64 0xb0 0xde 0x7f "); // ptx.construction_data.{unlock_time, use_rct} ASSERT_TRUE(tcd.unlock_time == 0); ASSERT_TRUE(tcd.use_rct); diff --git a/utils/gpg_keys/kenshi84.asc b/utils/gpg_keys/kenshi84.asc index d146beac2..d2de312ef 100644 --- a/utils/gpg_keys/kenshi84.asc +++ b/utils/gpg_keys/kenshi84.asc @@ -1,50 +1,51 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -mQINBFilp9wBEADL4GOqYB6D8gym7SB3+/IDc/kAM8EwWqUuKj0MC+JSKg0ubVes -EOgXpPVb6HlzvlAuEEtWa0/CLn+PFAkn3mrTrIlEkJfnrY+KBZpSC45iluquE/a0 -icxxmZ36QMSWCovw6xWbJ0KgofDi3jOt0Ag9QAT9IA69F9DztsXHoEjYHAshk4Uh -w0SHZIwDAvD+T4mJkBquYVFnNudWVeOsyL9P191P1TftQjWliUwNIEnQKokONg/Z -Iu5d5zOArSNqt3f7QxtLvBHNA5OmvpvA0OytUz4DsKNlU1uZHIfzyUyuLlaBmPXT -6tjJxW4XJOpdjJKKF1q2G0/gmFRYt3TWMgQL4C/5gVy1VmPPR+21WhAj2h1BHAlm -LiEd3yfefjZiDv57LCzviw9/7wuUIjcaja7MDFIZ2xoVO2PQkWEEo9SWlzE4KJw1 -p2NrWJuE1PnUfdalYjOJZn5eoyGaqcCjqEJEY2CBYSLziF6BpKw3Q4O6mIK/gCes -FcY9fkDG3mvHTMKNpR1eNk5PLDzPiUUfDiVHtKS0EVlRDe4N3UQJG3OM7wXmNrLp -6sIDVCJTn+sKBZc4zQHbqbEHzKddtUpgwXI3kbSbQWXdfIA8uBCZIA9dSEcBL8wh -xz+HCLTGJKHOHp3X6Mv9xX8HLkPtkU05Xl4jRxvo/fqSfFDO/mTvNGi8QQARAQAB -tCFrZW5zaGk4NCA8a2Vuc2hpODRAcHJvdG9ubWFpbC5jaD6JAjgEEwECACIFAlil -p9wCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEC7fR/fhomkz2gkP/2h5 -r//IY+lFux4vuHhNOGr62yEMmEPPsCca5UApPTV4U9d64eoiBKJBYhwhuf5DEKB4 -PSh2NrW1IJpZos6l4lF1UvwX9NQ/GnEiCATq+69z/EwvJbpbTPXY94KV3qBoZCOW -cbliRJYqGfWlH6ZWb2zCcjRxUr+FStSklrGE17DBiKCexgDBewjLumRD6fiJZP8u -1WLAJg+b7yplMRE3G8yKJXTmkc8EL+r2jC0tIsEZmNWu/H3NgEhawjM9OSRAsc32 -bVbdkJqQkjdUuKkkZVENxtq56yuZuOk07IHdATtiW4pVgobZ8ZzHt0JSZn9ZxGbc -stZVeK0E9WbVxFI1XqHPqVhWqmR1ZB2IcH1C6CbV5yoKdN6wkHEcKIo1eEd0p2h8 -u54K+yWamFTZuF4bS5pzWVtb5l7KqCQLsYig2hZX7jL4PLZa9QzClzkFy19jJQsD -o0L3oJfu142f6Nbg6Ip5dY6WuNrOmcY3LWhZ9g/N6oTCRdR1UCiOybEFhmsdnHm6 -hLRnYXVXgVKVV0QWtsCXk3f4hSqHnGi9COLnUk4faa1hR87ynlNVOok0DsvTXVqH -myotCLByXewlNFYO58pX4hTkxHLqdSNijZ/HQ+uUWjeXRuYFUL9oplfbQ7LNPU0r -6iIfbHhWPgDHirchKSg/MzhubDWMvpq42Y4ycoM2uQINBFilp9wBEADx1GPBnYMW -6h1sP8xHwOsbLEz8OuLzD6C64FFMkbLnloyxxxKvk4uPPzHvTdNkm9WTFSf2ffQp -5xTxbMWAbynzdQvWTHzt1j2QevDScQeqL9082Vstp3xwx05Z8+UdRj77/6gmkcUQ -4x/hbeKQYMdyFUfWl/AGLUqY7WcAlMLnJE9WJDKcrcQ1fRu5K0kylPYdtm5oL58r -C6tEBnUWFKeyGEVJ7SvcVSxkteHlUMiuHMSZUm38L7Z7dUUOMfXeSaZ/ZVdwCTSx -g8i8aN67m4WoYP6OUPWZlEcCdeYmFHcGge2LD5YWjNi1Tkgik2+KiXG6906FvbQz -msgxVUkZKi9ot/lm18uZt58iZWVrmXgikPtT1yrFRkIgHyNU2s47OHkhCGEXKvbv -rAGULvFzBkgSMZVGZXTmztGrffd3al4HTllh7Y/daQ6jeE2EMQQrmnUhrqdrdBd1 -fQn5gckqxrAQA/ec/+Ek52ImdQLrGi71wlk8zhsLVJCCW6282bPC6C98zyjNj3VF -/2GuebnOGNhNPOgGOEt1tJQk4H8DGQI3kx8Zfh5oZj5v3W3cOSLW7UuISM1PRgOQ -awViuLopDBDZQ/S11fC8sLJSpYeQZ3yCLoGFe/7f3MmvgP8SbSWdt7Yzz2T9q3+V -Nv8FTgqKQXGENfJlRQWsGuaGoG4j+2bk3wARAQABiQIfBBgBAgAJBQJYpafcAhsM -AAoJEC7fR/fhomkzwt0P/AklEyp0cU4EyHimH2kiip9c3kFmCwlIao0cxCZUzQoC -I4n8GgAwnDaJDvp0uuuBPMlbyWae/IWEBOAVXkoVPfAafHY+b9DfNf6CE6Ci9ylx -XCr207jyU8GIlz6+s6u15v8KnNUtt/IRlZxvHofSOvhMDzzIvVsnatBYgX9hB2g0 -s+MCVd/MmbXTJ2L43K5IWTzM6lUjFDt0kpdyPqyo3ZHZ2bQwKO5sd1kCzjPu1E+5 -wo9HlM84uQFSHr83MVxuRzbi9pSBiX+LlI70dUHkJuvIBaZ1aNi9zLosX+Digdy6 -q7wbzPz5rmVSLJzzlnk1TsRivaRFAzuPEMBxl7ZUeGZDllvdja9zQriMXAJ+Kyrp -nLTzvPslKQurM2vetVt0rgoe1DHtJ+7TlkGlUqtleOwvufcfPV2Y3j5UQ4HT+bhL -yUiJIs4/SxumjP3Wx+g6eqeHdLsUWjhXPnc++ipz2tN0Gy40DkLGhbS4RALRpBrK -Eh48OyH4LDAj/MWiC8eti2JpX0e7UBxKtDbuQVr596YUUE6DSrlI0b4fRDe+fIab -ooYbreMyoKXJv209yKUIEXlkvYzt8zjgD0D12RXl/5tRwsvcA8It+amfaD8Lzc5V -XEQh7cmDIPrL4WviQCN3nz0rtL2F22FqSHAUTu6uTDbTMS2C52/b3Cup/oZT5nZ2 -=ny+7 +mQINBFjtZtQBEADVjtpIAlDUqtWbr6yKikJDkPPhs6f2zByStaxB1GU/ZpuiO1WB +lI4u/k3WyjHwSiPlcSZ0jMn8wy01mWT0qWyN7ZOQ793kifMG+0encEWfPG/HSNym +AFQf0emRMr8WijtxpoBvHkv6BjbBgd57ByV7zoJWQ6giaLyvGvC3LGfuXFdcXjQP +bfi9T5o2J/K3c6FbuYyW0W/8cZq6s5jQdQk5cZMDwtA47qYgc0oUBBrLpii9B1Jz +s0zVoYORO/Wsuc5DwzswshHnHLOyxgppV1cjpJhGozxTRZE9AfCPv01Z3+TScjql +QnRwkCQIbQ0ketEmXm9QsKkkPKNNos+5ImNuarvoq355NTTltTgbTxkxNWQJqSgv +01hNxAYhvSi2QGcMZg0BTR/5SwR5m5nQgesrFuq5xStYFJ+lebj6qBQZF/Du5Gir +d0BFGiZxx4yDLnEk7MRvADs1POlPLHDgnL5K/eEOq/7KehmQQDUez4tyh5k69Hlc +BdPUHjZoIDMl3ym1LOxOrqzjlWhmiSHPvPM1Ytau96QhbSY9UeLQG/anpdrhEiiH +ckSqjSIsB1hVL802L5D0Tcrt174v3b+fZPvPbuqz3GbkoO7eMYK1WtgCQgHq9M2n +mx3jhqi3VQ4aY1gZlB+wefq8nB2X4HAdK2epDp5Ap1V56nQpkwQ/k9N7qQARAQAB +tCFrZW5zaGk4NCA8a2Vuc2hpODRAcHJvdG9ubWFpbC5jaD6JAj4EEwECACgFAljt +ZtQCGwMFCRLMAwAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEAhdCS8fQ9Ua +bk0QANP9nGWuNfvLP2HxaffDq/fzzSDOUtcEDK+v3/2tSku7AsBenCj2Ju7EAKKx +f9J5fMN40xOVhaJ72oKt8rKgG/U+M9F8IC6nmazqv67GacJsL8zAsOwS0iOKeNd0 +wv1nYRNQjlJzO3RhNo0gXLdRj/5trJrQiJIc4hIJU+KQinpq0C5QjIt1979N9j9f +sinsQoLZwY9vKmWhjWUQMv4jeb2j2SmwKsDCbh7+Edzwn/uDQGui7h6Rf3uB7hLl +MTaYAe6/q3B0zh725hSnuZwO4dLo7N7bwP/MWtYxgj0C7JXpA2o7BZRVKfX/mKeI +KP5U8d3gFJsfEGZxDle0BSN8N2Dx/QC5c+G1TD/zdnY4MUaqPs4wMKSLh5VOScb8 +HOvqnk70DDMvh12uqOZsyjorA35vywvxYeF+wF6poM5vv9/DaFYhVFRt6oO+twWc +IAU1uDEK3yrEUiRiEHkG2bxZPFDxjZqzDjWGADLeT+b+KqxW7lWtaXr//AK5KVPv +HCbCXXdi/HJ9lb8vRGMmQzf9vgNBQy3j5GFyFKcKG51yXkp73KxhMq0o5/DaC+n9 +7K33ZoyebKHKgChLup5ZaRiCfYFj3DU5CoIax0gpM+9YjPW2pTucDBlut1K2XHkJ +DGj1hy/tgJgYPe+Cs2RVTwqGwN4L2VVtfGw7F49HpMdNINpguQINBFjtZtQBEAC0 +spVhziTsh8xnBC5lbXXZvVHyPlslyTeCn+uUB79V3ER8HwTVviSV7XNRms19656e +1SpnjPZOalBnZ8mr78KgyTe6Ud7+7mBmvH/M7RwOjB3VIPNL/eLH1IfACc5KNDxd +3/jlwXRjPhD0HPkLsvTxm5hTMrqzD2ptf3cBCbKj/UVGmhPVn+E2gqis3ocOnSSN +De0ycjoY+3AWxiaUIPibgz/dBnhkqgoRuI1hnZ7qRPIVzef+7mrSc77s2AA8pBbl +mr8IebZD8BO1ZVIR34s6dtTxxonmCuZcIOoXRPB0fyqlF/tBzwYOU6kIcwBaKHaG +MOb4AdCmmEQjVF7klmWUJOQI9hISo5jKL5l4q2HD0x3WRgo7/ih443MLu7bqOUYS +G4y7sA5hzC/Y3L9hQ+RyHRzXdlKjx2T5uAheF+oSxWdJfkwUrGgWnatQ/CdD6dev +GkkIcnPp8L39/7+VXrYqhP3e5SAUrxPYB2k+6z/gMf2r+jwfKJYTYihdeeRcznzR +OlbybAmZ2JoKuzKiJ+zyHt71D+XQP37ssWR6CgO1kWDJopYTvhNxcST1DmesQCZ8 +/vV4Bd4CFlwtmlGoRRURYL9uoYUX4xplB/vy+Z4xCVTEEJcngExACuzvHdonOUI3 +QKc0CO5VfxJ+AeEGJj5rW3wUn/YBRDUaitTMsYgmOwARAQABiQIlBBgBAgAPBQJY +7WbUAhsMBQkSzAMAAAoJEAhdCS8fQ9UafU8QAMISseUfr4uR9ECAWtZlxn+jRzlr +YIQh/MV8JGRr5E8+aNjec/I33R003PvWKimaY1yzJcYw+FHq9l+NwcBTPiDDz1ya +KGPoJXCDAMYcgLpsB/yG1zcaU/Dxuw2eJdyFdlGpxSW87bJpp6jgboIQ4rLGo5Za +0i5j+wkhEQACWCKq0GQQhMWCUU2clZy/WjkkhqQ/sgB1zZgeuZoMtsuCKZ3kMrd1 +AY/5riNxnUCsfVfS3kSEarmZZzL62o3Q02v5q2rKEqaJBp/6bWBmOVtIYNbUyJq0 +76QLb4P90jo+LJPfh/XcpKzJOzkfiJfy9pC8+tNuGrzlqu/mdHh7dq4Egk76TLUg +8PvMgv5ndbKdPwxTx8crFFb5XPAsO6LyFofdmByZLHtXvYtiwtEiC8FL04ZYLsiE +9fvr548l/TLn6/+b3EnQ94YdNZFmtQVEY2QugJMmIK4LeaefZcLHtWWovWgIcA/3 +O4zqNetHyEB46Y1LnW+0IY+9LPRR3Vmhnz0RheFWLJE6jCCXmqmCqEA5jp/BR5Vl +nD/x5z0Yist5voIcVJk6z3FvuA1FclKctYM1SmAH2VZ0XfbWZEmveHr/+Z5ucwuC +WmaxcUCDA9ENP9uCJbqEREWCsTNxnvCs9mkF8mqzHwgFB6/uL2qVGieT6ne140RG +RPJhGZ14x9beu7eu +=R4WU -----END PGP PUBLIC KEY BLOCK----- |