From 99a62ef0324ce2b0fc8988cd4a6aa1dd84a5c4fd Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Sun, 14 Apr 2024 17:26:27 +0200 Subject: polyseed fix ugh --- patches/monero/0001-polyseed.patch | 304 +++-- ...et-background-sync-with-just-the-view-key.patch | 4 +- patches/monero/0003-airgap.patch | 2 +- patches/monero/0004-coin-control.patch | 2 +- patches/monero/0005-fix-build-issues.patch | 2 +- patches/monero/0006-macos-build-fix.patch | 2 +- .../monero/0007-fix-make-debug-test-target.patch | 2 +- ...issing-___clear_cache-when-targetting-iOS.patch | 10 +- ...api-compilation-target-https-git.wownero..patch | 4 +- patches/wownero/0002-polyseed-support.patch | 1300 -------------------- patches/wownero/0002-polyseed.patch | 1278 +++++++++++++++++++ ...et-background-sync-with-just-the-view-key.patch | 10 +- patches/wownero/0004-airgap.patch | 8 +- patches/wownero/0005-coin-control.patch | 8 +- patches/wownero/0006-fix-build.patch | 4 +- patches/wownero/0007-macos-build-fix.patch | 4 +- .../0008-FIX-wallet-listener-crashing.patch | 6 +- ...issing-___clear_cache-when-targetting-iOS.patch | 12 +- 18 files changed, 1461 insertions(+), 1501 deletions(-) delete mode 100644 patches/wownero/0002-polyseed-support.patch create mode 100644 patches/wownero/0002-polyseed.patch (limited to 'patches') diff --git a/patches/monero/0001-polyseed.patch b/patches/monero/0001-polyseed.patch index 855a2dc..8acb925 100644 --- a/patches/monero/0001-polyseed.patch +++ b/patches/monero/0001-polyseed.patch @@ -1,37 +1,41 @@ -From bf347a3c80f0e0647176752867a54ae9ecedb580 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 12 Mar 2024 09:42:37 +0100 +From 309e4165594749864bbf074f83893bbc1113f7f1 Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Sun, 14 Apr 2024 15:03:22 +0200 Subject: [PATCH 1/8] polyseed +Unknown date/time of origin, implemented by tobtoht for anonero, various modifications performed along the way + +Czarek Nakamoto +- 2024/04/14 - replaced the normalization code with reference implementation + +Co-authored-by: Czarek Nakamoto --- .github/workflows/build.yml | 4 +- - .gitmodules | 6 + - CMakeLists.txt | 4 +- + .gitmodules | 3 + + CMakeLists.txt | 3 +- contrib/epee/include/wipeable_string.h | 7 + contrib/epee/src/wipeable_string.cpp | 10 ++ - external/CMakeLists.txt | 2 + + external/CMakeLists.txt | 1 + external/polyseed | 1 + - external/utf8proc | 1 + src/CMakeLists.txt | 1 + src/cryptonote_basic/CMakeLists.txt | 1 + src/cryptonote_basic/account.cpp | 23 +++- src/cryptonote_basic/account.h | 6 + src/cryptonote_config.h | 2 + - src/polyseed/CMakeLists.txt | 25 ++++ - src/polyseed/pbkdf2.c | 85 ++++++++++++ + src/polyseed/CMakeLists.txt | 24 ++++ + src/polyseed/pbkdf2.c | 87 ++++++++++++ src/polyseed/pbkdf2.h | 46 +++++++ - src/polyseed/polyseed.cpp | 182 +++++++++++++++++++++++++ - src/polyseed/polyseed.hpp | 167 +++++++++++++++++++++++ + src/polyseed/polyseed.cpp | 178 +++++++++++++++++++++++++ + src/polyseed/polyseed.hpp | 162 ++++++++++++++++++++++ src/wallet/api/wallet.cpp | 71 ++++++++++ src/wallet/api/wallet.h | 10 ++ src/wallet/api/wallet2_api.h | 25 ++++ src/wallet/api/wallet_manager.cpp | 9 ++ src/wallet/api/wallet_manager.h | 10 ++ - src/wallet/wallet2.cpp | 102 ++++++++++++-- - src/wallet/wallet2.h | 30 +++- - 25 files changed, 809 insertions(+), 21 deletions(-) + src/wallet/wallet2.cpp | 102 +++++++++++--- + src/wallet/wallet2.h | 30 ++++- + 24 files changed, 795 insertions(+), 21 deletions(-) create mode 160000 external/polyseed - create mode 160000 external/utf8proc create mode 100644 src/polyseed/CMakeLists.txt create mode 100644 src/polyseed/pbkdf2.c create mode 100644 src/polyseed/pbkdf2.h @@ -54,41 +58,37 @@ index 4c1e381c0..70bea03b3 100644 test-ubuntu: needs: build-ubuntu diff --git a/.gitmodules b/.gitmodules -index 721cce3b4..73a23fb35 100644 +index 721cce3b4..2f4eb0b44 100644 --- a/.gitmodules +++ b/.gitmodules -@@ -10,6 +10,12 @@ +@@ -10,6 +10,9 @@ [submodule "external/randomx"] path = external/randomx url = https://github.com/tevador/RandomX -+[submodule "external/utf8proc"] -+ path = external/utf8proc -+ url = https://github.com/JuliaStrings/utf8proc.git +[submodule "external/polyseed"] + path = external/polyseed -+ url = https://github.com/tevador/polyseed.git ++ url = https://github.com/tevador/polyseed [submodule "external/supercop"] path = external/supercop url = https://github.com/monero-project/supercop diff --git a/CMakeLists.txt b/CMakeLists.txt -index 8fb03ba1f..63b8c5079 100644 +index 8fb03ba1f..6b50733e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -369,6 +369,8 @@ if(NOT MANUAL_SUBMODULES) +@@ -369,6 +369,7 @@ if(NOT MANUAL_SUBMODULES) check_submodule(external/trezor-common) check_submodule(external/randomx) check_submodule(external/supercop) + check_submodule(external/polyseed) -+ check_submodule(external/utf8proc) endif() endif() -@@ -458,7 +460,7 @@ endif() +@@ -458,7 +459,7 @@ endif() # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") # set(BSDI TRUE) -include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include) -+include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/polyseed/include external/utf8proc) ++include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/polyseed/include) if(APPLE) cmake_policy(SET CMP0042 NEW) @@ -137,15 +137,14 @@ index b016f2f48..f2f365b1b 100644 + } diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 5b7f69a56..1b9761d70 100644 +index 5b7f69a56..139c84073 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt -@@ -70,3 +70,5 @@ add_subdirectory(db_drivers) +@@ -70,3 +70,4 @@ add_subdirectory(db_drivers) add_subdirectory(easylogging++) add_subdirectory(qrcodegen) add_subdirectory(randomx EXCLUDE_FROM_ALL) +add_subdirectory(polyseed EXCLUDE_FROM_ALL) -+add_subdirectory(utf8proc EXCLUDE_FROM_ALL) \ No newline at end of file diff --git a/external/polyseed b/external/polyseed new file mode 160000 @@ -154,13 +153,6 @@ index 000000000..b7c35bb3c +++ b/external/polyseed @@ -0,0 +1 @@ +Subproject commit b7c35bb3c6b91e481ecb04fc235eaff69c507fa1 -diff --git a/external/utf8proc b/external/utf8proc -new file mode 160000 -index 000000000..1cb28a66c ---- /dev/null -+++ b/external/utf8proc -@@ -0,0 +1 @@ -+Subproject commit 1cb28a66ca79a0845e99433fd1056257456cef8b diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3335d3c21..06b708cf0 100644 --- a/src/CMakeLists.txt @@ -186,7 +178,7 @@ index 1414be1b2..414936a05 100644 ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 2ac455fda..4931c3740 100644 +index 2ac455fda..020e71e9d 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345) @@ -231,7 +223,7 @@ index 2ac455fda..4931c3740 100644 + + generate(secret_key, true, false); + -+ seed.save(m_keys.m_polyseed.data); ++ seed.save((uint8_t *)m_keys.m_polyseed.data); + m_keys.m_passphrase = passphrase; + } + //----------------------------------------------------------------- @@ -291,10 +283,10 @@ index 61146a114..8e1a07110 100644 // (1+32) + (1+1+16*32) + (1+16*32) = 1060 diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt new file mode 100644 -index 000000000..cca4eb746 +index 000000000..9f3339f3e --- /dev/null +++ b/src/polyseed/CMakeLists.txt -@@ -0,0 +1,25 @@ +@@ -0,0 +1,24 @@ +set(polyseed_sources + pbkdf2.c + polyseed.cpp @@ -315,43 +307,44 @@ index 000000000..cca4eb746 +target_link_libraries(polyseed_wrapper +PUBLIC + polyseed -+ utf8proc + ${SODIUM_LIBRARY} + PRIVATE + ${EXTRA_LIBRARIES} +) diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c new file mode 100644 -index 000000000..1c45f4708 +index 000000000..1f8e13955 --- /dev/null +++ b/src/polyseed/pbkdf2.c -@@ -0,0 +1,85 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// Copyright (c) 2005,2007,2009 Colin Percival -+// -+// 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. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +@@ -0,0 +1,87 @@ ++/* ++ * Copyright 2005,2007,2009 Colin Percival ++ * Copyright 2021 tevador ++ * 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. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. ++ * ++ * SOURCE: https://raw.githubusercontent.com/tevador/polyseed-examples/master/c/pbkdf2.c ++ */ + +#include + @@ -369,8 +362,8 @@ index 000000000..1c45f4708 + +void +crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, -+ const uint8_t* salt, size_t saltlen, uint64_t c, -+ uint8_t* buf, size_t dkLen) ++ const uint8_t* salt, size_t saltlen, uint64_t c, ++ uint8_t* buf, size_t dkLen) +{ + crypto_auth_hmacsha256_state Phctx, PShctx, hctx; + size_t i; @@ -411,38 +404,37 @@ index 000000000..1c45f4708 + sodium_memzero((void*)&Phctx, sizeof Phctx); + sodium_memzero((void*)&PShctx, sizeof PShctx); +} -\ No newline at end of file diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h new file mode 100644 -index 000000000..f6253b9d7 +index 000000000..8e712e988 --- /dev/null +++ b/src/polyseed/pbkdf2.h @@ -0,0 +1,46 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. ++/* ++ * Copyright 2021 tevador ++ * 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. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. ++ */ + +#ifndef PBKDF2_H +#define PBKDF2_H @@ -456,46 +448,45 @@ index 000000000..f6253b9d7 + +void +crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, -+ const uint8_t* salt, size_t saltlen, uint64_t c, -+ uint8_t* buf, size_t dkLen); ++ const uint8_t* salt, size_t saltlen, uint64_t c, ++ uint8_t* buf, size_t dkLen); + +#ifdef __cplusplus +} +#endif + +#endif -\ No newline at end of file diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp new file mode 100644 -index 000000000..b26f37574 +index 000000000..c1a4c59e1 --- /dev/null +++ b/src/polyseed/polyseed.cpp -@@ -0,0 +1,182 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +@@ -0,0 +1,178 @@ ++/* ++ * Copyright 2021 tevador ++ * 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. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "polyseed.hpp" +#include "pbkdf2.h" @@ -503,41 +494,33 @@ index 000000000..b26f37574 +#include +#include +#include -+#include ++#include + ++#include +#include +#include +#include + +namespace polyseed { + -+ inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) { -+ utf8proc_int32_t buffer[POLYSEED_STR_SIZE]; -+ utf8proc_ssize_t result; -+ -+ result = utf8proc_decompose(reinterpret_cast(str), 0, buffer, POLYSEED_STR_SIZE, options); -+ if (result < 0) { -+ return POLYSEED_STR_SIZE; -+ } -+ if (result > POLYSEED_STR_SIZE - 1) { -+ return result; -+ } -+ -+ result = utf8proc_reencode(buffer, result, options); -+ -+ strcpy(norm, reinterpret_cast(buffer)); -+ sodium_memzero(buffer, POLYSEED_STR_SIZE); -+ return result; -+ } ++ static std::locale locale; + + static size_t utf8_nfc(const char* str, polyseed_str norm) { -+ // Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases -+ // to allow wallets to split on ' '. -+ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA | UTF8PROC_LUMP)); ++ auto s = boost::locale::normalize(str, boost::locale::norm_type::norm_nfc, locale); ++ size_t size = std::min(s.size(), (size_t)POLYSEED_STR_SIZE - 1); ++ s.copy(norm, size); ++ norm[size] = '\0'; ++ sodium_memzero(&s[0], s.size()); ++ return size; + } + + static size_t utf8_nfkd(const char* str, polyseed_str norm) { -+ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA)); ++ auto s = boost::locale::normalize(str, boost::locale::norm_type::norm_nfkd, locale); ++ size_t size = std::min(s.size(), (size_t)POLYSEED_STR_SIZE - 1); ++ s.copy(norm, size); ++ norm[size] = '\0'; ++ sodium_memzero(&s[0], s.size()); ++ return size; + } + + struct dependency { @@ -552,6 +535,10 @@ index 000000000..b26f37574 + throw std::runtime_error("sodium_init failed"); + } + ++ boost::locale::generator gen; ++ gen.locale_cache_enabled(true); ++ locale = gen(""); ++ + polyseed_dependency pd; + pd.randbytes = &randombytes_buf; + pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256; @@ -655,10 +642,10 @@ index 000000000..b26f37574 +} diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp new file mode 100644 -index 000000000..2c8c777a7 +index 000000000..a23101cff --- /dev/null +++ b/src/polyseed/polyseed.hpp -@@ -0,0 +1,167 @@ +@@ -0,0 +1,162 @@ +// Copyright (c) 2023, The Monero Project +// Copyright (c) 2021, tevador +// @@ -710,15 +697,11 @@ index 000000000..2c8c777a7 + const char* name_en() const { + return polyseed_get_lang_name_en(m_lang); + } -+ const char* separator() const { -+ return m_lang->separator; -+ } + bool valid() const { + return m_lang != nullptr; + } -+ -+ const polyseed_lang* m_lang; -+ private: ++ private: ++ const polyseed_lang* m_lang; + + friend class data; + }; @@ -771,7 +754,7 @@ index 000000000..2c8c777a7 + auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]); + str.resize(size); + } -+ ++ + void split(const language& lang, polyseed_phrase& words); + + void save(polyseed_storage storage) const { @@ -824,7 +807,6 @@ index 000000000..2c8c777a7 + polyseed_coin m_coin; + }; +} -+ +#endif //POLYSEED_HPP \ No newline at end of file diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp diff --git a/patches/monero/0002-wallet-background-sync-with-just-the-view-key.patch b/patches/monero/0002-wallet-background-sync-with-just-the-view-key.patch index 610e5bd..1f8dca6 100644 --- a/patches/monero/0002-wallet-background-sync-with-just-the-view-key.patch +++ b/patches/monero/0002-wallet-background-sync-with-just-the-view-key.patch @@ -1,4 +1,4 @@ -From 0cc1775c6a41a1768ebbe6155496f01289f903d4 Mon Sep 17 00:00:00 2001 +From bafe1464686ae1da1a60e09d932dfa28a31c8618 Mon Sep 17 00:00:00 2001 From: j-berman Date: Thu, 13 Oct 2022 18:33:33 -0700 Subject: [PATCH 2/8] wallet: background sync with just the view key @@ -50,7 +50,7 @@ cache. 20 files changed, 2337 insertions(+), 130 deletions(-) diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 4931c3740..2d556f285 100644 +index 020e71e9d..fbe0a79ba 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -158,6 +158,17 @@ DISABLE_VS_WARNINGS(4244 4345) diff --git a/patches/monero/0003-airgap.patch b/patches/monero/0003-airgap.patch index 2689f06..8718117 100644 --- a/patches/monero/0003-airgap.patch +++ b/patches/monero/0003-airgap.patch @@ -1,4 +1,4 @@ -From c325add45cf950a42fc4cc16f99bb7385c53a634 Mon Sep 17 00:00:00 2001 +From 452451a0589f1313ffc3fd4dddc8f44ecbe46eb5 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 10:09:50 +0100 Subject: [PATCH 3/8] airgap diff --git a/patches/monero/0004-coin-control.patch b/patches/monero/0004-coin-control.patch index 9787116..670e464 100644 --- a/patches/monero/0004-coin-control.patch +++ b/patches/monero/0004-coin-control.patch @@ -1,4 +1,4 @@ -From fb9cd4ff500a56a1f29f323fe05de4633dd560de Mon Sep 17 00:00:00 2001 +From 8beb05d93ecd6f601628937c94054eab49765083 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 11:07:57 +0100 Subject: [PATCH 4/8] coin control diff --git a/patches/monero/0005-fix-build-issues.patch b/patches/monero/0005-fix-build-issues.patch index f5e5f08..26c257d 100644 --- a/patches/monero/0005-fix-build-issues.patch +++ b/patches/monero/0005-fix-build-issues.patch @@ -1,4 +1,4 @@ -From 6bd41f6e334fe979e2a5e874e404657ff0ef6c4f Mon Sep 17 00:00:00 2001 +From 950664a68c0d1764f36e6a671e67f58dc18298ff Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 12 Mar 2024 17:59:13 +0100 Subject: [PATCH 5/8] fix build issues diff --git a/patches/monero/0006-macos-build-fix.patch b/patches/monero/0006-macos-build-fix.patch index 4f7228f..c88fc4c 100644 --- a/patches/monero/0006-macos-build-fix.patch +++ b/patches/monero/0006-macos-build-fix.patch @@ -1,4 +1,4 @@ -From c8dfb421082c895c45c6e4fd1437fee4b554b612 Mon Sep 17 00:00:00 2001 +From c25842af73164b2465c0f57e2d652b6cca88fd9f Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 28 Mar 2024 02:03:08 +0100 Subject: [PATCH 6/8] macos build fix diff --git a/patches/monero/0007-fix-make-debug-test-target.patch b/patches/monero/0007-fix-make-debug-test-target.patch index 1e3f708..0d012d3 100644 --- a/patches/monero/0007-fix-make-debug-test-target.patch +++ b/patches/monero/0007-fix-make-debug-test-target.patch @@ -1,4 +1,4 @@ -From d0d86d333ea4788cc27c4c87c5ec2ec3bc1668b7 Mon Sep 17 00:00:00 2001 +From 5331a8d81e1a7945be02e5dd579e8360d7d851d7 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 2 Apr 2024 01:13:12 +0200 Subject: [PATCH 7/8] fix `make debug-test` target diff --git a/patches/monero/0008-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/monero/0008-fix-missing-___clear_cache-when-targetting-iOS.patch index b4a14f9..59af73b 100644 --- a/patches/monero/0008-fix-missing-___clear_cache-when-targetting-iOS.patch +++ b/patches/monero/0008-fix-missing-___clear_cache-when-targetting-iOS.patch @@ -1,4 +1,4 @@ -From b154719d3fe7ae407de91f5c5a245998efd05997 Mon Sep 17 00:00:00 2001 +From f130c717060a457efe9d869dae4d3c4991cb5355 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 2 Apr 2024 16:51:56 +0200 Subject: [PATCH 8/8] fix missing ___clear_cache when targetting iOS @@ -9,7 +9,7 @@ Subject: [PATCH 8/8] fix missing ___clear_cache when targetting iOS 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules -index 73a23fb35..7ea87a009 100644 +index 2f4eb0b44..bb8b3fa56 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,7 +9,7 @@ @@ -18,9 +18,9 @@ index 73a23fb35..7ea87a009 100644 path = external/randomx - url = https://github.com/tevador/RandomX + url = https://github.com/MrCyjaneK/RandomX - [submodule "external/utf8proc"] - path = external/utf8proc - url = https://github.com/JuliaStrings/utf8proc.git + [submodule "external/polyseed"] + path = external/polyseed + url = https://github.com/tevador/polyseed diff --git a/external/randomx b/external/randomx index 102f8acf9..ce72c9bb9 160000 --- a/external/randomx diff --git a/patches/wownero/0001-Fix-wallet_api-compilation-target-https-git.wownero..patch b/patches/wownero/0001-Fix-wallet_api-compilation-target-https-git.wownero..patch index 6a6c697..bffbdd3 100644 --- a/patches/wownero/0001-Fix-wallet_api-compilation-target-https-git.wownero..patch +++ b/patches/wownero/0001-Fix-wallet_api-compilation-target-https-git.wownero..patch @@ -1,4 +1,4 @@ -From f0f386f43d6fc998c93ecb12666688d85a4d6fca Mon Sep 17 00:00:00 2001 +From 576464e0e9fdf9e9c7973397107b315d4e95b000 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 26 Mar 2024 06:55:38 +0100 Subject: [PATCH 1/9] Fix wallet_api compilation target @@ -27,5 +27,5 @@ index 823f122c2..d81ddec93 100644 pendingTxPostProcess(transaction); -- -2.44.0 +2.39.2 diff --git a/patches/wownero/0002-polyseed-support.patch b/patches/wownero/0002-polyseed-support.patch deleted file mode 100644 index 8c5c12e..0000000 --- a/patches/wownero/0002-polyseed-support.patch +++ /dev/null @@ -1,1300 +0,0 @@ -From 33d611538efef134106f427c7ee5535d28d588f6 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 26 Mar 2024 08:19:23 +0100 -Subject: [PATCH 2/9] polyseed support - ---- - .github/workflows/build.yml | 4 +- - .gitmodules | 6 + - CMakeLists.txt | 4 +- - contrib/epee/include/wipeable_string.h | 7 + - contrib/epee/src/wipeable_string.cpp | 10 ++ - external/CMakeLists.txt | 2 + - external/polyseed | 1 + - external/utf8proc | 1 + - src/CMakeLists.txt | 1 + - src/cryptonote_basic/CMakeLists.txt | 1 + - src/cryptonote_basic/account.cpp | 23 +++- - src/cryptonote_basic/account.h | 6 + - src/cryptonote_config.h | 2 + - src/polyseed/CMakeLists.txt | 25 ++++ - src/polyseed/pbkdf2.c | 85 ++++++++++++ - src/polyseed/pbkdf2.h | 46 +++++++ - src/polyseed/polyseed.cpp | 182 +++++++++++++++++++++++++ - src/polyseed/polyseed.hpp | 167 +++++++++++++++++++++++ - src/wallet/api/wallet.cpp | 71 ++++++++++ - src/wallet/api/wallet.h | 10 ++ - src/wallet/api/wallet2_api.h | 25 ++++ - src/wallet/api/wallet_manager.cpp | 9 ++ - src/wallet/api/wallet_manager.h | 10 ++ - src/wallet/wallet2.cpp | 102 ++++++++++++-- - src/wallet/wallet2.h | 30 +++- - 25 files changed, 809 insertions(+), 21 deletions(-) - create mode 160000 external/polyseed - create mode 160000 external/utf8proc - create mode 100644 src/polyseed/CMakeLists.txt - create mode 100644 src/polyseed/pbkdf2.c - create mode 100644 src/polyseed/pbkdf2.h - create mode 100644 src/polyseed/polyseed.cpp - create mode 100644 src/polyseed/polyseed.hpp - -diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml -index 4c1e381c0..70bea03b3 100644 ---- a/.github/workflows/build.yml -+++ b/.github/workflows/build.yml -@@ -124,8 +124,8 @@ jobs: - - name: build - run: | - ${{env.CCACHE_SETTINGS}} -- cmake . -- make wallet_api -j3 -+ cmake -S . -B build -+ cmake --build build wallet_api -j3 - - test-ubuntu: - needs: build-ubuntu -diff --git a/.gitmodules b/.gitmodules -index 74571d5ee..86009b6b6 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -12,6 +12,12 @@ - path = external/supercop - url = https://github.com/monero-project/supercop - branch = monero -+[submodule "external/utf8proc"] -+ path = external/utf8proc -+ url = https://github.com/JuliaStrings/utf8proc.git -+[submodule "external/polyseed"] -+ path = external/polyseed -+ url = https://github.com/tevador/polyseed.git - [submodule "external/randomwow"] - path = external/randomwow - url = https://git.wownero.com/wownero/RandomWOW -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 20829bc30..2dd427d3d 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -370,6 +370,8 @@ if(NOT MANUAL_SUBMODULES) - #check_submodule(external/trezor-common) - check_submodule(external/randomwow) - check_submodule(external/supercop) -+ check_submodule(external/polyseed) -+ check_submodule(external/utf8proc) - endif() - endif() - -@@ -459,7 +461,7 @@ endif() - # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") - # set(BSDI TRUE) - --include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include) -+include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/polyseed/include external/utf8proc) - - if(APPLE) - cmake_policy(SET CMP0042 NEW) -diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h -index 65977cd97..594e15de4 100644 ---- a/contrib/epee/include/wipeable_string.h -+++ b/contrib/epee/include/wipeable_string.h -@@ -34,6 +34,7 @@ - #include - #include "memwipe.h" - #include "fnv1.h" -+#include "serialization/keyvalue_serialization.h" - - namespace epee - { -@@ -75,6 +76,12 @@ namespace epee - bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; } - wipeable_string &operator=(wipeable_string &&other); - wipeable_string &operator=(const wipeable_string &other); -+ char& operator[](size_t idx); -+ const char& operator[](size_t idx) const; -+ -+ BEGIN_KV_SERIALIZE_MAP() -+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer) -+ END_KV_SERIALIZE_MAP() - - private: - void grow(size_t sz, size_t reserved = 0); -diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp -index b016f2f48..f2f365b1b 100644 ---- a/contrib/epee/src/wipeable_string.cpp -+++ b/contrib/epee/src/wipeable_string.cpp -@@ -261,4 +261,14 @@ wipeable_string &wipeable_string::operator=(const wipeable_string &other) - return *this; - } - -+char& wipeable_string::operator[](size_t idx) { -+ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds"); -+ return buffer[idx]; -+} -+ -+const char& wipeable_string::operator[](size_t idx) const { -+ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds"); -+ return buffer[idx]; -+} -+ - } -diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 29aed0cc6..c023abffa 100644 ---- a/external/CMakeLists.txt -+++ b/external/CMakeLists.txt -@@ -70,3 +70,5 @@ add_subdirectory(db_drivers) - add_subdirectory(easylogging++) - add_subdirectory(qrcodegen) - add_subdirectory(randomwow EXCLUDE_FROM_ALL) -+add_subdirectory(polyseed EXCLUDE_FROM_ALL) -+add_subdirectory(utf8proc EXCLUDE_FROM_ALL) -\ No newline at end of file -diff --git a/external/polyseed b/external/polyseed -new file mode 160000 -index 000000000..b7c35bb3c ---- /dev/null -+++ b/external/polyseed -@@ -0,0 +1 @@ -+Subproject commit b7c35bb3c6b91e481ecb04fc235eaff69c507fa1 -diff --git a/external/utf8proc b/external/utf8proc -new file mode 160000 -index 000000000..1fe43f5a6 ---- /dev/null -+++ b/external/utf8proc -@@ -0,0 +1 @@ -+Subproject commit 1fe43f5a6d9c628f717c5ec8aeaeae4a9adfd167 -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 9216bcaa5..c043ba150 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -95,6 +95,7 @@ add_subdirectory(net) - add_subdirectory(hardforks) - add_subdirectory(blockchain_db) - add_subdirectory(mnemonics) -+add_subdirectory(polyseed) - add_subdirectory(rpc) - if(NOT IOS) - add_subdirectory(serialization) -diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt -index 1414be1b2..414936a05 100644 ---- a/src/cryptonote_basic/CMakeLists.txt -+++ b/src/cryptonote_basic/CMakeLists.txt -@@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic - checkpoints - cryptonote_format_utils_basic - device -+ polyseed_wrapper - ${Boost_DATE_TIME_LIBRARY} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_SERIALIZATION_LIBRARY} -diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 2ac455fda..4931c3740 100644 ---- a/src/cryptonote_basic/account.cpp -+++ b/src/cryptonote_basic/account.cpp -@@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345) - void account_keys::xor_with_key_stream(const crypto::chacha_key &key) - { - // encrypt a large enough byte stream with chacha20 -- epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size())); -+ epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (3 + m_multisig_keys.size()) + m_passphrase.size()); - const char *ptr = key_stream.data(); - for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) - m_spend_secret_key.data[i] ^= *ptr++; - for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) - m_view_secret_key.data[i] ^= *ptr++; -+ for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) -+ m_polyseed.data[i] ^= *ptr++; -+ for (size_t i = 0; i < m_passphrase.size(); ++i) -+ m_passphrase.data()[i] ^= *ptr++; - for (crypto::secret_key &k: m_multisig_keys) - { - for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) -@@ -150,6 +154,8 @@ DISABLE_VS_WARNINGS(4244 4345) - { - m_keys.m_spend_secret_key = crypto::secret_key(); - m_keys.m_multisig_keys.clear(); -+ m_keys.m_polyseed = crypto::secret_key(); -+ m_keys.m_passphrase.wipe(); - } - //----------------------------------------------------------------- - crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) -@@ -244,6 +250,21 @@ DISABLE_VS_WARNINGS(4244 4345) - create_from_keys(address, fake, viewkey); - } - //----------------------------------------------------------------- -+ void account_base::create_from_polyseed(const polyseed::data& seed, const epee::wipeable_string &passphrase) -+ { -+ crypto::secret_key secret_key; -+ seed.keygen(&secret_key, sizeof(secret_key)); -+ -+ if (!passphrase.empty()) { -+ secret_key = cryptonote::decrypt_key(secret_key, passphrase); -+ } -+ -+ generate(secret_key, true, false); -+ -+ seed.save(m_keys.m_polyseed.data); -+ m_keys.m_passphrase = passphrase; -+ } -+ //----------------------------------------------------------------- - bool account_base::make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys) - { - m_keys.m_account_address.m_spend_public_key = spend_public_key; -diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h -index 2ee9545d4..0099ebfe7 100644 ---- a/src/cryptonote_basic/account.h -+++ b/src/cryptonote_basic/account.h -@@ -33,6 +33,7 @@ - #include "cryptonote_basic.h" - #include "crypto/crypto.h" - #include "serialization/keyvalue_serialization.h" -+#include "polyseed/polyseed.hpp" - - namespace cryptonote - { -@@ -45,6 +46,8 @@ namespace cryptonote - std::vector m_multisig_keys; - hw::device *m_device = &hw::get_device("default"); - crypto::chacha_iv m_encryption_iv; -+ crypto::secret_key m_polyseed; -+ epee::wipeable_string m_passphrase; // Only used with polyseed - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(m_account_address) -@@ -53,6 +56,8 @@ namespace cryptonote - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys) - const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}}; - KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv) -+ KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed) -+ KV_SERIALIZE(m_passphrase) - END_KV_SERIALIZE_MAP() - - void encrypt(const crypto::chacha_key &key); -@@ -79,6 +84,7 @@ namespace cryptonote - void create_from_device(hw::device &hwdev); - void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); - void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey); -+ void create_from_polyseed(const polyseed::data &polyseed, const epee::wipeable_string &passphrase); - bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys); - const account_keys& get_keys() const; - std::string get_public_address_str(network_type nettype) const; -diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h -index 8c0d3ce20..1c1830ad4 100644 ---- a/src/cryptonote_config.h -+++ b/src/cryptonote_config.h -@@ -219,6 +219,8 @@ - - #define DNS_BLOCKLIST_LIFETIME (86400 * 8) - -+#define POLYSEED_COIN POLYSEED_MONERO -+ - //The limit is enough for the mandatory transaction content with 16 outputs (547 bytes), - //a custom tag (1 byte) and up to 32 bytes of custom data for each recipient. - // (1+32) + (1+1+16*32) + (1+16*32) = 1060 -diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt -new file mode 100644 -index 000000000..cca4eb746 ---- /dev/null -+++ b/src/polyseed/CMakeLists.txt -@@ -0,0 +1,25 @@ -+set(polyseed_sources -+ pbkdf2.c -+ polyseed.cpp -+) -+ -+monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") -+ -+monero_private_headers(polyseed_wrapper -+ ${polyseed_private_headers} -+) -+ -+monero_add_library(polyseed_wrapper -+ ${polyseed_sources} -+ ${polyseed_headers} -+ ${polyseed_private_headers} -+) -+ -+target_link_libraries(polyseed_wrapper -+PUBLIC -+ polyseed -+ utf8proc -+ ${SODIUM_LIBRARY} -+ PRIVATE -+ ${EXTRA_LIBRARIES} -+) -diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c -new file mode 100644 -index 000000000..1c45f4708 ---- /dev/null -+++ b/src/polyseed/pbkdf2.c -@@ -0,0 +1,85 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// Copyright (c) 2005,2007,2009 Colin Percival -+// -+// 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. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 -+ -+#include -+#include -+ -+static inline void -+store32_be(uint8_t dst[4], uint32_t w) -+{ -+ dst[3] = (uint8_t) w; w >>= 8; -+ dst[2] = (uint8_t) w; w >>= 8; -+ dst[1] = (uint8_t) w; w >>= 8; -+ dst[0] = (uint8_t) w; -+} -+ -+void -+crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, -+ const uint8_t* salt, size_t saltlen, uint64_t c, -+ uint8_t* buf, size_t dkLen) -+{ -+ crypto_auth_hmacsha256_state Phctx, PShctx, hctx; -+ size_t i; -+ uint8_t ivec[4]; -+ uint8_t U[32]; -+ uint8_t T[32]; -+ uint64_t j; -+ int k; -+ size_t clen; -+ -+ crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen); -+ PShctx = Phctx; -+ crypto_auth_hmacsha256_update(&PShctx, salt, saltlen); -+ -+ for (i = 0; i * 32 < dkLen; i++) { -+ store32_be(ivec, (uint32_t)(i + 1)); -+ hctx = PShctx; -+ crypto_auth_hmacsha256_update(&hctx, ivec, 4); -+ crypto_auth_hmacsha256_final(&hctx, U); -+ -+ memcpy(T, U, 32); -+ for (j = 2; j <= c; j++) { -+ hctx = Phctx; -+ crypto_auth_hmacsha256_update(&hctx, U, 32); -+ crypto_auth_hmacsha256_final(&hctx, U); -+ -+ for (k = 0; k < 32; k++) { -+ T[k] ^= U[k]; -+ } -+ } -+ -+ clen = dkLen - i * 32; -+ if (clen > 32) { -+ clen = 32; -+ } -+ memcpy(&buf[i * 32], T, clen); -+ } -+ sodium_memzero((void*)&Phctx, sizeof Phctx); -+ sodium_memzero((void*)&PShctx, sizeof PShctx); -+} -\ No newline at end of file -diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h -new file mode 100644 -index 000000000..f6253b9d7 ---- /dev/null -+++ b/src/polyseed/pbkdf2.h -@@ -0,0 +1,46 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. -+ -+#ifndef PBKDF2_H -+#define PBKDF2_H -+ -+#include -+#include -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+void -+crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, -+ const uint8_t* salt, size_t saltlen, uint64_t c, -+ uint8_t* buf, size_t dkLen); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif -\ No newline at end of file -diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp -new file mode 100644 -index 000000000..b26f37574 ---- /dev/null -+++ b/src/polyseed/polyseed.cpp -@@ -0,0 +1,182 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "polyseed.hpp" -+#include "pbkdf2.h" -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+namespace polyseed { -+ -+ inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) { -+ utf8proc_int32_t buffer[POLYSEED_STR_SIZE]; -+ utf8proc_ssize_t result; -+ -+ result = utf8proc_decompose(reinterpret_cast(str), 0, buffer, POLYSEED_STR_SIZE, options); -+ if (result < 0) { -+ return POLYSEED_STR_SIZE; -+ } -+ if (result > POLYSEED_STR_SIZE - 1) { -+ return result; -+ } -+ -+ result = utf8proc_reencode(buffer, result, options); -+ -+ strcpy(norm, reinterpret_cast(buffer)); -+ sodium_memzero(buffer, POLYSEED_STR_SIZE); -+ return result; -+ } -+ -+ static size_t utf8_nfc(const char* str, polyseed_str norm) { -+ // Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases -+ // to allow wallets to split on ' '. -+ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA | UTF8PROC_LUMP)); -+ } -+ -+ static size_t utf8_nfkd(const char* str, polyseed_str norm) { -+ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA)); -+ } -+ -+ struct dependency { -+ dependency(); -+ std::vector languages; -+ }; -+ -+ static dependency deps; -+ -+ dependency::dependency() { -+ if (sodium_init() == -1) { -+ throw std::runtime_error("sodium_init failed"); -+ } -+ -+ polyseed_dependency pd; -+ pd.randbytes = &randombytes_buf; -+ pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256; -+ pd.memzero = &sodium_memzero; -+ pd.u8_nfc = &utf8_nfc; -+ pd.u8_nfkd = &utf8_nfkd; -+ pd.time = nullptr; -+ pd.alloc = nullptr; -+ pd.free = nullptr; -+ -+ polyseed_inject(&pd); -+ -+ for (int i = 0; i < polyseed_get_num_langs(); ++i) { -+ languages.push_back(language(polyseed_get_lang(i))); -+ } -+ } -+ -+ static language invalid_lang; -+ -+ const std::vector& get_langs() { -+ return deps.languages; -+ } -+ -+ const language& get_lang_by_name(const std::string& name) { -+ for (auto& lang : deps.languages) { -+ if (name == lang.name_en()) { -+ return lang; -+ } -+ if (name == lang.name()) { -+ return lang; -+ } -+ } -+ return invalid_lang; -+ } -+ -+ inline void data::check_init() const { -+ if (valid()) { -+ throw std::runtime_error("already initialized"); -+ } -+ } -+ -+ static std::array error_desc = { -+ "Success", -+ "Wrong number of words in the phrase", -+ "Unknown language or unsupported words", -+ "Checksum mismatch", -+ "Unsupported seed features", -+ "Invalid seed format", -+ "Memory allocation failure", -+ "Unicode normalization failed" -+ }; -+ -+ static error get_error(polyseed_status status) { -+ if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) { -+ return error(error_desc[(int)status], status); -+ } -+ return error("Unknown error", status); -+ } -+ -+ void data::create(feature_type features) { -+ check_init(); -+ auto status = polyseed_create(features, &m_data); -+ if (status != POLYSEED_OK) { -+ throw get_error(status); -+ } -+ } -+ -+ void data::split(const language& lang, polyseed_phrase& words) { -+ check_init(); -+ if (!lang.valid()) { -+ throw std::runtime_error("invalid language"); -+ } -+ } -+ -+ void data::load(polyseed_storage storage) { -+ check_init(); -+ auto status = polyseed_load(storage, &m_data); -+ if (status != POLYSEED_OK) { -+ throw get_error(status); -+ } -+ } -+ -+ void data::load(const crypto::secret_key &key) { -+ polyseed_storage d; -+ memcpy(&d, &key.data, 32); -+ auto status = polyseed_load(d, &m_data); -+ if (status != POLYSEED_OK) { -+ throw get_error(status); -+ } -+ } -+ -+ language data::decode(const char* phrase) { -+ check_init(); -+ const polyseed_lang* lang; -+ auto status = polyseed_decode(phrase, m_coin, &lang, &m_data); -+ if (status != POLYSEED_OK) { -+ throw get_error(status); -+ } -+ return language(lang); -+ } -+} -diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp -new file mode 100644 -index 000000000..2c8c777a7 ---- /dev/null -+++ b/src/polyseed/polyseed.hpp -@@ -0,0 +1,167 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. -+ -+#ifndef POLYSEED_HPP -+#define POLYSEED_HPP -+ -+#include -+#include -+#include -+#include -+#include -+#include "crypto/crypto.h" -+ -+namespace polyseed { -+ -+ class data; -+ -+ class language { -+ public: -+ language() : m_lang(nullptr) {} -+ language(const language&) = default; -+ language(const polyseed_lang* lang) : m_lang(lang) {} -+ const char* name() const { -+ return polyseed_get_lang_name(m_lang); -+ } -+ const char* name_en() const { -+ return polyseed_get_lang_name_en(m_lang); -+ } -+ const char* separator() const { -+ return m_lang->separator; -+ } -+ bool valid() const { -+ return m_lang != nullptr; -+ } -+ -+ const polyseed_lang* m_lang; -+ private: -+ -+ friend class data; -+ }; -+ -+ const std::vector& get_langs(); -+ const language& get_lang_by_name(const std::string& name); -+ -+ class error : public std::runtime_error { -+ public: -+ error(const char* msg, polyseed_status status) -+ : std::runtime_error(msg), m_status(status) -+ { -+ } -+ polyseed_status status() const { -+ return m_status; -+ } -+ private: -+ polyseed_status m_status; -+ }; -+ -+ using feature_type = unsigned int; -+ -+ inline int enable_features(feature_type features) { -+ return polyseed_enable_features(features); -+ } -+ -+ class data { -+ public: -+ data(const data&) = delete; -+ data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {} -+ ~data() { -+ polyseed_free(m_data); -+ } -+ -+ void create(feature_type features); -+ -+ void load(polyseed_storage storage); -+ -+ void load(const crypto::secret_key &key); -+ -+ language decode(const char* phrase); -+ -+ template -+ void encode(const language& lang, str_type& str) const { -+ check_valid(); -+ if (!lang.valid()) { -+ throw std::runtime_error("invalid language"); -+ } -+ str.resize(POLYSEED_STR_SIZE); -+ auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]); -+ str.resize(size); -+ } -+ -+ void split(const language& lang, polyseed_phrase& words); -+ -+ void save(polyseed_storage storage) const { -+ check_valid(); -+ polyseed_store(m_data, storage); -+ } -+ -+ void save(void *storage) const { -+ check_valid(); -+ polyseed_store(m_data, (uint8_t*)storage); -+ } -+ -+ void crypt(const char* password) { -+ check_valid(); -+ polyseed_crypt(m_data, password); -+ } -+ -+ void keygen(void* ptr, size_t key_size) const { -+ check_valid(); -+ polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr); -+ } -+ -+ bool valid() const { -+ return m_data != nullptr; -+ } -+ -+ bool encrypted() const { -+ check_valid(); -+ return polyseed_is_encrypted(m_data); -+ } -+ -+ uint64_t birthday() const { -+ check_valid(); -+ return polyseed_get_birthday(m_data); -+ } -+ -+ bool has_feature(feature_type feature) const { -+ check_valid(); -+ return polyseed_get_feature(m_data, feature) != 0; -+ } -+ private: -+ void check_valid() const { -+ if (m_data == nullptr) { -+ throw std::runtime_error("invalid object"); -+ } -+ } -+ void check_init() const; -+ -+ polyseed_data* m_data; -+ polyseed_coin m_coin; -+ }; -+} -+ -+#endif //POLYSEED_HPP -\ No newline at end of file -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index d81ddec93..db9c2b5d9 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -683,6 +683,28 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p - return true; - } - -+bool WalletImpl::createFromPolyseed(const std::string &path, const std::string &password, const std::string &seed, -+ const std::string &passphrase, bool newWallet, uint64_t restoreHeight) -+{ -+ clearStatus(); -+ m_recoveringFromSeed = !newWallet; -+ m_recoveringFromDevice = false; -+ -+ polyseed::data polyseed(POLYSEED_COIN); -+ -+ try { -+ auto lang = polyseed.decode(seed.data()); -+ m_wallet->set_seed_language(lang.name()); -+ m_wallet->generate(path, password, polyseed, passphrase, !newWallet); -+ } -+ catch (const std::exception &e) { -+ setStatusError(e.what()); -+ return false; -+ } -+ -+ return true; -+} -+ - Wallet::Device WalletImpl::getDeviceType() const - { - return static_cast(m_wallet->get_device_type()); -@@ -820,6 +842,55 @@ std::string WalletImpl::seed(const std::string& seed_offset) const - return std::string(seed.data(), seed.size()); // TODO - } - -+bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const -+{ -+ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size()); -+ epee::wipeable_string passphrase_epee(passphrase.c_str(), passphrase.size()); -+ clearStatus(); -+ -+ if (!m_wallet) { -+ return false; -+ } -+ -+ bool result = m_wallet->get_polyseed(seed_words_epee, passphrase_epee); -+ -+ seed_words.assign(seed_words_epee.data(), seed_words_epee.size()); -+ passphrase.assign(passphrase_epee.data(), passphrase_epee.size()); -+ -+ return result; -+} -+ -+std::vector> Wallet::getPolyseedLanguages() -+{ -+ std::vector> languages; -+ -+ auto langs = polyseed::get_langs(); -+ for (const auto &lang : langs) { -+ languages.emplace_back(std::pair(lang.name_en(), lang.name())); -+ } -+ -+ return languages; -+} -+ -+bool Wallet::createPolyseed(std::string &seed_words, std::string &err, const std::string &language) -+{ -+ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size()); -+ -+ try { -+ polyseed::data polyseed(POLYSEED_COIN); -+ polyseed.create(0); -+ polyseed.encode(polyseed::get_lang_by_name(language), seed_words_epee); -+ -+ seed_words.assign(seed_words_epee.data(), seed_words_epee.size()); -+ } -+ catch (const std::exception &e) { -+ err = e.what(); -+ return false; -+ } -+ -+ return true; -+} -+ - std::string WalletImpl::getSeedLanguage() const - { - return m_wallet->get_seed_language(); -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 03b5a98e9..28135c82a 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -86,9 +86,19 @@ public: - bool recoverFromDevice(const std::string &path, - const std::string &password, - const std::string &device_name); -+ -+ bool createFromPolyseed(const std::string &path, -+ const std::string &password, -+ const std::string &seed, -+ const std::string &passphrase = "", -+ bool newWallet = true, -+ uint64_t restoreHeight = 0); -+ - Device getDeviceType() const override; - bool close(bool store = true); - std::string seed(const std::string& seed_offset = "") const override; -+ bool getPolyseed(std::string &seed_words, std::string &passphrase) const override; -+ - std::string getSeedLanguage() const override; - void setSeedLanguage(const std::string &arg) override; - // void setListener(Listener *) {} -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 6df661dc2..a0ed60a39 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -800,6 +800,10 @@ struct Wallet - static void warning(const std::string &category, const std::string &str); - static void error(const std::string &category, const std::string &str); - -+ virtual bool getPolyseed(std::string &seed, std::string &passphrase) const = 0; -+ static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English"); -+ static std::vector> getPolyseedLanguages(); -+ - /** - * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) - */ -@@ -1432,6 +1436,27 @@ struct WalletManager - uint64_t kdf_rounds = 1, - WalletListener * listener = nullptr) = 0; - -+ /*! -+ * \brief creates a wallet from a polyseed mnemonic phrase -+ * \param path Name of the wallet file to be created -+ * \param password Password of wallet file -+ * \param nettype Network type -+ * \param mnemonic Polyseed mnemonic -+ * \param passphrase Optional seed offset passphrase -+ * \param newWallet Whether it is a new wallet -+ * \param restoreHeight Override the embedded restore height -+ * \param kdf_rounds Number of rounds for key derivation function -+ * @return -+ */ -+ virtual Wallet * createWalletFromPolyseed(const std::string &path, -+ const std::string &password, -+ NetworkType nettype, -+ const std::string &mnemonic, -+ const std::string &passphrase = "", -+ bool newWallet = true, -+ uint64_t restore_height = 0, -+ uint64_t kdf_rounds = 1) = 0; -+ - /*! - * \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted - * \param wallet previously opened / created wallet instance -diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index b166d8ac7..f88bd9e64 100644 ---- a/src/wallet/api/wallet_manager.cpp -+++ b/src/wallet/api/wallet_manager.cpp -@@ -172,6 +172,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, - return wallet; - } - -+Wallet *WalletManagerImpl::createWalletFromPolyseed(const std::string &path, const std::string &password, NetworkType nettype, -+ const std::string &mnemonic, const std::string &passphrase, -+ bool newWallet, uint64_t restoreHeight, uint64_t kdf_rounds) -+{ -+ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); -+ wallet->createFromPolyseed(path, password, mnemonic, passphrase, newWallet, restoreHeight); -+ return wallet; -+} -+ - bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store) - { - WalletImpl * wallet_ = dynamic_cast(wallet); -diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h -index 206aedc14..e3149300c 100644 ---- a/src/wallet/api/wallet_manager.h -+++ b/src/wallet/api/wallet_manager.h -@@ -82,6 +82,16 @@ public: - const std::string &subaddressLookahead = "", - uint64_t kdf_rounds = 1, - WalletListener * listener = nullptr) override; -+ -+ virtual Wallet * createWalletFromPolyseed(const std::string &path, -+ const std::string &password, -+ NetworkType nettype, -+ const std::string &mnemonic, -+ const std::string &passphrase, -+ bool newWallet = true, -+ uint64_t restore_height = 0, -+ uint64_t kdf_rounds = 1) override; -+ - virtual bool closeWallet(Wallet *wallet, bool store = true) override; - bool walletExists(const std::string &path) override; - bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index e586d67f7..83aba6253 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -92,6 +92,7 @@ using namespace epee; - #include "device/device_cold.hpp" - #include "device_trezor/device_trezor.hpp" - #include "net/socks_connect.h" -+#include "polyseed/include/polyseed.h" - - extern "C" - { -@@ -1272,7 +1273,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_enable_multisig(false), - m_pool_info_query_time(0), - m_has_ever_refreshed_from_node(false), -- m_allow_mismatched_daemon_version(true) -+ m_allow_mismatched_daemon_version(true), -+ m_polyseed(false) - { - set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); - } -@@ -1450,10 +1452,25 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab - key = cryptonote::encrypt_key(key, passphrase); - if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language)) - { -- std::cout << "Failed to create seed from key for language: " << seed_language << std::endl; -+ std::cout << "Failed to create seed from key for language: " << seed_language << ", falling back to English." << std::endl; -+ crypto::ElectrumWords::bytes_to_words(key, electrum_words, "English"); -+ } -+ -+ return true; -+} -+//---------------------------------------------------------------------------------------------------- -+bool wallet2::get_polyseed(epee::wipeable_string& polyseed, epee::wipeable_string& passphrase) const -+{ -+ if (!m_polyseed) { - return false; - } - -+ polyseed::data data(POLYSEED_COIN); -+ data.load(get_account().get_keys().m_polyseed); -+ data.encode(polyseed::get_lang_by_name(seed_language), polyseed); -+ -+ passphrase = get_account().get_keys().m_passphrase; -+ - return true; - } - //---------------------------------------------------------------------------------------------------- -@@ -4712,6 +4729,9 @@ boost::optional wallet2::get_keys_file_data(const epee: - value2.SetInt(m_enable_multisig ? 1 : 0); - json.AddMember("enable_multisig", value2, json.GetAllocator()); - -+ value2.SetInt(m_polyseed ? 1 : 0); -+ json.AddMember("polyseed", value2, json.GetAllocator()); -+ - // Serialize the JSON object - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); -@@ -4860,6 +4880,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_credits_target = 0; - m_enable_multisig = false; - m_allow_mismatched_daemon_version = true; -+ m_polyseed = false; - } - else if(json.IsObject()) - { -@@ -5098,6 +5119,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_credits_target = field_credits_target; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false); - m_enable_multisig = field_enable_multisig; -+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false); -+ m_polyseed = field_polyseed; - } - else - { -@@ -5370,6 +5393,48 @@ void wallet2::init_type(hw::device::device_type device_type) - m_key_device_type = device_type; - } - -+/*! -+ * \brief Generates a polyseed wallet or restores one. -+ * \param wallet_ Name of wallet file -+ * \param password Password of wallet file -+ * \param passphrase Seed offset passphrase -+ * \param recover Whether it is a restore -+ * \param seed_words If it is a restore, the polyseed -+ * \param create_address_file Whether to create an address file -+ * \return The secret key of the generated wallet -+ */ -+void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, -+ const polyseed::data &seed, const epee::wipeable_string& passphrase, bool recover, uint64_t restoreHeight, bool create_address_file) -+{ -+ clear(); -+ prepare_file_names(wallet_); -+ -+ if (!wallet_.empty()) { -+ boost::system::error_code ignored_ec; -+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file); -+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); -+ } -+ -+ m_account.create_from_polyseed(seed, passphrase); -+ -+ init_type(hw::device::device_type::SOFTWARE); -+ m_polyseed = true; -+ setup_keys(password); -+ -+ if (recover) { -+ m_refresh_from_block_height = estimate_blockchain_height(restoreHeight > 0 ? restoreHeight : seed.birthday()); -+ } else { -+ m_refresh_from_block_height = estimate_blockchain_height(); -+ } -+ -+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); -+ -+ setup_new_blockchain(); -+ -+ if (!wallet_.empty()) -+ store(); -+} -+ - /*! - * \brief Generates a wallet or restores one. Assumes the multisig setup - * has already completed for the provided multisig info. -@@ -5497,7 +5562,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip - return retval; - } - -- uint64_t wallet2::estimate_blockchain_height() -+ uint64_t wallet2::estimate_blockchain_height(uint64_t time) - { - // -1 month for fluctuations in block time and machine date/time setup. - // avg seconds per block -@@ -5521,7 +5586,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip - // the daemon is currently syncing. - // If we use the approximate height we subtract one month as - // a safety margin. -- height = get_approximate_blockchain_height(); -+ height = get_approximate_blockchain_height(time); - uint64_t target_height = get_daemon_blockchain_target_height(err); - if (err.empty()) { - if (target_height < height) -@@ -13546,9 +13611,9 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) - return target_height; - } - --uint64_t wallet2::get_approximate_blockchain_height() const -+uint64_t wallet2::get_approximate_blockchain_height(uint64_t t) const - { -- uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : (time(NULL) - 1522624244)/307; -+ uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : ((t > 0 ? t : time(NULL)) - 1522624244)/307; - LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); - return approx_blockchain_height; - } -@@ -15262,15 +15327,6 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin - //---------------------------------------------------------------------------------------------------- - uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day) - { -- uint32_t version; -- if (!check_connection(&version)) -- { -- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); -- } -- if (version < MAKE_CORE_RPC_VERSION(1, 6)) -- { -- throw std::runtime_error("this function requires RPC version 1.6 or higher"); -- } - std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 }; - date.tm_year = year - 1900; - date.tm_mon = month - 1; -@@ -15279,7 +15335,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui - { - throw std::runtime_error("month or day out of range"); - } -+ - uint64_t timestamp_target = std::mktime(&date); -+ -+ return get_blockchain_height_by_timestamp(timestamp_target); -+} -+ -+uint64_t wallet2::get_blockchain_height_by_timestamp(uint64_t timestamp_target) { -+ uint32_t version; -+ if (!check_connection(&version)) -+ { -+ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); -+ } -+ if (version < MAKE_CORE_RPC_VERSION(1, 6)) -+ { -+ throw std::runtime_error("this function requires RPC version 1.6 or higher"); -+ } -+ - std::string err; - uint64_t height_min = 0; - uint64_t height_max = get_daemon_blockchain_height(err) - 1; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index df34f9abf..db5c1feb3 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -72,6 +72,7 @@ - #include "message_store.h" - #include "wallet_light_rpc.h" - #include "wallet_rpc_helpers.h" -+#include "polyseed/polyseed.hpp" - - #undef MONERO_DEFAULT_LOG_CATEGORY - #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" -@@ -854,6 +855,20 @@ private: - void generate(const std::string& wallet_, const epee::wipeable_string& password, - const epee::wipeable_string& multisig_data, bool create_address_file = false); - -+ /*! -+ * \brief Generates a wallet from a polyseed. -+ * @param wallet_ Name of wallet file -+ * @param password Password of wallet file -+ * @param seed Polyseed data -+ * @param passphrase Optional seed offset passphrase -+ * @param recover Whether it is a restore -+ * @param restoreHeight Override the embedded restore height -+ * @param create_address_file Whether to create an address file -+ */ -+ void generate(const std::string& wallet_, const epee::wipeable_string& password, -+ const polyseed::data &seed, const epee::wipeable_string& passphrase = "", -+ bool recover = false, uint64_t restoreHeight = 0, bool create_address_file = false); -+ - /*! - * \brief Generates a wallet or restores one. - * \param wallet_ Name of wallet file -@@ -1018,6 +1033,15 @@ private: - bool is_deterministic() const; - bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; - -+ /*! -+ * \brief get_polyseed Gets the polyseed (if available) and passphrase (if set) needed to recover the wallet. -+ * @param seed Polyseed mnemonic phrase -+ * @param passphrase Seed offset passphrase that was used to restore the wallet -+ * @return Returns true if the wallet has a polyseed. -+ * Note: both the mnemonic phrase and the passphrase are needed to recover the wallet -+ */ -+ bool get_polyseed(epee::wipeable_string& seed, epee::wipeable_string &passphrase) const; -+ - /*! - * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. - */ -@@ -1469,8 +1493,8 @@ private: - /*! - * \brief Calculates the approximate blockchain height from current date/time. - */ -- uint64_t get_approximate_blockchain_height() const; -- uint64_t estimate_blockchain_height(); -+ uint64_t get_approximate_blockchain_height(uint64_t time = 0) const; -+ uint64_t estimate_blockchain_height(uint64_t time = 0); - std::vector select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); - std::vector select_available_outputs(const std::function &f); - std::vector select_available_unmixable_outputs(); -@@ -1563,6 +1587,7 @@ private: - bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error); - - uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31 -+ uint64_t get_blockchain_height_by_timestamp(uint64_t timestamp); - - bool is_synced(); - -@@ -1900,6 +1925,7 @@ private: - std::string seed_language; /*!< Language of the mnemonics (seed). */ - bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ - bool m_watch_only; /*!< no spend key */ -+ bool m_polyseed; - bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */ - uint32_t m_multisig_threshold; - std::vector m_multisig_signers; --- -2.44.0 - diff --git a/patches/wownero/0002-polyseed.patch b/patches/wownero/0002-polyseed.patch new file mode 100644 index 0000000..bee6707 --- /dev/null +++ b/patches/wownero/0002-polyseed.patch @@ -0,0 +1,1278 @@ +From 84925f0c3587e9fde73ea441a4a19d0ec2c61aae Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Sun, 14 Apr 2024 15:03:22 +0200 +Subject: [PATCH 2/9] polyseed + +Unknown date/time of origin, implemented by tobtoht for anonero, various modifications performed along the way + +Czarek Nakamoto +- 2024/04/14 - replaced the normalization code with reference implementation + +Co-authored-by: Czarek Nakamoto +--- + .github/workflows/build.yml | 4 +- + .gitmodules | 3 + + CMakeLists.txt | 3 +- + contrib/epee/include/wipeable_string.h | 7 + + contrib/epee/src/wipeable_string.cpp | 10 ++ + external/CMakeLists.txt | 1 + + external/polyseed | 1 + + src/CMakeLists.txt | 1 + + src/cryptonote_basic/CMakeLists.txt | 1 + + src/cryptonote_basic/account.cpp | 23 +++- + src/cryptonote_basic/account.h | 6 + + src/cryptonote_config.h | 2 + + src/polyseed/CMakeLists.txt | 24 ++++ + src/polyseed/pbkdf2.c | 87 ++++++++++++ + src/polyseed/pbkdf2.h | 46 +++++++ + src/polyseed/polyseed.cpp | 178 +++++++++++++++++++++++++ + src/polyseed/polyseed.hpp | 162 ++++++++++++++++++++++ + src/wallet/api/wallet.cpp | 71 ++++++++++ + src/wallet/api/wallet.h | 10 ++ + src/wallet/api/wallet2_api.h | 25 ++++ + src/wallet/api/wallet_manager.cpp | 9 ++ + src/wallet/api/wallet_manager.h | 10 ++ + src/wallet/wallet2.cpp | 100 ++++++++++++-- + src/wallet/wallet2.h | 30 ++++- + 24 files changed, 794 insertions(+), 20 deletions(-) + create mode 160000 external/polyseed + create mode 100644 src/polyseed/CMakeLists.txt + create mode 100644 src/polyseed/pbkdf2.c + create mode 100644 src/polyseed/pbkdf2.h + create mode 100644 src/polyseed/polyseed.cpp + create mode 100644 src/polyseed/polyseed.hpp + +diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml +index 4c1e381c0..70bea03b3 100644 +--- a/.github/workflows/build.yml ++++ b/.github/workflows/build.yml +@@ -124,8 +124,8 @@ jobs: + - name: build + run: | + ${{env.CCACHE_SETTINGS}} +- cmake . +- make wallet_api -j3 ++ cmake -S . -B build ++ cmake --build build wallet_api -j3 + + test-ubuntu: + needs: build-ubuntu +diff --git a/.gitmodules b/.gitmodules +index 74571d5ee..bab1cbed2 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -8,6 +8,9 @@ + active = false + path = external/trezor-common + url = https://github.com/trezor/trezor-common.git ++[submodule "external/polyseed"] ++ path = external/polyseed ++ url = https://github.com/tevador/polyseed + [submodule "external/supercop"] + path = external/supercop + url = https://github.com/monero-project/supercop +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 20829bc30..5661c5c48 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -370,6 +370,7 @@ if(NOT MANUAL_SUBMODULES) + #check_submodule(external/trezor-common) + check_submodule(external/randomwow) + check_submodule(external/supercop) ++ check_submodule(external/polyseed) + endif() + endif() + +@@ -459,7 +460,7 @@ endif() + # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") + # set(BSDI TRUE) + +-include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include) ++include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/polyseed/include) + + if(APPLE) + cmake_policy(SET CMP0042 NEW) +diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h +index 65977cd97..594e15de4 100644 +--- a/contrib/epee/include/wipeable_string.h ++++ b/contrib/epee/include/wipeable_string.h +@@ -34,6 +34,7 @@ + #include + #include "memwipe.h" + #include "fnv1.h" ++#include "serialization/keyvalue_serialization.h" + + namespace epee + { +@@ -75,6 +76,12 @@ namespace epee + bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; } + wipeable_string &operator=(wipeable_string &&other); + wipeable_string &operator=(const wipeable_string &other); ++ char& operator[](size_t idx); ++ const char& operator[](size_t idx) const; ++ ++ BEGIN_KV_SERIALIZE_MAP() ++ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer) ++ END_KV_SERIALIZE_MAP() + + private: + void grow(size_t sz, size_t reserved = 0); +diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp +index b016f2f48..f2f365b1b 100644 +--- a/contrib/epee/src/wipeable_string.cpp ++++ b/contrib/epee/src/wipeable_string.cpp +@@ -261,4 +261,14 @@ wipeable_string &wipeable_string::operator=(const wipeable_string &other) + return *this; + } + ++char& wipeable_string::operator[](size_t idx) { ++ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds"); ++ return buffer[idx]; ++} ++ ++const char& wipeable_string::operator[](size_t idx) const { ++ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds"); ++ return buffer[idx]; ++} ++ + } +diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt +index 29aed0cc6..b83c08bb0 100644 +--- a/external/CMakeLists.txt ++++ b/external/CMakeLists.txt +@@ -70,3 +70,4 @@ add_subdirectory(db_drivers) + add_subdirectory(easylogging++) + add_subdirectory(qrcodegen) + add_subdirectory(randomwow EXCLUDE_FROM_ALL) ++add_subdirectory(polyseed EXCLUDE_FROM_ALL) +diff --git a/external/polyseed b/external/polyseed +new file mode 160000 +index 000000000..b7c35bb3c +--- /dev/null ++++ b/external/polyseed +@@ -0,0 +1 @@ ++Subproject commit b7c35bb3c6b91e481ecb04fc235eaff69c507fa1 +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 9216bcaa5..c043ba150 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -95,6 +95,7 @@ add_subdirectory(net) + add_subdirectory(hardforks) + add_subdirectory(blockchain_db) + add_subdirectory(mnemonics) ++add_subdirectory(polyseed) + add_subdirectory(rpc) + if(NOT IOS) + add_subdirectory(serialization) +diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt +index 1414be1b2..414936a05 100644 +--- a/src/cryptonote_basic/CMakeLists.txt ++++ b/src/cryptonote_basic/CMakeLists.txt +@@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic + checkpoints + cryptonote_format_utils_basic + device ++ polyseed_wrapper + ${Boost_DATE_TIME_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SERIALIZATION_LIBRARY} +diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp +index 2ac455fda..020e71e9d 100644 +--- a/src/cryptonote_basic/account.cpp ++++ b/src/cryptonote_basic/account.cpp +@@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345) + void account_keys::xor_with_key_stream(const crypto::chacha_key &key) + { + // encrypt a large enough byte stream with chacha20 +- epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size())); ++ epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (3 + m_multisig_keys.size()) + m_passphrase.size()); + const char *ptr = key_stream.data(); + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) + m_spend_secret_key.data[i] ^= *ptr++; + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) + m_view_secret_key.data[i] ^= *ptr++; ++ for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) ++ m_polyseed.data[i] ^= *ptr++; ++ for (size_t i = 0; i < m_passphrase.size(); ++i) ++ m_passphrase.data()[i] ^= *ptr++; + for (crypto::secret_key &k: m_multisig_keys) + { + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) +@@ -150,6 +154,8 @@ DISABLE_VS_WARNINGS(4244 4345) + { + m_keys.m_spend_secret_key = crypto::secret_key(); + m_keys.m_multisig_keys.clear(); ++ m_keys.m_polyseed = crypto::secret_key(); ++ m_keys.m_passphrase.wipe(); + } + //----------------------------------------------------------------- + crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) +@@ -244,6 +250,21 @@ DISABLE_VS_WARNINGS(4244 4345) + create_from_keys(address, fake, viewkey); + } + //----------------------------------------------------------------- ++ void account_base::create_from_polyseed(const polyseed::data& seed, const epee::wipeable_string &passphrase) ++ { ++ crypto::secret_key secret_key; ++ seed.keygen(&secret_key, sizeof(secret_key)); ++ ++ if (!passphrase.empty()) { ++ secret_key = cryptonote::decrypt_key(secret_key, passphrase); ++ } ++ ++ generate(secret_key, true, false); ++ ++ seed.save((uint8_t *)m_keys.m_polyseed.data); ++ m_keys.m_passphrase = passphrase; ++ } ++ //----------------------------------------------------------------- + bool account_base::make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys) + { + m_keys.m_account_address.m_spend_public_key = spend_public_key; +diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h +index 2ee9545d4..0099ebfe7 100644 +--- a/src/cryptonote_basic/account.h ++++ b/src/cryptonote_basic/account.h +@@ -33,6 +33,7 @@ + #include "cryptonote_basic.h" + #include "crypto/crypto.h" + #include "serialization/keyvalue_serialization.h" ++#include "polyseed/polyseed.hpp" + + namespace cryptonote + { +@@ -45,6 +46,8 @@ namespace cryptonote + std::vector m_multisig_keys; + hw::device *m_device = &hw::get_device("default"); + crypto::chacha_iv m_encryption_iv; ++ crypto::secret_key m_polyseed; ++ epee::wipeable_string m_passphrase; // Only used with polyseed + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_account_address) +@@ -53,6 +56,8 @@ namespace cryptonote + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys) + const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}}; + KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv) ++ KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed) ++ KV_SERIALIZE(m_passphrase) + END_KV_SERIALIZE_MAP() + + void encrypt(const crypto::chacha_key &key); +@@ -79,6 +84,7 @@ namespace cryptonote + void create_from_device(hw::device &hwdev); + void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); + void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey); ++ void create_from_polyseed(const polyseed::data &polyseed, const epee::wipeable_string &passphrase); + bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys); + const account_keys& get_keys() const; + std::string get_public_address_str(network_type nettype) const; +diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h +index 8c0d3ce20..1c1830ad4 100644 +--- a/src/cryptonote_config.h ++++ b/src/cryptonote_config.h +@@ -219,6 +219,8 @@ + + #define DNS_BLOCKLIST_LIFETIME (86400 * 8) + ++#define POLYSEED_COIN POLYSEED_MONERO ++ + //The limit is enough for the mandatory transaction content with 16 outputs (547 bytes), + //a custom tag (1 byte) and up to 32 bytes of custom data for each recipient. + // (1+32) + (1+1+16*32) + (1+16*32) = 1060 +diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt +new file mode 100644 +index 000000000..9f3339f3e +--- /dev/null ++++ b/src/polyseed/CMakeLists.txt +@@ -0,0 +1,24 @@ ++set(polyseed_sources ++ pbkdf2.c ++ polyseed.cpp ++) ++ ++monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") ++ ++monero_private_headers(polyseed_wrapper ++ ${polyseed_private_headers} ++) ++ ++monero_add_library(polyseed_wrapper ++ ${polyseed_sources} ++ ${polyseed_headers} ++ ${polyseed_private_headers} ++) ++ ++target_link_libraries(polyseed_wrapper ++PUBLIC ++ polyseed ++ ${SODIUM_LIBRARY} ++ PRIVATE ++ ${EXTRA_LIBRARIES} ++) +diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c +new file mode 100644 +index 000000000..1f8e13955 +--- /dev/null ++++ b/src/polyseed/pbkdf2.c +@@ -0,0 +1,87 @@ ++/* ++ * Copyright 2005,2007,2009 Colin Percival ++ * Copyright 2021 tevador ++ * 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. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. ++ * ++ * SOURCE: https://raw.githubusercontent.com/tevador/polyseed-examples/master/c/pbkdf2.c ++ */ ++ ++#include ++ ++#include ++#include ++ ++static inline void ++store32_be(uint8_t dst[4], uint32_t w) ++{ ++ dst[3] = (uint8_t) w; w >>= 8; ++ dst[2] = (uint8_t) w; w >>= 8; ++ dst[1] = (uint8_t) w; w >>= 8; ++ dst[0] = (uint8_t) w; ++} ++ ++void ++crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, ++ const uint8_t* salt, size_t saltlen, uint64_t c, ++ uint8_t* buf, size_t dkLen) ++{ ++ crypto_auth_hmacsha256_state Phctx, PShctx, hctx; ++ size_t i; ++ uint8_t ivec[4]; ++ uint8_t U[32]; ++ uint8_t T[32]; ++ uint64_t j; ++ int k; ++ size_t clen; ++ ++ crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen); ++ PShctx = Phctx; ++ crypto_auth_hmacsha256_update(&PShctx, salt, saltlen); ++ ++ for (i = 0; i * 32 < dkLen; i++) { ++ store32_be(ivec, (uint32_t)(i + 1)); ++ hctx = PShctx; ++ crypto_auth_hmacsha256_update(&hctx, ivec, 4); ++ crypto_auth_hmacsha256_final(&hctx, U); ++ ++ memcpy(T, U, 32); ++ for (j = 2; j <= c; j++) { ++ hctx = Phctx; ++ crypto_auth_hmacsha256_update(&hctx, U, 32); ++ crypto_auth_hmacsha256_final(&hctx, U); ++ ++ for (k = 0; k < 32; k++) { ++ T[k] ^= U[k]; ++ } ++ } ++ ++ clen = dkLen - i * 32; ++ if (clen > 32) { ++ clen = 32; ++ } ++ memcpy(&buf[i * 32], T, clen); ++ } ++ sodium_memzero((void*)&Phctx, sizeof Phctx); ++ sodium_memzero((void*)&PShctx, sizeof PShctx); ++} +diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h +new file mode 100644 +index 000000000..8e712e988 +--- /dev/null ++++ b/src/polyseed/pbkdf2.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright 2021 tevador ++ * 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. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. ++ */ ++ ++#ifndef PBKDF2_H ++#define PBKDF2_H ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++void ++crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, ++ const uint8_t* salt, size_t saltlen, uint64_t c, ++ uint8_t* buf, size_t dkLen); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp +new file mode 100644 +index 000000000..c1a4c59e1 +--- /dev/null ++++ b/src/polyseed/polyseed.cpp +@@ -0,0 +1,178 @@ ++/* ++ * Copyright 2021 tevador ++ * 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. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "polyseed.hpp" ++#include "pbkdf2.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++namespace polyseed { ++ ++ static std::locale locale; ++ ++ static size_t utf8_nfc(const char* str, polyseed_str norm) { ++ auto s = boost::locale::normalize(str, boost::locale::norm_type::norm_nfc, locale); ++ size_t size = std::min(s.size(), (size_t)POLYSEED_STR_SIZE - 1); ++ s.copy(norm, size); ++ norm[size] = '\0'; ++ sodium_memzero(&s[0], s.size()); ++ return size; ++ } ++ ++ static size_t utf8_nfkd(const char* str, polyseed_str norm) { ++ auto s = boost::locale::normalize(str, boost::locale::norm_type::norm_nfkd, locale); ++ size_t size = std::min(s.size(), (size_t)POLYSEED_STR_SIZE - 1); ++ s.copy(norm, size); ++ norm[size] = '\0'; ++ sodium_memzero(&s[0], s.size()); ++ return size; ++ } ++ ++ struct dependency { ++ dependency(); ++ std::vector languages; ++ }; ++ ++ static dependency deps; ++ ++ dependency::dependency() { ++ if (sodium_init() == -1) { ++ throw std::runtime_error("sodium_init failed"); ++ } ++ ++ boost::locale::generator gen; ++ gen.locale_cache_enabled(true); ++ locale = gen(""); ++ ++ polyseed_dependency pd; ++ pd.randbytes = &randombytes_buf; ++ pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256; ++ pd.memzero = &sodium_memzero; ++ pd.u8_nfc = &utf8_nfc; ++ pd.u8_nfkd = &utf8_nfkd; ++ pd.time = nullptr; ++ pd.alloc = nullptr; ++ pd.free = nullptr; ++ ++ polyseed_inject(&pd); ++ ++ for (int i = 0; i < polyseed_get_num_langs(); ++i) { ++ languages.push_back(language(polyseed_get_lang(i))); ++ } ++ } ++ ++ static language invalid_lang; ++ ++ const std::vector& get_langs() { ++ return deps.languages; ++ } ++ ++ const language& get_lang_by_name(const std::string& name) { ++ for (auto& lang : deps.languages) { ++ if (name == lang.name_en()) { ++ return lang; ++ } ++ if (name == lang.name()) { ++ return lang; ++ } ++ } ++ return invalid_lang; ++ } ++ ++ inline void data::check_init() const { ++ if (valid()) { ++ throw std::runtime_error("already initialized"); ++ } ++ } ++ ++ static std::array error_desc = { ++ "Success", ++ "Wrong number of words in the phrase", ++ "Unknown language or unsupported words", ++ "Checksum mismatch", ++ "Unsupported seed features", ++ "Invalid seed format", ++ "Memory allocation failure", ++ "Unicode normalization failed" ++ }; ++ ++ static error get_error(polyseed_status status) { ++ if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) { ++ return error(error_desc[(int)status], status); ++ } ++ return error("Unknown error", status); ++ } ++ ++ void data::create(feature_type features) { ++ check_init(); ++ auto status = polyseed_create(features, &m_data); ++ if (status != POLYSEED_OK) { ++ throw get_error(status); ++ } ++ } ++ ++ void data::split(const language& lang, polyseed_phrase& words) { ++ check_init(); ++ if (!lang.valid()) { ++ throw std::runtime_error("invalid language"); ++ } ++ } ++ ++ void data::load(polyseed_storage storage) { ++ check_init(); ++ auto status = polyseed_load(storage, &m_data); ++ if (status != POLYSEED_OK) { ++ throw get_error(status); ++ } ++ } ++ ++ void data::load(const crypto::secret_key &key) { ++ polyseed_storage d; ++ memcpy(&d, &key.data, 32); ++ auto status = polyseed_load(d, &m_data); ++ if (status != POLYSEED_OK) { ++ throw get_error(status); ++ } ++ } ++ ++ language data::decode(const char* phrase) { ++ check_init(); ++ const polyseed_lang* lang; ++ auto status = polyseed_decode(phrase, m_coin, &lang, &m_data); ++ if (status != POLYSEED_OK) { ++ throw get_error(status); ++ } ++ return language(lang); ++ } ++} +diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp +new file mode 100644 +index 000000000..a23101cff +--- /dev/null ++++ b/src/polyseed/polyseed.hpp +@@ -0,0 +1,162 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// 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. ++// ++// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. ++ ++#ifndef POLYSEED_HPP ++#define POLYSEED_HPP ++ ++#include ++#include ++#include ++#include ++#include ++#include "crypto/crypto.h" ++ ++namespace polyseed { ++ ++ class data; ++ ++ class language { ++ public: ++ language() : m_lang(nullptr) {} ++ language(const language&) = default; ++ language(const polyseed_lang* lang) : m_lang(lang) {} ++ const char* name() const { ++ return polyseed_get_lang_name(m_lang); ++ } ++ const char* name_en() const { ++ return polyseed_get_lang_name_en(m_lang); ++ } ++ bool valid() const { ++ return m_lang != nullptr; ++ } ++ private: ++ const polyseed_lang* m_lang; ++ ++ friend class data; ++ }; ++ ++ const std::vector& get_langs(); ++ const language& get_lang_by_name(const std::string& name); ++ ++ class error : public std::runtime_error { ++ public: ++ error(const char* msg, polyseed_status status) ++ : std::runtime_error(msg), m_status(status) ++ { ++ } ++ polyseed_status status() const { ++ return m_status; ++ } ++ private: ++ polyseed_status m_status; ++ }; ++ ++ using feature_type = unsigned int; ++ ++ inline int enable_features(feature_type features) { ++ return polyseed_enable_features(features); ++ } ++ ++ class data { ++ public: ++ data(const data&) = delete; ++ data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {} ++ ~data() { ++ polyseed_free(m_data); ++ } ++ ++ void create(feature_type features); ++ ++ void load(polyseed_storage storage); ++ ++ void load(const crypto::secret_key &key); ++ ++ language decode(const char* phrase); ++ ++ template ++ void encode(const language& lang, str_type& str) const { ++ check_valid(); ++ if (!lang.valid()) { ++ throw std::runtime_error("invalid language"); ++ } ++ str.resize(POLYSEED_STR_SIZE); ++ auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]); ++ str.resize(size); ++ } ++ ++ void split(const language& lang, polyseed_phrase& words); ++ ++ void save(polyseed_storage storage) const { ++ check_valid(); ++ polyseed_store(m_data, storage); ++ } ++ ++ void save(void *storage) const { ++ check_valid(); ++ polyseed_store(m_data, (uint8_t*)storage); ++ } ++ ++ void crypt(const char* password) { ++ check_valid(); ++ polyseed_crypt(m_data, password); ++ } ++ ++ void keygen(void* ptr, size_t key_size) const { ++ check_valid(); ++ polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr); ++ } ++ ++ bool valid() const { ++ return m_data != nullptr; ++ } ++ ++ bool encrypted() const { ++ check_valid(); ++ return polyseed_is_encrypted(m_data); ++ } ++ ++ uint64_t birthday() const { ++ check_valid(); ++ return polyseed_get_birthday(m_data); ++ } ++ ++ bool has_feature(feature_type feature) const { ++ check_valid(); ++ return polyseed_get_feature(m_data, feature) != 0; ++ } ++ private: ++ void check_valid() const { ++ if (m_data == nullptr) { ++ throw std::runtime_error("invalid object"); ++ } ++ } ++ void check_init() const; ++ ++ polyseed_data* m_data; ++ polyseed_coin m_coin; ++ }; ++} ++#endif //POLYSEED_HPP +\ No newline at end of file +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index d81ddec93..ceb0bcb8b 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -683,6 +683,28 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p + return true; + } + ++bool WalletImpl::createFromPolyseed(const std::string &path, const std::string &password, const std::string &seed, ++ const std::string &passphrase, bool newWallet, uint64_t restoreHeight) ++{ ++ clearStatus(); ++ m_recoveringFromSeed = !newWallet; ++ m_recoveringFromDevice = false; ++ ++ polyseed::data polyseed(POLYSEED_COIN); ++ ++ try { ++ auto lang = polyseed.decode(seed.data()); ++ m_wallet->set_seed_language(lang.name()); ++ m_wallet->generate(path, password, polyseed, passphrase, !newWallet); ++ } ++ catch (const std::exception &e) { ++ setStatusError(e.what()); ++ return false; ++ } ++ ++ return true; ++} ++ + Wallet::Device WalletImpl::getDeviceType() const + { + return static_cast(m_wallet->get_device_type()); +@@ -820,6 +842,55 @@ std::string WalletImpl::seed(const std::string& seed_offset) const + return std::string(seed.data(), seed.size()); // TODO + } + ++bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const ++{ ++ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size()); ++ epee::wipeable_string passphrase_epee(passphrase.c_str(), passphrase.size()); ++ clearStatus(); ++ ++ if (!m_wallet) { ++ return false; ++ } ++ ++ bool result = m_wallet->get_polyseed(seed_words_epee, passphrase_epee); ++ ++ seed_words.assign(seed_words_epee.data(), seed_words_epee.size()); ++ passphrase.assign(passphrase_epee.data(), passphrase_epee.size()); ++ ++ return result; ++} ++ ++std::vector> Wallet::getPolyseedLanguages() ++{ ++ std::vector> languages; ++ ++ auto langs = polyseed::get_langs(); ++ for (const auto &lang : langs) { ++ languages.emplace_back(std::pair(lang.name_en(), lang.name())); ++ } ++ ++ return languages; ++} ++ ++bool Wallet::createPolyseed(std::string &seed_words, std::string &err, const std::string &language) ++{ ++ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size()); ++ ++ try { ++ polyseed::data polyseed(POLYSEED_COIN); ++ polyseed.create(0); ++ polyseed.encode(polyseed::get_lang_by_name(language), seed_words_epee); ++ ++ seed_words.assign(seed_words_epee.data(), seed_words_epee.size()); ++ } ++ catch (const std::exception &e) { ++ err = e.what(); ++ return false; ++ } ++ ++ return true; ++} ++ + std::string WalletImpl::getSeedLanguage() const + { + return m_wallet->get_seed_language(); +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 03b5a98e9..28135c82a 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -86,9 +86,19 @@ public: + bool recoverFromDevice(const std::string &path, + const std::string &password, + const std::string &device_name); ++ ++ bool createFromPolyseed(const std::string &path, ++ const std::string &password, ++ const std::string &seed, ++ const std::string &passphrase = "", ++ bool newWallet = true, ++ uint64_t restoreHeight = 0); ++ + Device getDeviceType() const override; + bool close(bool store = true); + std::string seed(const std::string& seed_offset = "") const override; ++ bool getPolyseed(std::string &seed_words, std::string &passphrase) const override; ++ + std::string getSeedLanguage() const override; + void setSeedLanguage(const std::string &arg) override; + // void setListener(Listener *) {} +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 6df661dc2..a0ed60a39 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -800,6 +800,10 @@ struct Wallet + static void warning(const std::string &category, const std::string &str); + static void error(const std::string &category, const std::string &str); + ++ virtual bool getPolyseed(std::string &seed, std::string &passphrase) const = 0; ++ static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English"); ++ static std::vector> getPolyseedLanguages(); ++ + /** + * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) + */ +@@ -1432,6 +1436,27 @@ struct WalletManager + uint64_t kdf_rounds = 1, + WalletListener * listener = nullptr) = 0; + ++ /*! ++ * \brief creates a wallet from a polyseed mnemonic phrase ++ * \param path Name of the wallet file to be created ++ * \param password Password of wallet file ++ * \param nettype Network type ++ * \param mnemonic Polyseed mnemonic ++ * \param passphrase Optional seed offset passphrase ++ * \param newWallet Whether it is a new wallet ++ * \param restoreHeight Override the embedded restore height ++ * \param kdf_rounds Number of rounds for key derivation function ++ * @return ++ */ ++ virtual Wallet * createWalletFromPolyseed(const std::string &path, ++ const std::string &password, ++ NetworkType nettype, ++ const std::string &mnemonic, ++ const std::string &passphrase = "", ++ bool newWallet = true, ++ uint64_t restore_height = 0, ++ uint64_t kdf_rounds = 1) = 0; ++ + /*! + * \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted + * \param wallet previously opened / created wallet instance +diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp +index b166d8ac7..f88bd9e64 100644 +--- a/src/wallet/api/wallet_manager.cpp ++++ b/src/wallet/api/wallet_manager.cpp +@@ -172,6 +172,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, + return wallet; + } + ++Wallet *WalletManagerImpl::createWalletFromPolyseed(const std::string &path, const std::string &password, NetworkType nettype, ++ const std::string &mnemonic, const std::string &passphrase, ++ bool newWallet, uint64_t restoreHeight, uint64_t kdf_rounds) ++{ ++ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); ++ wallet->createFromPolyseed(path, password, mnemonic, passphrase, newWallet, restoreHeight); ++ return wallet; ++} ++ + bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store) + { + WalletImpl * wallet_ = dynamic_cast(wallet); +diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h +index 206aedc14..e3149300c 100644 +--- a/src/wallet/api/wallet_manager.h ++++ b/src/wallet/api/wallet_manager.h +@@ -82,6 +82,16 @@ public: + const std::string &subaddressLookahead = "", + uint64_t kdf_rounds = 1, + WalletListener * listener = nullptr) override; ++ ++ virtual Wallet * createWalletFromPolyseed(const std::string &path, ++ const std::string &password, ++ NetworkType nettype, ++ const std::string &mnemonic, ++ const std::string &passphrase, ++ bool newWallet = true, ++ uint64_t restore_height = 0, ++ uint64_t kdf_rounds = 1) override; ++ + virtual bool closeWallet(Wallet *wallet, bool store = true) override; + bool walletExists(const std::string &path) override; + bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index e586d67f7..8b6682da9 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -92,6 +92,7 @@ using namespace epee; + #include "device/device_cold.hpp" + #include "device_trezor/device_trezor.hpp" + #include "net/socks_connect.h" ++#include "polyseed/include/polyseed.h" + + extern "C" + { +@@ -1272,7 +1273,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std + m_enable_multisig(false), + m_pool_info_query_time(0), + m_has_ever_refreshed_from_node(false), +- m_allow_mismatched_daemon_version(true) ++ m_allow_mismatched_daemon_version(true), ++ m_polyseed(false) + { + set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); + } +@@ -1450,10 +1452,25 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + key = cryptonote::encrypt_key(key, passphrase); + if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language)) + { +- std::cout << "Failed to create seed from key for language: " << seed_language << std::endl; ++ std::cout << "Failed to create seed from key for language: " << seed_language << ", falling back to English." << std::endl; ++ crypto::ElectrumWords::bytes_to_words(key, electrum_words, "English"); ++ } ++ ++ return true; ++} ++//---------------------------------------------------------------------------------------------------- ++bool wallet2::get_polyseed(epee::wipeable_string& polyseed, epee::wipeable_string& passphrase) const ++{ ++ if (!m_polyseed) { + return false; + } + ++ polyseed::data data(POLYSEED_COIN); ++ data.load(get_account().get_keys().m_polyseed); ++ data.encode(polyseed::get_lang_by_name(seed_language), polyseed); ++ ++ passphrase = get_account().get_keys().m_passphrase; ++ + return true; + } + //---------------------------------------------------------------------------------------------------- +@@ -4712,6 +4729,9 @@ boost::optional wallet2::get_keys_file_data(const epee: + value2.SetInt(m_enable_multisig ? 1 : 0); + json.AddMember("enable_multisig", value2, json.GetAllocator()); + ++ value2.SetInt(m_polyseed ? 1 : 0); ++ json.AddMember("polyseed", value2, json.GetAllocator()); ++ + // Serialize the JSON object + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); +@@ -4860,6 +4880,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_credits_target = 0; + m_enable_multisig = false; + m_allow_mismatched_daemon_version = true; ++ m_polyseed = false; + } + else if(json.IsObject()) + { +@@ -5098,6 +5119,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_credits_target = field_credits_target; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false); + m_enable_multisig = field_enable_multisig; ++ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false); ++ m_polyseed = field_polyseed; + } + else + { +@@ -5370,6 +5393,48 @@ void wallet2::init_type(hw::device::device_type device_type) + m_key_device_type = device_type; + } + ++/*! ++ * \brief Generates a polyseed wallet or restores one. ++ * \param wallet_ Name of wallet file ++ * \param password Password of wallet file ++ * \param passphrase Seed offset passphrase ++ * \param recover Whether it is a restore ++ * \param seed_words If it is a restore, the polyseed ++ * \param create_address_file Whether to create an address file ++ * \return The secret key of the generated wallet ++ */ ++void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, ++ const polyseed::data &seed, const epee::wipeable_string& passphrase, bool recover, uint64_t restoreHeight, bool create_address_file) ++{ ++ clear(); ++ prepare_file_names(wallet_); ++ ++ if (!wallet_.empty()) { ++ boost::system::error_code ignored_ec; ++ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file); ++ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); ++ } ++ ++ m_account.create_from_polyseed(seed, passphrase); ++ ++ init_type(hw::device::device_type::SOFTWARE); ++ m_polyseed = true; ++ setup_keys(password); ++ ++ if (recover) { ++ m_refresh_from_block_height = estimate_blockchain_height(restoreHeight > 0 ? restoreHeight : seed.birthday()); ++ } else { ++ m_refresh_from_block_height = estimate_blockchain_height(); ++ } ++ ++ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); ++ ++ setup_new_blockchain(); ++ ++ if (!wallet_.empty()) ++ store(); ++} ++ + /*! + * \brief Generates a wallet or restores one. Assumes the multisig setup + * has already completed for the provided multisig info. +@@ -5497,7 +5562,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip + return retval; + } + +- uint64_t wallet2::estimate_blockchain_height() ++ uint64_t wallet2::estimate_blockchain_height(uint64_t time) + { + // -1 month for fluctuations in block time and machine date/time setup. + // avg seconds per block +@@ -5521,7 +5586,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip + // the daemon is currently syncing. + // If we use the approximate height we subtract one month as + // a safety margin. +- height = get_approximate_blockchain_height(); ++ height = get_approximate_blockchain_height(time); + uint64_t target_height = get_daemon_blockchain_target_height(err); + if (err.empty()) { + if (target_height < height) +@@ -13546,7 +13611,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) + return target_height; + } + +-uint64_t wallet2::get_approximate_blockchain_height() const ++uint64_t wallet2::get_approximate_blockchain_height(uint64_t t) const + { + uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : (time(NULL) - 1522624244)/307; + LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); +@@ -15262,15 +15327,6 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin + //---------------------------------------------------------------------------------------------------- + uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day) + { +- uint32_t version; +- if (!check_connection(&version)) +- { +- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); +- } +- if (version < MAKE_CORE_RPC_VERSION(1, 6)) +- { +- throw std::runtime_error("this function requires RPC version 1.6 or higher"); +- } + std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 }; + date.tm_year = year - 1900; + date.tm_mon = month - 1; +@@ -15279,7 +15335,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui + { + throw std::runtime_error("month or day out of range"); + } ++ + uint64_t timestamp_target = std::mktime(&date); ++ ++ return get_blockchain_height_by_timestamp(timestamp_target); ++} ++ ++uint64_t wallet2::get_blockchain_height_by_timestamp(uint64_t timestamp_target) { ++ uint32_t version; ++ if (!check_connection(&version)) ++ { ++ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); ++ } ++ if (version < MAKE_CORE_RPC_VERSION(1, 6)) ++ { ++ throw std::runtime_error("this function requires RPC version 1.6 or higher"); ++ } ++ + std::string err; + uint64_t height_min = 0; + uint64_t height_max = get_daemon_blockchain_height(err) - 1; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index df34f9abf..db5c1feb3 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -72,6 +72,7 @@ + #include "message_store.h" + #include "wallet_light_rpc.h" + #include "wallet_rpc_helpers.h" ++#include "polyseed/polyseed.hpp" + + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" +@@ -854,6 +855,20 @@ private: + void generate(const std::string& wallet_, const epee::wipeable_string& password, + const epee::wipeable_string& multisig_data, bool create_address_file = false); + ++ /*! ++ * \brief Generates a wallet from a polyseed. ++ * @param wallet_ Name of wallet file ++ * @param password Password of wallet file ++ * @param seed Polyseed data ++ * @param passphrase Optional seed offset passphrase ++ * @param recover Whether it is a restore ++ * @param restoreHeight Override the embedded restore height ++ * @param create_address_file Whether to create an address file ++ */ ++ void generate(const std::string& wallet_, const epee::wipeable_string& password, ++ const polyseed::data &seed, const epee::wipeable_string& passphrase = "", ++ bool recover = false, uint64_t restoreHeight = 0, bool create_address_file = false); ++ + /*! + * \brief Generates a wallet or restores one. + * \param wallet_ Name of wallet file +@@ -1018,6 +1033,15 @@ private: + bool is_deterministic() const; + bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; + ++ /*! ++ * \brief get_polyseed Gets the polyseed (if available) and passphrase (if set) needed to recover the wallet. ++ * @param seed Polyseed mnemonic phrase ++ * @param passphrase Seed offset passphrase that was used to restore the wallet ++ * @return Returns true if the wallet has a polyseed. ++ * Note: both the mnemonic phrase and the passphrase are needed to recover the wallet ++ */ ++ bool get_polyseed(epee::wipeable_string& seed, epee::wipeable_string &passphrase) const; ++ + /*! + * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. + */ +@@ -1469,8 +1493,8 @@ private: + /*! + * \brief Calculates the approximate blockchain height from current date/time. + */ +- uint64_t get_approximate_blockchain_height() const; +- uint64_t estimate_blockchain_height(); ++ uint64_t get_approximate_blockchain_height(uint64_t time = 0) const; ++ uint64_t estimate_blockchain_height(uint64_t time = 0); + std::vector select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); + std::vector select_available_outputs(const std::function &f); + std::vector select_available_unmixable_outputs(); +@@ -1563,6 +1587,7 @@ private: + bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error); + + uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31 ++ uint64_t get_blockchain_height_by_timestamp(uint64_t timestamp); + + bool is_synced(); + +@@ -1900,6 +1925,7 @@ private: + std::string seed_language; /*!< Language of the mnemonics (seed). */ + bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ + bool m_watch_only; /*!< no spend key */ ++ bool m_polyseed; + bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */ + uint32_t m_multisig_threshold; + std::vector m_multisig_signers; +-- +2.39.2 + diff --git a/patches/wownero/0003-wallet-background-sync-with-just-the-view-key.patch b/patches/wownero/0003-wallet-background-sync-with-just-the-view-key.patch index 95a8cfa..9c9347e 100644 --- a/patches/wownero/0003-wallet-background-sync-with-just-the-view-key.patch +++ b/patches/wownero/0003-wallet-background-sync-with-just-the-view-key.patch @@ -1,4 +1,4 @@ -From 9d6099e9c47f7cbd12f4d6550d6fda520733c2c5 Mon Sep 17 00:00:00 2001 +From 05003c0562e12cd9a768b7799982f1b05149cae2 Mon Sep 17 00:00:00 2001 From: j-berman Date: Thu, 13 Oct 2022 18:33:33 -0700 Subject: [PATCH 3/9] wallet: background sync with just the view key @@ -50,7 +50,7 @@ cache. 20 files changed, 2350 insertions(+), 132 deletions(-) diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 4931c3740..2d556f285 100644 +index 020e71e9d..fbe0a79ba 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -158,6 +158,17 @@ DISABLE_VS_WARNINGS(4244 4345) @@ -748,7 +748,7 @@ index 8e3477ba3..ae1aa312b 100644 bool set_show_detailed_prompt(const std::vector &args = std::vector()); bool set_inactivity_lock_timeout(const std::vector &args = std::vector()); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index db9c2b5d9..5ef3132d2 100644 +index ceb0bcb8b..b6ef2956f 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -56,6 +56,40 @@ using namespace cryptonote; @@ -1326,7 +1326,7 @@ index a0ed60a39..626c63fc5 100644 virtual TransactionHistory * history() = 0; virtual AddressBook * addressBook() = 0; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 83aba6253..d7e2a9699 100644 +index 8b6682da9..5b4977b8d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -158,6 +158,8 @@ static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; @@ -4342,5 +4342,5 @@ index 1e10e1f86..bff33a561 100644 + } + return self.rpc.send_json_rpc_request(stop_background_sync) -- -2.44.0 +2.39.2 diff --git a/patches/wownero/0004-airgap.patch b/patches/wownero/0004-airgap.patch index b0c3c02..5f5969b 100644 --- a/patches/wownero/0004-airgap.patch +++ b/patches/wownero/0004-airgap.patch @@ -1,4 +1,4 @@ -From 60c89b5c4e2e874e725915ed0a8b7709849ef140 Mon Sep 17 00:00:00 2001 +From 2ee7b25a0cee0ee0fbbf7fadd494b2eac85bbdd6 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 26 Mar 2024 09:06:32 +0100 Subject: [PATCH 4/9] airgap @@ -12,7 +12,7 @@ Subject: [PATCH 4/9] airgap 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 5ef3132d2..1c9a709d4 100644 +index b6ef2956f..20be5c9f0 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1172,6 +1172,24 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const @@ -94,7 +94,7 @@ index 626c63fc5..39b942ef4 100644 * \brief exportKeyImages - exports key images to file * \param filename diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index d7e2a9699..f996863ff 100644 +index 5b4977b8d..31855a256 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7432,6 +7432,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * @@ -166,5 +166,5 @@ index 2f2664f8e..51dbb5b89 100644 std::map balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; std::map>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); -- -2.44.0 +2.39.2 diff --git a/patches/wownero/0005-coin-control.patch b/patches/wownero/0005-coin-control.patch index e2dbdb1..b5d74bb 100644 --- a/patches/wownero/0005-coin-control.patch +++ b/patches/wownero/0005-coin-control.patch @@ -1,4 +1,4 @@ -From 9f9937ef8f4822c498e10e5bbfc57dfdfb7137f7 Mon Sep 17 00:00:00 2001 +From 878df2babdc8abd7d38edae49aa321d3b2185bbd Mon Sep 17 00:00:00 2001 From: tobtoht Date: Wed, 27 Mar 2024 16:31:36 +0100 Subject: [PATCH 5/9] coin control @@ -118,7 +118,7 @@ index 3293d8ae9..bcd8b517f 100644 WalletImpl *m_wallet; std::vector m_rows; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 1c9a709d4..b935723f7 100644 +index 20be5c9f0..0430a7f9b 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2018,11 +2018,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector Date: Tue, 26 Mar 2024 09:44:54 +0100 Subject: [PATCH 6/9] fix build @@ -133,5 +133,5 @@ index 000000000..f05cb2b6a + #else + #ifdef POLYSEED_SHARED -- -2.44.0 +2.39.2 diff --git a/patches/wownero/0007-macos-build-fix.patch b/patches/wownero/0007-macos-build-fix.patch index 10c33d8..006ddfa 100644 --- a/patches/wownero/0007-macos-build-fix.patch +++ b/patches/wownero/0007-macos-build-fix.patch @@ -1,4 +1,4 @@ -From 0f3c19706a35798a28eb040858052292a3709312 Mon Sep 17 00:00:00 2001 +From 7360e324de154023d185d3181e46fcc2ef397ccc Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 28 Mar 2024 02:03:08 +0100 Subject: [PATCH 7/9] macos build fix @@ -111,5 +111,5 @@ index 000000000..a261636e8 +2.39.2 + -- -2.44.0 +2.39.2 diff --git a/patches/wownero/0008-FIX-wallet-listener-crashing.patch b/patches/wownero/0008-FIX-wallet-listener-crashing.patch index ae5c712..822202a 100644 --- a/patches/wownero/0008-FIX-wallet-listener-crashing.patch +++ b/patches/wownero/0008-FIX-wallet-listener-crashing.patch @@ -1,4 +1,4 @@ -From a2beff7fc49b80972f78dddedf0ea8c4db145f72 Mon Sep 17 00:00:00 2001 +From d9544a736cbccc173dcdf79538ee206a4ec63db6 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 2 Apr 2024 11:56:09 +0200 Subject: [PATCH 8/9] FIX: wallet listener crashing @@ -8,7 +8,7 @@ Subject: [PATCH 8/9] FIX: wallet listener crashing 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index b935723f7..432ca4fbf 100644 +index 0430a7f9b..95d1fcbb8 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -201,8 +201,11 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback @@ -54,5 +54,5 @@ index b935723f7..432ca4fbf 100644 virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) -- -2.44.0 +2.39.2 diff --git a/patches/wownero/0009-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/wownero/0009-fix-missing-___clear_cache-when-targetting-iOS.patch index e8809cd..b7a0b9b 100644 --- a/patches/wownero/0009-fix-missing-___clear_cache-when-targetting-iOS.patch +++ b/patches/wownero/0009-fix-missing-___clear_cache-when-targetting-iOS.patch @@ -1,4 +1,4 @@ -From 8cfcf5b132c41aedbc9c9a0932ffb832cf410761 Mon Sep 17 00:00:00 2001 +From dec7e838540e0b9c3fab27886864de29221a9fe9 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 2 Apr 2024 17:07:19 +0200 Subject: [PATCH 9/9] fix missing ___clear_cache when targetting iOS @@ -9,17 +9,17 @@ Subject: [PATCH 9/9] fix missing ___clear_cache when targetting iOS 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules -index 86009b6b6..f723bd2d8 100644 +index bab1cbed2..354638453 100644 --- a/.gitmodules +++ b/.gitmodules -@@ -20,6 +20,6 @@ - url = https://github.com/tevador/polyseed.git +@@ -17,6 +17,6 @@ + branch = monero [submodule "external/randomwow"] path = external/randomwow - url = https://git.wownero.com/wownero/RandomWOW - branch = 1.2.1-wow + url = https://github.com/mrcyjanek/RandomWOW -+ branch = cyjan-fix-ios ++ branch = cyjan-fix-ios diff --git a/external/randomwow b/external/randomwow index 27b099b6d..6f30d4b92 160000 @@ -29,5 +29,5 @@ index 27b099b6d..6f30d4b92 160000 -Subproject commit 27b099b6dd6fef6e17f58c6dfe00009e9c5df587 +Subproject commit 6f30d4b924fecb231e5b683915cc75d18b3b5866 -- -2.44.0 +2.39.2 -- cgit v1.2.3