diff --git a/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/build-aux/m4/ax_cxx_compile_stdcxx.m4 index 5a787b485f..7c8e6123ac 100644 --- a/build-aux/m4/ax_cxx_compile_stdcxx.m4 +++ b/build-aux/m4/ax_cxx_compile_stdcxx.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS @@ -10,13 +10,13 @@ # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and -# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) -# or '14' (for the C++14 standard). +# CXXCPP to enable support. VERSION may be '11', '14', '17', or '20' for +# the respective C++ standard version. # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with -# preference for an extended mode. +# preference for no added switch, and then for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is @@ -33,21 +33,26 @@ # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# Copyright (c) 2019 Enji Cooper +# Copyright (c) 2020 Jason Merrill +# Copyright (c) 2021 Jörn Heusipp # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 4 +#serial 18 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl - m4_if([$1], [11], [], - [$1], [14], [], - [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])], + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [$1], [20], [ax_cxx_compile_alternatives="20"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], @@ -57,26 +62,23 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) - m4_if([$4], [], [ax_cxx_compile_cxx$1_try_default=true], - [$4], [default], [ax_cxx_compile_cxx$1_try_default=true], - [$4], [nodefault], [ax_cxx_compile_cxx$1_try_default=false], - [m4_fatal([invalid fourth argument `$4' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no - m4_if([$4], [nodefault], [], [dnl - AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, - ax_cv_cxx_compile_cxx$1, - [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], - [ax_cv_cxx_compile_cxx$1=yes], - [ax_cv_cxx_compile_cxx$1=no])]) - if test x$ax_cv_cxx_compile_cxx$1 = xyes; then - ac_success=yes - fi]) + m4_if([$2], [], [dnl + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi]) m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then - for switch in -std=gnu++$1 -std=gnu++0x; do + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, @@ -102,22 +104,36 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" - for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do - cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) - AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, - $cachevar, - [ac_save_CXX="$CXX" - CXX="$CXX $switch" - AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], - [eval $cachevar=yes], - [eval $cachevar=no]) - CXX="$ac_save_CXX"]) - if eval test x\$$cachevar = xyes; then - CXX="$CXX $switch" - if test -n "$CXXCPP" ; then - CXXCPP="$CXXCPP $switch" + dnl MSVC needs -std:c++NN for C++17 and later (default is C++14) + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do + if test x"$switch" = xMSVC; then + dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide + dnl with -std=c++17. We suffix the cache variable name with _MSVC to + dnl avoid this. + switch=-std:c++${alternative} + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC]) + else + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) fi - ac_success=yes + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then break fi done @@ -146,7 +162,6 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) - dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], @@ -154,6 +169,23 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) +dnl Test body for checking C++17 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Test body for checking C++20 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_20 +) + dnl Tests for new features in C++11 @@ -166,7 +198,11 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ #error "This is not a C++ compiler" -#elif __cplusplus < 201103L +// MSVC always sets __cplusplus to 199711L in older versions; newer versions +// only set it correctly if /Zc:__cplusplus is specified as well as a +// /std:c++NN switch: +// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ +#elif __cplusplus < 201103L && !defined _MSC_VER #error "This is not a C++11 compiler" @@ -191,11 +227,13 @@ namespace cxx11 struct Base { + virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { + virtual ~Derived() override {} virtual void f() override {} }; @@ -396,13 +434,13 @@ namespace cxx11 template struct sum { - static const int value = N0 + sum::value; + static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { - static const int value = 0; + static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); @@ -455,7 +493,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ #error "This is not a C++ compiler" -#elif __cplusplus < 201402L +#elif __cplusplus < 201402L && !defined _MSC_VER #error "This is not a C++14 compiler" @@ -524,7 +562,7 @@ namespace cxx14 } - namespace test_digit_seperators + namespace test_digit_separators { constexpr auto ten_million = 100'000'000; @@ -566,3 +604,415 @@ namespace cxx14 #endif // __cplusplus >= 201402L ]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201703L && !defined _MSC_VER + +#error "This is not a C++17 compiler" + +#else + +#include +#include +#include + +namespace cxx17 +{ + + namespace test_constexpr_lambdas + { + + constexpr int foo = [](){return 42;}(); + + } + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + + namespace test_template_argument_deduction_for_class_templates + { + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + + namespace test_structured_bindings + { + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } + + namespace test_exception_spec_type_system + { + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus < 201703L && !defined _MSC_VER + +]]) + + +dnl Tests for new features in C++20 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[ + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 202002L && !defined _MSC_VER + +#error "This is not a C++20 compiler" + +#else + +#include + +namespace cxx20 +{ + +// As C++20 supports feature test macros in the standard, there is no +// immediate need to actually test for feature availability on the +// Autoconf side. + +} // namespace cxx20 + +#endif // __cplusplus < 202002L && !defined _MSC_VER + +]]) \ No newline at end of file diff --git a/build-aux/m4/ax_cxx_compile_stdcxx_17.m4 b/build-aux/m4/ax_cxx_compile_stdcxx_17.m4 new file mode 100644 index 0000000000..685247a5ad --- /dev/null +++ b/build-aux/m4/ax_cxx_compile_stdcxx_17.m4 @@ -0,0 +1,35 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_17.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_17([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++17 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++17. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 2 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_17], [AX_CXX_COMPILE_STDCXX([17], [$1], [$2])]) \ No newline at end of file diff --git a/configure.ac b/configure.ac index 34eea4f78f..b6bcd7415b 100755 --- a/configure.ac +++ b/configure.ac @@ -80,8 +80,8 @@ AC_CONFIG_MACRO_DIR([build-aux/m4]) m4_include([build-aux/m4/ax_check_compile_flag.m4]) m4_include([build-aux/m4/ax_cxx_compile_stdcxx.m4]) -#check for c++14 -AX_CXX_COMPILE_STDCXX([14], [noext], [mandatory], [nodefault]) +#check for c++17 +AX_CXX_COMPILE_STDCXX_17([noext], [mandatory]) # Make the compilation flags quiet unless V=1 is used. m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/cppForSwig/BinaryData.h b/cppForSwig/BinaryData.h index 610bccaf3c..d1bb1913f4 100644 --- a/cppForSwig/BinaryData.h +++ b/cppForSwig/BinaryData.h @@ -108,7 +108,7 @@ class BinaryData ///////////////////////////////////////////////////////////////////////////// BinaryData(void) : data_(0) { } explicit BinaryData(size_t sz) { alloc(sz); } - BinaryData(uint8_t const * inData, size_t sz) + BinaryData(uint8_t const * inData, size_t sz) { copyFrom(inData, sz); } BinaryData(char const * inData, size_t sz) { copyFrom(inData, sz); } BinaryData(uint8_t const * dstart, uint8_t const * dend ) @@ -561,16 +561,14 @@ class BinaryData std::vector data_; private: - void alloc(size_t sz) + void alloc(size_t sz) { if(sz != getSize()) { data_.clear(); data_.resize(sz); } - } - }; diff --git a/cppForSwig/BridgeAPI/CppBridge.cpp b/cppForSwig/BridgeAPI/CppBridge.cpp index 5987b738fb..7ecc22a071 100755 --- a/cppForSwig/BridgeAPI/CppBridge.cpp +++ b/cppForSwig/BridgeAPI/CppBridge.cpp @@ -271,7 +271,7 @@ void CppBridge::createBackupStringForWallet(const string& waaId, }); auto lbd = passPromptObj->getLambda(); - Armory::Seeds::WalletBackup backupData; + unique_ptr backupData = nullptr; try { //grab wallet @@ -290,7 +290,7 @@ void CppBridge::createBackupStringForWallet(const string& waaId, auto reply = payload->mutable_reply(); reply->set_reference_id(msgId); - if (backupData.rootClear_.empty()) + if (backupData == nullptr) { //return on error reply->set_success(false); @@ -298,25 +298,58 @@ void CppBridge::createBackupStringForWallet(const string& waaId, return; } - auto backupStringProto = reply->mutable_wallet()->mutable_backup_string(); - for (auto& line : backupData.rootClear_) - backupStringProto->add_root_clear(line); + auto backupE16 = dynamic_cast( + backupData.get()); + if (backupE16 == nullptr) + { + throw runtime_error("[createBackupStringForWallet]" + " invalid backup type"); + } - for (auto& line : backupData.rootEncr_) - backupStringProto->add_root_encr(line); + auto backupStringProto = reply->mutable_wallet()->mutable_backup_string(); - if (!backupData.chaincodeClear_.empty()) + //cleartext root { - for (auto& line : backupData.chaincodeClear_) - backupStringProto->add_chain_clear(line); + auto line1 = backupE16->getRoot( + Armory::Seeds::Backup_Easy16::LineIndex::One, false); + backupStringProto->add_root_clear(line1.data(), line1.size()); + auto line2 = backupE16->getRoot( + Armory::Seeds::Backup_Easy16::LineIndex::Two, false); + backupStringProto->add_root_clear(line2.data(), line2.size()); + + //encrypted root + auto line3 = backupE16->getRoot( + Armory::Seeds::Backup_Easy16::LineIndex::One, true); + backupStringProto->add_root_encr(line3.data(), line3.size()); + auto line4 = backupE16->getRoot( + Armory::Seeds::Backup_Easy16::LineIndex::Two, true); + backupStringProto->add_root_encr(line3.data(), line3.size()); + } - for (auto& line : backupData.chaincodeEncr_) - backupStringProto->add_chain_encr(line); + if (backupE16->hasChaincode()) + { + //cleartext chaincode + auto line1 = backupE16->getChaincode( + Armory::Seeds::Backup_Easy16::LineIndex::One, false); + backupStringProto->add_chain_clear(line1.data(), line1.size()); + + auto line2 = backupE16->getChaincode( + Armory::Seeds::Backup_Easy16::LineIndex::Two, false); + backupStringProto->add_chain_clear(line2.data(), line2.size()); + + //encrypted chaincode + auto line3 = backupE16->getChaincode( + Armory::Seeds::Backup_Easy16::LineIndex::One, true); + backupStringProto->add_chain_encr(line3.data(), line3.size()); + + auto line4 = backupE16->getChaincode( + Armory::Seeds::Backup_Easy16::LineIndex::Two, true); + backupStringProto->add_chain_encr(line4.data(), line4.size()); } //secure print passphrase - backupStringProto->set_sp_pass( - backupData.spPass_.toCharPtr(), backupData.spPass_.getSize()); + auto spPass = backupE16->getSpPass(); + backupStringProto->set_sp_pass(spPass.data(), spPass.size()); reply->set_success(true); writeToClient(move(payload)); diff --git a/cppForSwig/BridgeAPI/WalletManager.cpp b/cppForSwig/BridgeAPI/WalletManager.cpp index fbe582186a..a9f29b1a20 100644 --- a/cppForSwig/BridgeAPI/WalletManager.cpp +++ b/cppForSwig/BridgeAPI/WalletManager.cpp @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////////// // // -// Copyright (C) 2016-20, goatpig // +// Copyright (C) 2016-2023, goatpig // // Distributed under the MIT license // // See LICENSE-MIT or https://opensource.org/licenses/MIT // // // @@ -9,6 +9,7 @@ #include "WalletManager.h" #include "Wallets/Seeds/Backups.h" #include "PassphrasePrompt.h" +#include "../Wallets/Seeds/Seeds.h" #ifdef _WIN32 #include "leveldb_windows_port\win32_posix\dirent_win32.h" @@ -20,6 +21,7 @@ using namespace std; using namespace Armory::Signer; using namespace Armory::Accounts; using namespace Armory::Wallets; +using namespace Armory::Seeds; #define WALLET_135_HEADER "\xbaWALLET\x00" #define PYBTC_ADDRESS_SIZE 237 @@ -151,8 +153,11 @@ shared_ptr WalletManager::createNewWallet( if (extraEntropy.getSize() >= 32) root.XOR(extraEntropy); - auto wallet = AssetWallet_Single::createFromPrivateRoot_Armory135( - path_, root, {}, pass, controlPass, lookup); + unique_ptr seed(new ClearTextSeed_Armory135(root, + ClearTextSeed_Armory135::LegacyType::Armory200)); + auto wallet = AssetWallet_Single::createFromSeed(move(seed), + pass, controlPass, + path_, lookup); return addWallet(wallet, wallet->getMainAccountID()); } @@ -635,7 +640,7 @@ map> WalletContainer::getUpdatedAddressMap( } //////////////////////////////////////////////////////////////////////////////// -Armory::Seeds::WalletBackup WalletContainer::getBackupStrings( +unique_ptr WalletContainer::getBackupStrings( const PassphraseLambda& passLbd) const { auto wltSingle = dynamic_pointer_cast(wallet_); @@ -932,9 +937,11 @@ shared_ptr Armory135Header::migrate( } else { - wallet = AssetWallet_Single::createFromPrivateRoot_Armory135( - folder, decryptedRoot, chaincodeCopy, - privKeyPass, controlPass, highestIndex); + unique_ptr seed(new ClearTextSeed_Armory135( + decryptedRoot, chaincodeCopy)); + wallet = AssetWallet_Single::createFromSeed(move(seed), + privKeyPass, controlPass, + folder, highestIndex); } //main account id, check it matches armory wallet id diff --git a/cppForSwig/BridgeAPI/WalletManager.h b/cppForSwig/BridgeAPI/WalletManager.h index 263588bac8..6e2cffd251 100644 --- a/cppForSwig/BridgeAPI/WalletManager.h +++ b/cppForSwig/BridgeAPI/WalletManager.h @@ -215,7 +215,8 @@ class WalletContainer Armory::Wallets::AssetKeyType getHighestUsedIndex(void) const; std::map> getUpdatedAddressMap(); - Armory::Seeds::WalletBackup getBackupStrings(const PassphraseLambda&) const; + std::unique_ptr getBackupStrings( + const PassphraseLambda&) const; void setComment(const std::string&, const std::string&); void setLabels(const std::string&, const std::string&); diff --git a/cppForSwig/SecureBinaryData.h b/cppForSwig/SecureBinaryData.h index 87fb06a0ec..e793d0018c 100644 --- a/cppForSwig/SecureBinaryData.h +++ b/cppForSwig/SecureBinaryData.h @@ -148,7 +148,17 @@ class SecureBinaryData : public BinaryData return {}; SecureBinaryData sbd(str.size()); - memcpy(sbd.getPtr(), str.c_str(), str.size()); + memcpy(sbd.getPtr(), str.data(), str.size()); + return sbd; + } + + static SecureBinaryData fromStringView(const std::string_view& strv) + { + if (strv.empty()) + return {}; + + SecureBinaryData sbd(strv.size()); + memcpy(sbd.getPtr(), strv.data(), strv.size()); return sbd; } }; diff --git a/cppForSwig/Signer/ResolverFeed.cpp b/cppForSwig/Signer/ResolverFeed.cpp index 0fd4e7e342..6e4eec4900 100644 --- a/cppForSwig/Signer/ResolverFeed.cpp +++ b/cppForSwig/Signer/ResolverFeed.cpp @@ -42,7 +42,9 @@ uint32_t BIP32_PublicDerivedRoot::getThisFingerprint() const if (thisFingerprint_ == UINT32_MAX) { BIP32_Node node; - node.initFromBase58(SecureBinaryData::fromString(xpub_)); + BinaryDataRef xpubRef; + xpubRef.setRef(xpub_); + node.initFromBase58(xpubRef); thisFingerprint_ = node.getThisFingerprint(); } diff --git a/cppForSwig/Wallets/AssetEncryption.h b/cppForSwig/Wallets/AssetEncryption.h index 6929fd9d44..85c151309c 100644 --- a/cppForSwig/Wallets/AssetEncryption.h +++ b/cppForSwig/Wallets/AssetEncryption.h @@ -20,7 +20,6 @@ #define PRIVKEY_BYTE 0x82 #define ENCRYPTIONKEY_BYTE 0x83 -#define WALLET_SEED_BYTE 0x84 #define CIPHER_DATA_VERSION 0x00000001 #define ENCRYPTION_KEY_VERSION 0x00000001 diff --git a/cppForSwig/Wallets/AuthorizedPeers.cpp b/cppForSwig/Wallets/AuthorizedPeers.cpp index f3fcc61ce7..df02273934 100644 --- a/cppForSwig/Wallets/AuthorizedPeers.cpp +++ b/cppForSwig/Wallets/AuthorizedPeers.cpp @@ -14,6 +14,7 @@ #include "AuthorizedPeers.h" #include "btc/ecc.h" #include "WalletFileInterface.h" +#include "Seeds/Seeds.h" #include "TerminalPassphrasePrompt.h" @@ -21,6 +22,7 @@ using namespace std; using namespace Armory::Assets; using namespace Armory::Accounts; using namespace Armory::Wallets; +using namespace Armory::Seeds; //////////////////////////////////////////////////////////////////////////////// AuthorizedPeers::AuthorizedPeers( @@ -170,10 +172,10 @@ void AuthorizedPeers::createWallet( derPath.push_back(0xF0000000); //generate bip32 node from random seed - auto&& seed = CryptoPRNG::generateRandom(32); - - wallet_ = AssetWallet_Single::createFromSeed_BIP32_Blank( - baseDir, seed, password, controlPassphrase); + wallet_ = AssetWallet_Single::createFromSeed( + make_unique( + CryptoPRNG::generateRandom(32), SeedType::BIP32_Virgin), + password, controlPassphrase, baseDir); auto wltSingle = dynamic_pointer_cast(wallet_); auto rootBip32 = dynamic_pointer_cast( diff --git a/cppForSwig/Wallets/BIP32_Node.cpp b/cppForSwig/Wallets/BIP32_Node.cpp index 7cee9ecf15..e85abc3a00 100644 --- a/cppForSwig/Wallets/BIP32_Node.cpp +++ b/cppForSwig/Wallets/BIP32_Node.cpp @@ -120,7 +120,7 @@ void BIP32_Node::initFromSeed(const SecureBinaryData& seed) } //////////////////////////////////////////////////////////////////////////////// -void BIP32_Node::initFromBase58(const SecureBinaryData& b58) +void BIP32_Node::initFromBase58(BinaryDataRef b58) { //sbd doesnt 0 terminate strings as it is not specialized for char strings, //have to set it manually since libbtc b58 code derives string length from diff --git a/cppForSwig/Wallets/BIP32_Node.h b/cppForSwig/Wallets/BIP32_Node.h index 3af9c10da2..192034a177 100644 --- a/cppForSwig/Wallets/BIP32_Node.h +++ b/cppForSwig/Wallets/BIP32_Node.h @@ -38,7 +38,7 @@ class BIP32_Node //init void initFromSeed(const SecureBinaryData&); - void initFromBase58(const SecureBinaryData&); + void initFromBase58(BinaryDataRef); void initFromPrivateKey(uint8_t depth, unsigned leaf_id, unsigned fingerprint, const SecureBinaryData& privKey, const SecureBinaryData& chaincode); void initFromPublicKey(uint8_t depth, unsigned leaf_id, unsigned fingerprint, diff --git a/cppForSwig/Wallets/DecryptedDataContainer.cpp b/cppForSwig/Wallets/DecryptedDataContainer.cpp index 16cd45e827..15fed862b8 100644 --- a/cppForSwig/Wallets/DecryptedDataContainer.cpp +++ b/cppForSwig/Wallets/DecryptedDataContainer.cpp @@ -63,11 +63,14 @@ void DecryptedDataContainer::lockOther( shared_ptr other) { if (!ownsLock()) - throw DecryptedDataContainerException("unlocked/does not own lock"); + { + throw DecryptedDataContainerException( + "[DecryptedDataContainer::lockOther] unlocked/does not own lock"); + } if (lockedDecryptedData_ == nullptr) throw DecryptedDataContainerException( - "nullptr lock! how did we get this far?"); + "nullptr lock! how did we get this far?"); otherLocks_.push_back(OtherLockedContainer(other)); } @@ -103,11 +106,17 @@ unique_ptr DecryptedDataContainer::deriveEncryptionKey( { //sanity check if (!ownsLock()) - throw DecryptedDataContainerException("unlocked/does not own lock"); + { + throw DecryptedDataContainerException( + "[DecryptedDataContainer::deriveEncryptionKey]" + " unlocked/does not own lock"); + } if (lockedDecryptedData_ == nullptr) + { throw DecryptedDataContainerException( - "nullptr lock! how did we get this far?"); + "nullptr lock! how did we get this far?"); + } //does the decryption key have this derivation? auto derivationIter = decrKey->derivedKeys_.find(kdfid); @@ -146,7 +155,11 @@ const SecureBinaryData& DecryptedDataContainer::getClearTextAssetData( //sanity check if (!ownsLock()) - throw DecryptedDataContainerException("unlocked/does not own lock"); + { + throw DecryptedDataContainerException( + "[DecryptedDataContainer::getClearTextAssetData]" + " unlocked/does not own lock"); + } if (lockedDecryptedData_ == nullptr) throw DecryptedDataContainerException( @@ -289,7 +302,11 @@ EncryptionKeyId DecryptedDataContainer::populateEncryptionKey( //sanity check if (!ownsLock()) - throw DecryptedDataContainerException("unlocked/does not own lock"); + { + throw DecryptedDataContainerException( + "[DecryptedDataContainer::populateEncryptionKey]" + " unlocked/does not own lock"); + } if (lockedDecryptedData_ == nullptr) throw DecryptedDataContainerException( @@ -437,7 +454,11 @@ SecureBinaryData DecryptedDataContainer::encryptData( throw DecryptedDataContainerException("null cipher"); if (!ownsLock()) - throw DecryptedDataContainerException("unlocked/does not own lock"); + { + throw DecryptedDataContainerException( + "[DecryptedDataContainer::encryptData]" + " unlocked/does not own lock"); + } if (lockedDecryptedData_ == nullptr) throw DecryptedDataContainerException( @@ -573,7 +594,11 @@ void DecryptedDataContainer::deleteFromDisk( { //sanity checks if (!ownsLock()) - throw DecryptedDataContainerException("unlocked/does not own lock"); + { + throw DecryptedDataContainerException( + "[DecryptedDataContainer::deleteFromDisk]" + " unlocked/does not own lock"); + } //erase key, db interface will wipe it from file tx->erase(key); @@ -656,7 +681,11 @@ void DecryptedDataContainer::encryptEncryptionKey( //we have to own the lock on this container before proceeding if (!ownsLock()) - throw DecryptedDataContainerException("unlocked/does not own lock"); + { + throw DecryptedDataContainerException( + "[DecryptedDataContainer::encryptEncryptionKey]" + " unlocked/does not own lock"); + } if (lockedDecryptedData_ == nullptr) { @@ -790,7 +819,11 @@ void DecryptedDataContainer::eraseEncryptionKey( //we have to own the lock on this container before proceeding if (!ownsLock()) - throw DecryptedDataContainerException("unlocked/does not own lock"); + { + throw DecryptedDataContainerException( + "[DecryptedDataContainer::eraseEncryptionKey]" + " unlocked/does not own lock"); + } if (lockedDecryptedData_ == nullptr) { diff --git a/cppForSwig/Wallets/Seeds/Backups.cpp b/cppForSwig/Wallets/Seeds/Backups.cpp index 709a8d0363..c8caf140f9 100644 --- a/cppForSwig/Wallets/Seeds/Backups.cpp +++ b/cppForSwig/Wallets/Seeds/Backups.cpp @@ -11,6 +11,7 @@ #include "BtcUtils.h" #include "../WalletIdTypes.h" #include "Seeds.h" +#include "protobuf/BridgeProto.pb.h" #define EASY16_CHECKSUM_LEN 2 #define EASY16_INDEX_MAX 15 @@ -24,7 +25,7 @@ using namespace Armory::Assets; using namespace Armory::Wallets; //////////////////////////////////////////////////////////////////////////////// -const vector BackupEasy16::e16chars_ = +const vector Easy16Codec::e16chars_ = { 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', @@ -33,18 +34,18 @@ const vector BackupEasy16::e16chars_ = }; //////////////////////////////////////////////////////////////////////////////// -const set BackupEasy16::eligibleIndexes_ = +const set Easy16Codec::eligibleIndexes_ = { - (uint8_t)BackupType::Armory135, - (uint8_t)BackupType::BIP32_Seed_Structured, - (uint8_t)BackupType::BIP32_Root, - (uint8_t)BackupType::BIP32_Seed_Virgin, + BackupType::Armory135, + BackupType::Armory200a, + BackupType::Armory200b, + BackupType::Armory200c, + BackupType::Armory200d }; - //////////////////////////////////////////////////////////////////////////////// -/* +/* - comment from etotheipi: - Nothing up my sleeve! Need some hardcoded random numbers to use for encryption IV and salt. Using the first 256 digits of Pi for the the IV, and first 256 digits of e for the salt (hashed) @@ -72,10 +73,10 @@ const uint32_t SecurePrint::kdfBytes_ = 16 * 1024 * 1024; //////////////////////////////////////////////////////////////////////////////// //// -//// BackupEasy16 +//// Easy16Codec //// //////////////////////////////////////////////////////////////////////////////// -BinaryData BackupEasy16::getHash(const BinaryDataRef& data, uint8_t hint) +BinaryData Easy16Codec::getHash(const BinaryDataRef& data, uint8_t hint) { if (hint == 0) { @@ -92,92 +93,111 @@ BinaryData BackupEasy16::getHash(const BinaryDataRef& data, uint8_t hint) } //////////////////////////////////////////////////////////////////////////////// -uint8_t BackupEasy16::verifyChecksum( +uint8_t Easy16Codec::verifyChecksum( const BinaryDataRef& data, const BinaryDataRef& checksum) { for (const auto& indexCandidate : eligibleIndexes_) { - auto hash = getHash(data, indexCandidate); + auto hash = getHash(data, (uint8_t)indexCandidate); if (hash.getSliceRef(0, EASY16_CHECKSUM_LEN) == checksum) - return indexCandidate; + return (uint8_t)indexCandidate; } return EASY16_INVALID_CHECKSUM_INDEX; } //////////////////////////////////////////////////////////////////////////////// -vector BackupEasy16::encode(const BinaryDataRef data, uint8_t index) +vector Easy16Codec::encode( + const BinaryDataRef data, BackupType bType) { + //TODO: use index pairs for a given backup type instead (one index per line) + uint8_t index = (uint8_t)bType; + if (index > EASY16_INDEX_MAX) { LOGERR << "index is too large"; throw runtime_error("index is too large"); } - auto encodeByte = [](stringstream& ss, uint8_t c)->void + auto encodeByte = [](char* ptr, uint8_t c)->void { uint8_t val1 = c >> 4; uint8_t val2 = c & 0x0F; - ss << e16chars_[val1] << e16chars_[val2]; + ptr[0] = e16chars_[val1]; + ptr[1] = e16chars_[val2]; }; auto encodeValue = [&encodeByte, &index]( - const BinaryDataRef& chunk16)->string + const BinaryDataRef& chunk16)->SecureBinaryData { //get hash auto h256 = getHash(chunk16, index); - + SecureBinaryData result(46); + //encode the chunk - stringstream ss; unsigned charCount = 0; + unsigned offset = 0; auto ptr = chunk16.getPtr(); for (unsigned i=0; i result; BinaryRefReader brr(data); + uint32_t count = (data.getSize() + EASY16_LINE_LENGTH - 1) / + EASY16_LINE_LENGTH; + vector result; + result.reserve(count); - uint32_t count = - (data.getSize() + EASY16_LINE_LENGTH - 1) / EASY16_LINE_LENGTH; for (unsigned i=0; i& lines) +BackupEasy16DecodeResult Easy16Codec::decode( + const vector& lines) { vector refVec; + refVec.reserve(lines.size()); for (const auto& line : lines) - refVec.emplace_back((const uint8_t*)line.c_str(), line.size()); + refVec.emplace_back(line.getRef()); return decode(refVec); } -//////////////////////////////////////////////////////////////////////////////// -BackupEasy16DecodeResult BackupEasy16::decode(const vector& lines) +//// +BackupEasy16DecodeResult Easy16Codec::decode(const vector& lines) { if (lines.size() == 0) throw runtime_error("empty easy16 code"); @@ -187,12 +207,9 @@ BackupEasy16DecodeResult BackupEasy16::decode(const vector& lines for (unsigned i=0; ibool + auto isSpace = [](const char* str)->bool { - if (str[0] == ' ') - return false; - - return true; + return (*str == ' '); }; auto decodeCharacters = [&easy16Vals]( @@ -206,7 +223,7 @@ BackupEasy16DecodeResult BackupEasy16::decode(const vector& lines auto iter2 = easy16Vals.find(str[1]); if (iter2 != easy16Vals.end()) - result += iter2->second; + result += iter2->second; }; /* @@ -218,8 +235,8 @@ BackupEasy16DecodeResult BackupEasy16::decode(const vector& lines . -2: invalid checksum data . -3: not enough room in the result buffer */ - auto decodeLine = [&checkSpace, &decodeCharacters]( - uint8_t* result, size_t& len, + auto decodeLine = [&isSpace, &decodeCharacters]( + uint8_t* result, size_t& len, const BinaryDataRef& line, BinaryData& checksum)->int { auto maxlen = len; @@ -230,12 +247,12 @@ BackupEasy16DecodeResult BackupEasy16::decode(const vector& lines for (; i= maxlen) return -3; - + decodeCharacters(result[len], ptr + i); //increment result length @@ -252,7 +269,7 @@ BackupEasy16DecodeResult BackupEasy16::decode(const vector& lines for (; i= EASY16_CHECKSUM_LEN) @@ -328,7 +345,7 @@ BackupEasy16DecodeResult BackupEasy16::decode(const vector& lines } //////////////////////////////////////////////////////////////////////////////// -bool BackupEasy16::repair(BackupEasy16DecodeResult& faultyBackup) +bool Easy16Codec::repair(BackupEasy16DecodeResult& faultyBackup) { //sanity check if (faultyBackup.data_.empty() || faultyBackup.checksums_.empty() || @@ -342,7 +359,7 @@ bool BackupEasy16::repair(BackupEasy16DecodeResult& faultyBackup) set validIndexes; for (auto index : faultyBackup.checksumIndexes_) { - auto indexIter = eligibleIndexes_.find(index); + auto indexIter = eligibleIndexes_.find((BackupType)index); if (indexIter == eligibleIndexes_.end()) { if (index == EASY16_INVALID_CHECKSUM_INDEX) @@ -403,10 +420,10 @@ bool BackupEasy16::repair(BackupEasy16DecodeResult& faultyBackup) //check all eligible indexes for (const auto& indexCandidate : eligibleIndexes_) { - auto hash = getHash(copied, indexCandidate); + auto hash = getHash(copied, (uint8_t)indexCandidate); if (hash.getSliceRef(0, 2) == checksum) { - auto& chkVal = result[indexCandidate]; + auto& chkVal = result[(uint8_t)indexCandidate]; auto& pos = chkVal[i]; pos.insert(y); } @@ -431,8 +448,8 @@ bool BackupEasy16::repair(BackupEasy16DecodeResult& faultyBackup) else if (validIndexes.size() == 1) { /* - Some lines are invalid but we have at least one that is valid. This - allows us to search for the expected checksum index in the invalid + Some lines are invalid but we have at least one that is valid. This + allows us to search for the expected checksum index in the invalid lines (they should all match) */ unsigned hint = *validIndexes.begin(); @@ -477,7 +494,7 @@ bool BackupEasy16::repair(BackupEasy16DecodeResult& faultyBackup) else { /* - All lines are invalid. There is no indication of what the checksum index + All lines are invalid. There is no indication of what the checksum index ought to be. We have to search all lines for a matching index. */ vector>>> resultMap; @@ -563,7 +580,46 @@ bool BackupEasy16::repair(BackupEasy16DecodeResult& faultyBackup) } return true; -} +} + +//////////////////////////////////////////////////////////////////////////////// +//// +//// BackupEasy16DecodeResult +//// +//////////////////////////////////////////////////////////////////////////////// +bool BackupEasy16DecodeResult::isInitialized() const +{ + return checksumIndexes_.size() == 2; +} + +//// +int BackupEasy16DecodeResult::getIndex() const +{ + if (!isInitialized()) + return -1; + + if (repairedIndexes_.size() == 2) + { + if (repairedIndexes_[0] == repairedIndexes_[1]) + return repairedIndexes_[0]; + } + else + { + if (checksumIndexes_[0] == checksumIndexes_[1]) + return checksumIndexes_[0]; + } + + return -1; +} + +bool BackupEasy16DecodeResult::isValid() const +{ + if (!isInitialized()) + return false; + + auto iter = Easy16Codec::eligibleIndexes_.find((BackupType)getIndex()); + return (iter != Easy16Codec::eligibleIndexes_.end()); +} //////////////////////////////////////////////////////////////////////////////// //// @@ -584,7 +640,7 @@ SecurePrint::SecurePrint() //////////////////////////////////////////////////////////////////////////////// pair SecurePrint::encrypt( - const SecureBinaryData& root, const SecureBinaryData& chaincode) + BinaryDataRef root, BinaryDataRef chaincode) { /* 1. generate passphrase from root and chaincode @@ -617,7 +673,8 @@ pair SecurePrint::encrypt( Concatenate root and chaincode then hmac */ - SecureBinaryData rootCopy = root; + SecureBinaryData rootCopy(64); + rootCopy.append(root); rootCopy.append(chaincode); auto rootHash = BtcUtils::getHash256(rootCopy); @@ -679,7 +736,7 @@ pair SecurePrint::encrypt( if (!chaincode.empty()) { - if (!encrypt(chaincode, result.second)) + if (!encrypt(chaincode, result.second)) { LOGERR << "SecurePrint encryption failure"; throw runtime_error("SecurePrint encryption failure"); @@ -694,6 +751,7 @@ SecureBinaryData SecurePrint::decrypt( const SecureBinaryData& ciphertext, const BinaryDataRef passphrase) const { //check passphrase checksum + //TODO: try with std::string_view instead string passStr(passphrase.toCharPtr(), passphrase.getSize()); BinaryData passBin; try @@ -770,420 +828,651 @@ SecureBinaryData SecurePrint::decrypt( //// Helpers //// //////////////////////////////////////////////////////////////////////////////// -WalletRootData Helpers::getRootData( - shared_ptr wltSingle) -{ - WalletRootData rootData; - rootData.wltId_ = wltSingle->getID(); - auto root = dynamic_pointer_cast( - wltSingle->getRoot()); - //lock wallet - auto lock = wltSingle->lockDecryptedContainer(); +/////////////////////////////// -- backup strings -- /////////////////////////// +unique_ptr Helpers::getWalletBackup( + shared_ptr wltPtr, BackupType bType) +{ + std::unique_ptr clearTextSeed; - //check root - auto rootBip32 = dynamic_pointer_cast(root); - if (rootBip32 == nullptr) + //grab encrypted seed from wallet + auto lock = wltPtr->lockDecryptedContainer(); + auto wltSeed = wltPtr->getEncryptedSeed(); + if (wltSeed != nullptr) { - /* - This isn't a bip32 root, therefor it's an Armory root. It may carry a - dedicated chaincode, let's check for that. - */ - + const auto& rawClearTextSeed = wltPtr->getDecryptedValue(wltSeed); + clearTextSeed = ClearTextSeed::deserialize(rawClearTextSeed); + } + else + { + //wallet has no seed, maybe it's a legacy Armory wallet, where + //the seed and root are the same + auto root = wltPtr->getRoot(); auto root135 = dynamic_pointer_cast(root); if (root135 == nullptr) + return {}; + + const auto& rootPrivKey = wltPtr->getDecryptedPrivateKeyForAsset( + root135); + clearTextSeed = unique_ptr(new ClearTextSeed_Armory135( + rootPrivKey, root135->getChaincode())); + } + + if (clearTextSeed == nullptr) + throw runtime_error("[getWalletBackup] could not get seed from wallet"); + + //pick default backup type for seed if not set explicitly + if (bType == BackupType::Invalid) + bType = clearTextSeed->getPreferedBackupType(); + + auto backup = getWalletBackup(move(clearTextSeed), bType); + backup->wltId_ = wltPtr->getID(); + return backup; +} + +//// +unique_ptr Helpers::getWalletBackup( + unique_ptr seed, BackupType bType) +{ + //sanity check + if (!seed->isBackupTypeEligible(bType)) + throw runtime_error("[getWalletBackup] ineligible backup type"); + + switch (bType) + { + case BackupType::Armory135: + case BackupType::Armory200a: + case BackupType::Armory200b: + case BackupType::Armory200c: + case BackupType::Armory200d: + return getEasy16BackupString(move(seed)); + + case BackupType::Base58: + return getBase58BackupString(move(seed)); + + case BackupType::BIP39: + return getBIP39BackupString(move(seed)); + + default: + throw runtime_error("[getWalletBackup] invalid backup type"); + } +} + +//////// +unique_ptr Helpers::getEasy16BackupString( + unique_ptr seed) +{ + BinaryDataRef primaryData; + BinaryDataRef secondaryData; + BackupType mode = BackupType::Invalid; + + switch (seed->type()) + { + case SeedType::Armory135: { - LOGERR << "unexpected wallet root type"; - throw runtime_error("unexpected wallet root type"); + auto seed135 = dynamic_cast(seed.get()); + primaryData = seed135->getRoot().getRef(); + secondaryData = seed135->getChaincode().getRef(); + mode = seed->getPreferedBackupType(); + break; } - rootData.root_ = wltSingle->getDecryptedPrivateKeyForAsset(root); - rootData.type_ = BackupType::Armory135; - - const auto& wltChaincode = root135->getChaincode(); - if (!wltChaincode.empty()) + case SeedType::BIP32_Structured: + case SeedType::BIP32_Virgin: + case SeedType::BIP39: { - /* - If the root carries a chaincode, it may be non deterministic. Let's - check. - */ + auto seedBip32 = dynamic_cast(seed.get()); + primaryData = seedBip32->getRawEntropy().getRef(); + mode = seed->getPreferedBackupType(); - auto computedChaincode = - BtcUtils::computeChainCode_Armory135(rootData.root_); - - if (computedChaincode != wltChaincode) - rootData.secondaryData_ = wltChaincode; + switch (seed->type()) + { + case SeedType::BIP39: + //force Armory200d for BIP39 seeds + mode = BackupType::Armory200d; + break; + + default: + mode = seed->getPreferedBackupType(); + } + break; } + + default: + throw runtime_error("[getEasy16BackupString] invalid seed type"); } - else + + //apply secureprint to seed data + SecurePrint sp; + auto encrRoot = sp.encrypt(primaryData, secondaryData); + + //set cleartext and encrypted root + auto lines_clear = Easy16Codec::encode(primaryData, mode); + auto lines_encr = Easy16Codec::encode(encrRoot.first, mode); + + auto result = make_unique(mode); + result->rootClear_ = move(lines_clear); + result->rootEncr_ = move(lines_encr); + if (mode == BackupType::Armory135) { - //bip32 wallet, grab the seed instead - auto seedPtr = wltSingle->getEncryptedSeed(); - if (seedPtr == nullptr) + //if there's a chaincode, set it too + if (!secondaryData.empty()) { - /* - For now, abort if bip32 wallet is missing its seed. May implement - root backups for bip32 wallets (privkey + chaincode) in the future. - */ - rootData.type_ = BackupType::BIP32_Root; - return rootData; + result->chaincodeClear_ = move(Easy16Codec::encode(secondaryData, mode)); + result->chaincodeEncr_ = move(Easy16Codec::encode(encrRoot.second, mode)); } + } + result->spPass_ = move(sp.getPassphrase()); + return result; +} + +//////// +unique_ptr Helpers::getBIP39BackupString( + unique_ptr seed) +{ + //sanity check + if (seed->type() != SeedType::BIP39) + throw runtime_error("[getBIP39BackupString] invalid seed type"); - rootData.type_ = BackupType::BIP32_Seed_Structured; + auto seedBip39 = dynamic_cast(seed.get()); + SecureBinaryData mnemonicString; + switch (seedBip39->getDictionnaryId()) + { + case 1: + { + //convert raw entropy to mnemonic string + break; + } - //decrypt the seed - rootData.root_ = wltSingle->getDecryptedValue(seedPtr); + default: + throw runtime_error("[getBIP39BackupString] invalid dictionnary id"); } - return rootData; + auto result = make_unique(move(mnemonicString)); + return result; } -//////////////////////////////////////////////////////////////////////////////// -WalletRootData Helpers::getRootData_Multisig( - shared_ptr) +//////// +unique_ptr Helpers::getBase58BackupString( + unique_ptr seed) { - throw runtime_error("TODO: needs implementation"); -} + auto seedBip32 = dynamic_cast(seed.get()); + if (seedBip32 == nullptr) + throw runtime_error("[getBase58BackupString] invalid seed object"); -//////////////////////////////////////////////////////////////////////////////// -WalletBackup Helpers::getWalletBackup( - std::shared_ptr wltPtr, BackupType type) -{ - auto rootData = getRootData(wltPtr); - return getWalletBackup(rootData, type); + if (seedBip32->type() != SeedType::BIP32_base58Root) + throw runtime_error("[getBase58BackupString] invalid seed type"); + + auto node = seedBip32->getRootNode(); + auto result = make_unique(move(node->getBase58())); + return result; } -//////////////////////////////////////////////////////////////////////////////// -WalletBackup Helpers::getWalletBackup(WalletRootData& rootData, - BackupType forceBackupType) +////////////////////////////// -- restore methods -- /////////////////////////// +shared_ptr Helpers::restoreFromBackup( + unique_ptr backup, const std::string& homedir, + const UserPrompt& callback) { - //apply secureprint - SecurePrint sp; - auto encrRoot = sp.encrypt(rootData.root_, rootData.secondaryData_); - - WalletBackup backup; + unique_ptr seed = nullptr; + auto bType = backup->type(); + switch (bType) + { + //easy16 backups + case BackupType::Armory135: + case BackupType::Armory200a: + case BackupType::Armory200b: + case BackupType::Armory200d: + case BackupType::Easy16_Unkonwn: + seed = restoreFromEasy16(move(backup), callback, bType); + break; - if (forceBackupType != BackupType::Invalid) - rootData.type_ = forceBackupType; + case BackupType::Base58: + seed = restoreFromBase58(move(backup)); + break; - unsigned mode = UINT32_MAX; - switch (rootData.type_) - { - case BackupType::Armory135: - case BackupType::BIP32_Seed_Structured: - case BackupType::BIP32_Root: - case BackupType::BIP32_Seed_Virgin: - { - mode = unsigned(rootData.type_); - break; - } + case BackupType::BIP39: + seed = restoreFromBIP39(move(backup), callback); + break; - default: - break; + default: + break; } - if (mode == UINT32_MAX) + if (seed == nullptr) { - LOGERR << "cannot create backup for unknown wallet type"; - throw runtime_error("cannot create backup for unknown wallet type"); + BridgeProto::RestorePrompt prompt; + prompt.mutable_type_error()->set_error( + "failed to create seed from backup"); + callback(move(prompt)); + return nullptr; } - //cleartext root easy16 - backup.rootClear_ = move(BackupEasy16::encode(rootData.root_, mode)); + //prompt user to verify id + { + BridgeProto::RestorePrompt prompt; + auto checkWltIdMsg = prompt.mutable_check_wallet_id(); + checkWltIdMsg->set_wallet_id(seed->getWalletId()); + checkWltIdMsg->set_backup_type((int)bType); - //encrypted root easy16 - backup.rootEncr_ = move(BackupEasy16::encode(encrRoot.first,mode)); + auto reply = callback(move(prompt)); + if (!reply.success()) + throw RestoreUserException("user rejected id"); + } - if (!rootData.secondaryData_.empty()) + //prompt for passwords + SecureBinaryData privkey, control; { - //cleartext chaincode easy16 - backup.chaincodeClear_ = move(BackupEasy16::encode( - rootData.secondaryData_, mode)); + BridgeProto::RestorePrompt prompt; + prompt.set_get_passphrases(true); + auto reply = callback(move(prompt)); - //encrypted chaincode easy16 - backup.chaincodeEncr_ = move(BackupEasy16::encode( - encrRoot.second, mode)); - } + if (!reply.success()) + throw RestoreUserException("user did not provide a passphrase"); - backup.spPass_ = sp.getPassphrase(); - backup.wltId_ = rootData.wltId_; + privkey = SecureBinaryData::fromString(reply.passphrases().privkey()); + control = SecureBinaryData::fromString(reply.passphrases().control()); + } - return backup; + //return wallet + return AssetWallet_Single::createFromSeed( + std::move(seed), privkey, control, homedir); } -//////////////////////////////////////////////////////////////////////////////// -shared_ptr Helpers::restoreFromBackup( - const vector& data, const BinaryDataRef passphrase, - const string& homedir, const UserPrompt& callerPrompt) +//////// +unique_ptr Helpers::restoreFromEasy16( + unique_ptr backup, const UserPrompt& callback, + BackupType& bType) { - vector bdrVec; - for (const auto& str : data) - bdrVec.emplace_back((const uint8_t*)str.c_str(), str.size()); + auto backupE16 = dynamic_cast(backup.get()); + if (backupE16 == nullptr) + return nullptr; + bool isEncrypted = !backupE16->getSpPass().empty(); - return restoreFromBackup(bdrVec, passphrase, homedir, callerPrompt); -} + /* decode data */ -//////////////////////////////////////////////////////////////////////////////// -shared_ptr Helpers::restoreFromBackup( - const vector& data, const BinaryDataRef passphrase, - const string& homedir, const UserPrompt& callerPrompt) -{ - SecureBinaryData promptDummy; - bool hasSecondaryData = false; + //root + vector first2Lines; + first2Lines.reserve(2); - //decode the data - BackupEasy16DecodeResult primaryData, secondaryData; - if (data.size() == 2) - { - primaryData = BackupEasy16::decode(data); - } - else if (data.size() > 2) - { - vector primarySlice; - primarySlice.insert(primarySlice.end(), data.begin(), data.begin() + 2); - primaryData = BackupEasy16::decode(primarySlice); + auto firstLine = backupE16->getRoot( + Backup_Easy16::LineIndex::One, isEncrypted); + first2Lines.emplace_back(BinaryDataRef( + (uint8_t*)firstLine.data(), firstLine.size())); - vector secondarySlice; - secondarySlice.insert(secondarySlice.end(), data.begin() + 2, data.end()); - secondaryData = BackupEasy16::decode(secondarySlice); + auto secondLine = backupE16->getRoot( + Backup_Easy16::LineIndex::Two, isEncrypted); + first2Lines.emplace_back(BinaryDataRef( + (uint8_t*)secondLine.data(), secondLine.size())); - hasSecondaryData = true; - } - else - { - callerPrompt(RestorePromptType::FormatError, {}, promptDummy); + auto primaryData = Easy16Codec::decode(first2Lines); + if (!primaryData.isInitialized()) return nullptr; - } - - if (primaryData.checksumIndexes_.empty() || - (hasSecondaryData && secondaryData.checksumIndexes_.empty())) - { - callerPrompt(RestorePromptType::Failure, {}, promptDummy); - return nullptr; - } - //sanity check - auto checksumIndexes = primaryData.checksumIndexes_; - if (hasSecondaryData) + //chaincode + BackupEasy16DecodeResult secondaryData; + if (backupE16->hasChaincode()) { - checksumIndexes.insert(checksumIndexes.end(), - secondaryData.checksumIndexes_.begin(), - secondaryData.checksumIndexes_.end()); + vector next2Lines; + auto thirdLine = backupE16->getChaincode( + Backup_Easy16::LineIndex::One, isEncrypted); + next2Lines.emplace_back(BinaryDataRef( + (uint8_t*)thirdLine.data(), thirdLine.size())); + + auto fourthLine = backupE16->getChaincode( + Backup_Easy16::LineIndex::Two, isEncrypted); + next2Lines.emplace_back(BinaryDataRef( + (uint8_t*)fourthLine.data(), fourthLine.size())); + + secondaryData = Easy16Codec::decode(next2Lines); + if (!secondaryData.isInitialized()) + return nullptr; } - bool checksumErrors; - int firstIndex; + /* checksums & repair */ - auto processChecksumIndexes = [&checksumErrors, &firstIndex]( - const vector& checksumValues) + //root + if (!primaryData.isValid()) { - /* - Set the common checksum result value and make sure all lines - carry the same value. - */ + if (!Easy16Codec::repair(primaryData)) + { + BridgeProto::RestorePrompt prompt; + auto checksumError = prompt.mutable_checksum_error(); + checksumError->add_index(primaryData.checksumIndexes_[0]); + checksumError->add_index(primaryData.checksumIndexes_[1]); + callback(move(prompt)); + return nullptr; + } - checksumErrors = false; - firstIndex = checksumValues[0]; - for (const auto& result : checksumValues) + if (!primaryData.isValid()) { - if (result < 0 || result != firstIndex) - { - checksumErrors = true; - break; - } + BridgeProto::RestorePrompt prompt; + auto checksumError = prompt.mutable_checksum_error(); + checksumError->add_index(primaryData.repairedIndexes_[0]); + checksumError->add_index(primaryData.repairedIndexes_[1]); + callback(move(prompt)); + return nullptr; } - }; - processChecksumIndexes(checksumIndexes); + } - if (checksumErrors) + //chaincode + if (secondaryData.isInitialized()) { - auto reportError = [&callerPrompt, &checksumIndexes](void) + if (!Easy16Codec::repair(secondaryData)) { - //prompt caller if we can't repair the error and throw - SecureBinaryData dummy; - callerPrompt( - RestorePromptType::ChecksumError, checksumIndexes, dummy); - throw RestoreUserException("checksum error"); - }; - - vector repairedIndexes; + BridgeProto::RestorePrompt prompt; + auto checksumError = prompt.mutable_checksum_error(); + checksumError->add_index(secondaryData.checksumIndexes_[0]); + checksumError->add_index(secondaryData.checksumIndexes_[1]); + callback(move(prompt)); + return nullptr; + } - auto repairData = [&reportError, &repairedIndexes]( - BackupEasy16DecodeResult& data) + if (!secondaryData.isValid()) { - //attempt to repair the data - auto result = BackupEasy16::repair(data); - if (!result) - reportError(); - - if (data.repairedIndexes_.size() != - data.checksumIndexes_.size()) - reportError(); + BridgeProto::RestorePrompt prompt; + auto checksumError = prompt.mutable_checksum_error(); + checksumError->add_index(secondaryData.repairedIndexes_[0]); + checksumError->add_index(secondaryData.repairedIndexes_[1]); + callback(move(prompt)); + return nullptr; + } - repairedIndexes.insert(repairedIndexes.end(), - data.repairedIndexes_.begin(), - data.repairedIndexes_.end()); - }; + //check chaincode index matches root index + if (primaryData.getIndex() != secondaryData.getIndex()) + { + BridgeProto::RestorePrompt prompt; + auto checksumError = prompt.mutable_checksum_mismatch(); + checksumError->add_index(primaryData.getIndex()); + checksumError->add_index(secondaryData.getIndex()); + callback(move(prompt)); + return nullptr; + } + } - //found some checksum errors, attempt to auto repair - repairData(primaryData); - if (hasSecondaryData) - repairData(secondaryData); + /* SecurePrint */ + if (isEncrypted) + try + { + SecurePrint sp; + auto pass = backupE16->getSpPass(); + BinaryDataRef passRef((uint8_t*)pass.data(), pass.size()); + primaryData.data_ = move(sp.decrypt(primaryData.data_, passRef)); - //check the repaired checksum result values - processChecksumIndexes(repairedIndexes); + if (secondaryData.isInitialized()) + secondaryData.data_ = move(sp.decrypt(secondaryData.data_, passRef)); + } + catch (const exception&) + { + BridgeProto::RestorePrompt prompt; + prompt.set_decrypt_error(true); + callback(move(prompt)); + throw RestoreUserException("invalid SP pass"); + } - if (checksumErrors) - reportError(); + /* backup type */ + if (bType == BackupType::Easy16_Unkonwn) + { + bType = (BackupType)primaryData.getIndex(); + } + else + { + if ((BackupType)primaryData.getIndex() != bType) + { + //mismatch between easy16 index and backup expected type + BridgeProto::RestorePrompt prompt; + auto checksumError = prompt.mutable_checksum_mismatch(); + checksumError->add_index(primaryData.getIndex()); + checksumError->add_index((int)bType); + callback(move(prompt)); + return nullptr; + } } - //check for encryption - if (!passphrase.empty()) + /* create seed */ + unique_ptr seedPtr = nullptr; + switch (bType) { - try + case BackupType::Armory135: { - SecurePrint sp; - auto decryptedData = sp.decrypt(primaryData.data_, passphrase); - primaryData.data_ = move(decryptedData); + /*legacy armory wallet, legacy backup string*/ + seedPtr = std::move(std::make_unique( + primaryData.data_, secondaryData.data_, + ClearTextSeed_Armory135::LegacyType::Armory135)); + break; + } - if (hasSecondaryData) - { - auto decryptedData = sp.decrypt(secondaryData.data_, passphrase); - secondaryData.data_ = move(decryptedData); - } + case BackupType::Armory200a: + { + /*legacy armory wallet, indexed backup string*/ + seedPtr = std::move(std::make_unique( + primaryData.data_, secondaryData.data_, + ClearTextSeed_Armory135::LegacyType::Armory200)); + break; } - catch (const exception&) + + //bip32 wallets + case BackupType::Armory200b: + { + /*BIP32 wallet with BIP44/49/84 accounts*/ + seedPtr = std::move(std::make_unique( + primaryData.data_, SeedType::BIP32_Structured)); + break; + } + + case BackupType::Armory200c: { - //prompt caller on decrypt error and return - callerPrompt(RestorePromptType::DecryptError, {}, promptDummy); - throw RestoreUserException("invalid SP pass"); + //empty BIP32 wallet + seedPtr = std::move(std::make_unique( + primaryData.data_, SeedType::BIP32_Virgin)); + break; } + + case BackupType::Armory200d: + { + //empty BIP32 wallet + seedPtr = std::move(std::make_unique( + primaryData.data_, 1)); + break; + } + + default: + return nullptr; } + return seedPtr; +} + +//////// +unique_ptr Helpers::restoreFromBase58( + unique_ptr backup) +{ + auto backupB58 = dynamic_cast(backup.get()); + if (backupB58 == nullptr) + return nullptr; - auto computeWalletId = []( - const SecureBinaryData& root, const SecureBinaryData& chaincode) - ->string + unique_ptr seed; + try { + auto b58StrView = backupB58->getBase58String(); + BinaryData b58Ref(b58StrView.data(), b58StrView.size()); + return ClearTextSeed_BIP32::fromBase58(b58Ref); + } + catch (const std::exception&) { - auto chaincodeCopy = chaincode; - if (chaincodeCopy.empty()) - chaincodeCopy = BtcUtils::computeChainCode_Armory135(root); + return nullptr; + } +} - auto derScheme = - make_shared(chaincodeCopy); +//////// +unique_ptr Helpers::restoreFromBIP39( + unique_ptr backup, const UserPrompt& callback) +{ + auto backupBIP39 = dynamic_cast(backup.get()); + if (backupBIP39 == nullptr) + return nullptr; - auto pubkey = CryptoECDSA().ComputePublicKey(root); - auto asset_single = make_shared( - Armory::Wallets::AssetId::getRootAssetId(), pubkey, nullptr); + //TODO: convert words to raw entropy + SecureBinaryData rawEntropy; - return AssetWallet_Single::computeWalletID(derScheme, asset_single); - }; + //entropy to seed + return make_unique(rawEntropy, 1); +} - auto promptForPassphrase = [&callerPrompt]( - SecureBinaryData& passphrase, SecureBinaryData& control)->bool - { - //prompt for wallet passphrase - if (!callerPrompt(RestorePromptType::Passphrase, {}, passphrase)) - return false; +//////////////////////////////////////////////////////////////////////////////// +// +//// WalletBackup +// +//////////////////////////////////////////////////////////////////////////////// +WalletBackup::WalletBackup(BackupType bType) : + type_(bType) +{} - //prompt for control passphrase - if (!callerPrompt(RestorePromptType::Control, {}, control)) - return false; +WalletBackup::~WalletBackup() +{} - return true; - }; +const string& WalletBackup::getWalletId() const +{ + return wltId_; +} - //generate wallet - shared_ptr wallet; - switch (firstIndex) - { - case BackupType::Armory135: - { - /*legacy armory wallet*/ +const BackupType& WalletBackup::type() const +{ + return type_; +} - auto id = SecureBinaryData::fromString( - computeWalletId(primaryData.data_, secondaryData.data_)); - if (!callerPrompt(RestorePromptType::Id, checksumIndexes, id)) - throw RestoreUserException("user rejected id"); +///////////////////////////////// Backup_Easy16 //////////////////////////////// +Backup_Easy16::Backup_Easy16(BackupType bType) : + WalletBackup(bType) +{} + +Backup_Easy16::~Backup_Easy16() +{} - //prompt for passwords - SecureBinaryData pass, control; - if (!promptForPassphrase(pass, control)) - throw RestoreUserException("user did not provide passphrase"); +bool Backup_Easy16::hasChaincode() const +{ + if (type() != BackupType::Armory135 && type() != BackupType::Easy16_Unkonwn) + return false; - //create wallet - wallet = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir, - primaryData.data_, - secondaryData.data_, - pass, control, - WALLET_RESTORE_LOOKUP); + return !chaincodeClear_.empty() || !chaincodeEncr_.empty() ; +} - break; +//// +string_view Backup_Easy16::getRoot(LineIndex li, bool encrypted) const +{ + auto lineIndex = (int)li; + std::vector::const_iterator iter; + if (!encrypted) + { + iter = rootClear_.begin() + lineIndex; + if (iter == rootClear_.end()) + { + throw runtime_error("[Backup_Easy16::getRoot]" + " missing cleartext line"); + } + } + else + { + iter = rootEncr_.begin() + lineIndex; + if (iter == rootEncr_.end()) + { + throw runtime_error("[Backup_Easy16::getRoot]" + " missing encrypted line"); + } } - //bip32 wallets - case BackupType::BIP32_Seed_Structured: + return string_view(iter->toCharPtr(), iter->getSize()); +} + +string_view Backup_Easy16::getChaincode(LineIndex li, bool encrypted) const +{ + auto lineIndex = (int)li; + std::vector::const_iterator iter; + if (!encrypted) + { + iter = chaincodeClear_.begin() + lineIndex; + if (iter == chaincodeClear_.end()) + { + throw runtime_error("[Backup_Easy16::getChaincode]" + " missing cleartext line"); + } + } + else { - /*BIP32 wallet with BIP44/49/84 accounts*/ + iter = chaincodeEncr_.begin() + lineIndex; + if (iter == chaincodeEncr_.end()) + { + throw runtime_error("[Backup_Easy16::getChaincode]" + " missing encrypted line"); + } + } - //create root node from seed - BIP32_Node rootNode; - rootNode.initFromSeed(primaryData.data_); + return string_view(iter->toCharPtr(), iter->getSize()); +} - //compute id and present to caller - auto id = SecureBinaryData::fromString( - computeWalletId(rootNode.getPrivateKey(), rootNode.getChaincode())); - if (!callerPrompt(RestorePromptType::Id, checksumIndexes, id)) - throw RestoreUserException("user rejected id"); +string_view Backup_Easy16::getSpPass() const +{ + return string_view(spPass_.toCharPtr(), spPass_.getSize()); +} - //prompt for passwords - SecureBinaryData pass, control; - if (!promptForPassphrase(pass, control)) - throw RestoreUserException("user did not provide passphrase"); +//// +unique_ptr Backup_Easy16::fromLines( + const vector& lines, string_view spPass) +{ + if (lines.size() % 2 != 0) + throw runtime_error("[Backup_Easy16::fromLines] invalid line count"); - //create wallet - wallet = AssetWallet_Single::createFromSeed_BIP32( - homedir, - primaryData.data_, - pass, control, - WALLET_RESTORE_LOOKUP); + auto result = make_unique(BackupType::Easy16_Unkonwn); + unsigned i=0; - break; + if (spPass.empty()) + { + for (const auto& line : lines) + { + auto lineSBD = SecureBinaryData::fromStringView(line); + if (i<2) + result->rootClear_.emplace_back(move(lineSBD)); + else + result->chaincodeClear_.emplace_back(move(lineSBD)); + ++i; + } } - - case BIP32_Seed_Virgin: + else { - /*empty BIP32 wallet*/ - - //create root node from seed - BIP32_Node rootNode; - rootNode.initFromSeed(primaryData.data_); - - //compute id and present to caller - auto id = SecureBinaryData::fromString( - computeWalletId(rootNode.getPrivateKey(), rootNode.getChaincode())); - if (!callerPrompt(RestorePromptType::Id, checksumIndexes, id)) - throw RestoreUserException("user rejected id"); + for (const auto& line : lines) + { + auto lineSBD = SecureBinaryData::fromStringView(line); + if (i<2) + result->rootEncr_.emplace_back(move(lineSBD)); + else + result->chaincodeEncr_.emplace_back(move(lineSBD)); + ++i; + } + result->spPass_ = SecureBinaryData::fromStringView(spPass); + } + + return result; +} - //prompt for passwords - SecureBinaryData pass, control; - if (!promptForPassphrase(pass, control)) - throw RestoreUserException("user did not provide passphrase"); +///////////////////////////////// Backup_Base58 //////////////////////////////// +Backup_Base58::Backup_Base58(SecureBinaryData b58String) : + WalletBackup(BackupType::Base58), b58String_(move(b58String)) +{} - //create wallet - wallet = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir, - primaryData.data_, - pass, control); +Backup_Base58::~Backup_Base58() +{} - break; - } +string_view Backup_Base58::getBase58String() const +{ + return string_view(b58String_.toCharPtr(), b58String_.getSize()); +} - //case BackupType::BIP32_Root: +unique_ptr Backup_Base58::fromString(const string_view& strV) +{ + return make_unique(SecureBinaryData::fromStringView(strV)); +} - default: - callerPrompt(RestorePromptType::TypeError, {}, promptDummy); - } +///////////////////////////////// Backup_BIP39 ///////////////////////////////// +Backup_BIP39::Backup_BIP39(SecureBinaryData mnemonicString) : + WalletBackup(BackupType::BIP39), mnemonicString_(move(mnemonicString)) +{} - return wallet; -} \ No newline at end of file +Backup_BIP39::~Backup_BIP39() +{} \ No newline at end of file diff --git a/cppForSwig/Wallets/Seeds/Backups.h b/cppForSwig/Wallets/Seeds/Backups.h index 43a952138f..45587057bc 100644 --- a/cppForSwig/Wallets/Seeds/Backups.h +++ b/cppForSwig/Wallets/Seeds/Backups.h @@ -11,6 +11,7 @@ #include #include +#include #include "SecureBinaryData.h" #include "EncryptionUtils.h" @@ -18,11 +19,21 @@ #define EASY16_INVALID_CHECKSUM_INDEX UINT8_MAX +namespace BridgeProto +{ + class RestorePrompt; + class RestoreReply; +} + namespace Armory { namespace Seeds { - //// + //forward declarations + class ClearTextSeed; + class ClearTextSeed_BIP39; + + ////////////////////////////////////////////////////////////////////////// class RestoreUserException : public std::runtime_error { public: @@ -31,6 +42,7 @@ namespace Armory {} }; + //// class Easy16RepairError : public std::runtime_error { public: @@ -40,52 +52,38 @@ namespace Armory }; //// - enum BackupType + enum class BackupType : int { - /* - Armory135: - For wallets using the Armory specific derivation scheme. - */ - Armory135 = 0, + //easy16, seed (2 or 4 lines), hash index is always 0 + Armory135 = 0, /* - BIP32_Seed_Structured: - For wallets carrying BIP44/49/84 accounts. Restores to a bip32 wallet - with all these accounts. + easy16, seed (2 lines), hash index defines seed type: + - a: Armory legacy derivation, P2PKH + P2WPK + P2SH-2WPKH + addresses in a single address account + - b: BIP32 with BIP44/49/84 chains, as individual address accounts + - c: BIP32 with no accounts + - d: BIP39 seed with BIP44/49/84 chains, as individual + address accounts */ - BIP32_Seed_Structured = 1, + Armory200a = 3, + Armory200b = 4, + Armory200c = 5, + Armory200d = 6, - /* - BIP32_Root: - For bip32 wallets that do not carry their own seed. Support for this is - not implemented at the moment. This type of backup would have to carry - the root privkey and chaincode generated through the seed's hmac. + //state of an easy16 backup prior to decode + Easy16_Unkonwn = 10, - May implement support in the future. - */ - BIP32_Root = 2, + //bip32 mnemonic phrase (12~24 words), english dictionnary + BIP39 = 0xFFFF, - /* - BIP32_Seed_Virgin: - No info is provided about the wallet's structure, restores to an empt - bip32 wallet. - */ - BIP32_Seed_Virgin = 15, - - /* - Default marker value. - */ - Invalid = UINT32_MAX - }; + Base58 = 58, - //// - struct WalletRootData - { - SecureBinaryData root_; - SecureBinaryData secondaryData_; + //raw binary of the seed in hexits, no extra info provided + Raw = INT32_MAX - 1, - BackupType type_; - std::string wltId_; + //end marker + Invalid = INT32_MAX }; //// @@ -95,10 +93,14 @@ namespace Armory std::vector repairedIndexes_; std::vector checksums_; SecureBinaryData data_; + + bool isInitialized(void) const; + bool isValid(void) const; + int getIndex(void) const; }; //// - struct BackupEasy16 + struct Easy16Codec { public: /*** @@ -111,7 +113,7 @@ namespace Armory one another. ***/ - static const std::set eligibleIndexes_; + static const std::set eligibleIndexes_; private: static BinaryData getHash(const BinaryDataRef&, uint8_t); @@ -120,8 +122,8 @@ namespace Armory public: const static std::vector e16chars_; - static std::vector encode(const BinaryDataRef, uint8_t); - static BackupEasy16DecodeResult decode(const std::vector&); + static std::vector encode(const BinaryDataRef, BackupType); + static BackupEasy16DecodeResult decode(const std::vector&); static BackupEasy16DecodeResult decode(const std::vector&); static bool repair(BackupEasy16DecodeResult&); }; @@ -144,27 +146,96 @@ namespace Armory SecurePrint(void); std::pair encrypt( - const SecureBinaryData&, const SecureBinaryData&); + BinaryDataRef, BinaryDataRef); SecureBinaryData decrypt( const SecureBinaryData&, const BinaryDataRef) const; const SecureBinaryData& getPassphrase(void) const { return passphrase_; } }; + ////////////////////////////////////////////////////////////////////////// + class WalletBackup + { + friend struct Helpers; + + protected: + const BackupType type_; + std::string wltId_; + + public: + WalletBackup(BackupType); + virtual ~WalletBackup(void) = 0; + + const BackupType& type(void) const; + const std::string& getWalletId(void) const; + }; + //// - struct WalletBackup + class Backup_Easy16 : public WalletBackup { - std::vector rootClear_; - std::vector chaincodeClear_; + friend class Helpers; + + public: + enum class LineIndex : int + { + One = 0, + Two = 1 + }; + + private: + std::vector rootClear_; + std::vector chaincodeClear_; - std::vector rootEncr_; - std::vector chaincodeEncr_; + std::vector rootEncr_; + std::vector chaincodeEncr_; SecureBinaryData spPass_; - std::string wltId_; + + public: + Backup_Easy16(BackupType); + ~Backup_Easy16(void) override; + + bool hasChaincode(void) const; + std::string_view getRoot(LineIndex, bool) const; + std::string_view getChaincode(LineIndex, bool) const; + std::string_view getSpPass(void) const; + + static std::unique_ptr fromLines( + const std::vector&, + std::string_view spPass = {}); + }; + + //// + class Backup_Base58 : public WalletBackup + { + private: + SecureBinaryData b58String_; + + public: + Backup_Base58(SecureBinaryData); + ~Backup_Base58(void) override; + + std::string_view getBase58String(void) const; + static std::unique_ptr fromString( + const std::string_view&); }; //// + class Backup_BIP39 : public WalletBackup + { + private: + SecureBinaryData mnemonicString_; + + public: + Backup_BIP39(SecureBinaryData); + ~Backup_BIP39(void) override; + + std::string_view getMnemonicString(void) const; + static std::unique_ptr fromMnemonics( + const std::vector&); + }; + + //////// enum RestorePromptType { //invalid backup format @@ -194,34 +265,31 @@ namespace Armory //// struct Helpers { - using UserPrompt = std::function&, - SecureBinaryData&)>; - - //getting root data from wallets - static WalletRootData getRootData( - std::shared_ptr); - static WalletRootData getRootData_Multisig( - std::shared_ptr); + using UserPrompt = std::function; //backup methods - static WalletBackup getWalletBackup( - std::shared_ptr, - BackupType bType = BackupType::Invalid); - - static WalletBackup getWalletBackup( - WalletRootData&, + static std::unique_ptr getWalletBackup( + std::shared_ptr, BackupType bType = BackupType::Invalid); + static std::unique_ptr getWalletBackup( + std::unique_ptr, BackupType); + static std::unique_ptr getEasy16BackupString( + std::unique_ptr); + static std::unique_ptr getBIP39BackupString( + std::unique_ptr); + static std::unique_ptr getBase58BackupString( + std::unique_ptr); //restore methods static std::shared_ptr restoreFromBackup( - const std::vector&, const BinaryDataRef, - const std::string&, const UserPrompt&); - - static std::shared_ptr restoreFromBackup( - const std::vector&, const BinaryDataRef, - const std::string&, const UserPrompt&); + std::unique_ptr, const std::string&, const UserPrompt&); + static std::unique_ptr restoreFromEasy16( + std::unique_ptr, const UserPrompt&, BackupType&); + static std::unique_ptr restoreFromBase58( + std::unique_ptr); + static std::unique_ptr restoreFromBIP39( + std::unique_ptr, const UserPrompt&); }; }; //namespace Backups }; //namespace Armory diff --git a/cppForSwig/Wallets/Seeds/Seeds.cpp b/cppForSwig/Wallets/Seeds/Seeds.cpp index b1186a5919..98a0d6675d 100644 --- a/cppForSwig/Wallets/Seeds/Seeds.cpp +++ b/cppForSwig/Wallets/Seeds/Seeds.cpp @@ -6,16 +6,25 @@ // // //////////////////////////////////////////////////////////////////////////////// -#include "Seeds.h" #include "../AssetEncryption.h" +#include "../DecryptedDataContainer.h" +#include "../DerivationScheme.h" +#include "../Assets.h" #include "../WalletIdTypes.h" +#include "../BIP32_Node.h" +#include "Seeds.h" +#include "Backups.h" +#include "BtcUtils.h" using namespace std; using namespace Armory; -using namespace Armory::Seed; +using namespace Armory::Seeds; using namespace Armory::Wallets; +using namespace Armory::Assets; -#define ENCRYPTED_SEED_VERSION 0x00000001 +#define ENCRYPTED_SEED_VERSION_1 0x00000001 +#define ENCRYPTED_SEED_VERSION_2 0x00000002 +#define WALLET_SEED_BYTE 0x84 //////////////////////////////////////////////////////////////////////////////// // @@ -24,12 +33,25 @@ using namespace Armory::Wallets; //////////////////////////////////////////////////////////////////////////////// const Wallets::AssetId EncryptedSeed::seedAssetId_(0x5EED, 0xDEE5, 0x5EED); -//////////////////////////////////////////////////////////////////////////////// +EncryptedSeed::EncryptedSeed(CipherText cipher, SeedType sType) : + Encryption::EncryptedAssetData(move(cipher)), type_(sType) +{} + +EncryptedSeed::~EncryptedSeed() +{} + +SeedType EncryptedSeed::type() const +{ + return type_; +} + +//////////////////////////////-- overrides --/////////////////////////////////// BinaryData EncryptedSeed::serialize() const { BinaryWriter bw; - bw.put_uint32_t(ENCRYPTED_SEED_VERSION); + bw.put_uint32_t(ENCRYPTED_SEED_VERSION_2); bw.put_uint8_t(WALLET_SEED_BYTE); + bw.put_int32_t((int32_t)type()); auto&& cipherData = getCipherDataPtr()->serialize(); bw.put_var_int(cipherData.getSize()); @@ -41,7 +63,7 @@ BinaryData EncryptedSeed::serialize() const return finalBw.getData(); } -//////////////////////////////////////////////////////////////////////////////// +//////// bool EncryptedSeed::isSame(Encryption::EncryptedAssetData* const seed) const { auto asset_ed = dynamic_cast(seed); @@ -51,13 +73,13 @@ bool EncryptedSeed::isSame(Encryption::EncryptedAssetData* const seed) const return Encryption::EncryptedAssetData::isSame(seed); } -//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////-- statics --///////////////////////////////////// const AssetId& EncryptedSeed::getAssetId() const { return seedAssetId_; } -//////////////////////////////////////////////////////////////////////////////// +//// unique_ptr EncryptedSeed::deserialize( const BinaryDataRef& data) { @@ -80,32 +102,598 @@ unique_ptr EncryptedSeed::deserialize( { case 0x00000001: { + //cipher data auto len = brr.get_var_int(); if (len > brr.getSizeRemaining()) - throw runtime_error("invalid serialized encrypted data len"); + throw runtime_error("[EncryptedSeed::deserialize]" + " invalid serialized encrypted data len"); auto cipherBdr = brr.get_BinaryDataRef(len); BinaryRefReader cipherBrr(cipherBdr); auto cipherData = Encryption::CipherData::deserialize(cipherBrr); - //ptr - assetPtr = make_unique(move(cipherData)); + //seed object + assetPtr = make_unique(move(cipherData), SeedType::Raw); + break; + } + + case 0x00000002: + { + //seed type + auto sType = (SeedType)brr.get_int32_t(); + + //cipher data + auto len = brr.get_var_int(); + if (len > brr.getSizeRemaining()) + throw runtime_error("[EncryptedSeed::deserialize]" + " invalid serialized encrypted data len"); + + auto cipherBdr = brr.get_BinaryDataRef(len); + BinaryRefReader cipherBrr(cipherBdr); + auto cipherData = Encryption::CipherData::deserialize(cipherBrr); + assetPtr = make_unique(move(cipherData), sType); break; } default: - throw runtime_error("unsupported seed version"); + throw runtime_error("[EncryptedSeed::deserialize]" + " unsupported seed version"); } break; } default: - throw runtime_error("unexpected encrypted data prefix"); + throw runtime_error("[EncryptedSeed::deserialize]" + " unexpected encrypted data prefix"); } if (assetPtr == nullptr) - throw runtime_error("failed to deserialize encrypted asset"); + { + throw runtime_error("[EncryptedSeed::deserialize]" + " failed to deserialize encrypted asset"); + } return assetPtr; } + +//// +std::unique_ptr EncryptedSeed::fromClearTextSeed( + std::unique_ptr seed, + std::unique_ptr cipher, + std::shared_ptr decrCont) +{ + //sanity checks + if (seed == nullptr) + throw runtime_error("[fromClearTextSeed] null seed"); + + if (cipher == nullptr) + throw runtime_error("[fromClearTextSeed] null cipher"); + + if (decrCont == nullptr) + throw runtime_error("[fromClearTextSeed] null decrypted data container"); + + //copy the cipher to cycle the IV + auto cipherCopy = cipher->getCopy(); + + //serialized clear text seed + BinaryWriter bw; //TODO: need SBD based bw + seed->serialize(bw); + + //encrypt it + auto cipherText = decrCont->encryptData(cipherCopy.get(), bw.getData()); + auto cipherData = make_unique( + cipherText, move(cipherCopy)); + + //instantiate encrypted seed object + return make_unique(move(cipherData), seed->type()); +} + +//////////////////////////////////////////////////////////////////////////////// +// +//// ClearTextSeed +// +//////////////////////////////////////////////////////////////////////////////// +ClearTextSeed::ClearTextSeed(SeedType sType) : + type_(sType) +{} + +ClearTextSeed::~ClearTextSeed() +{} + +SeedType ClearTextSeed::type() const +{ + return type_; +} + +//// +const string& ClearTextSeed::getWalletId() const +{ + if (walletId_.empty()) + walletId_ = computeWalletId(); + return walletId_; +} + +const string& ClearTextSeed::getMasterId() const +{ + if (masterId_.empty()) + masterId_ = computeMasterId(); + return masterId_; +} + +unique_ptr ClearTextSeed::deserialize( + const SecureBinaryData& serializedData) +{ + BinaryRefReader brr(serializedData); + auto type = (SeedType)brr.get_uint8_t(); + + //sanity check + auto len = brr.get_var_int(); + if (len != brr.getSizeRemaining()) + { + throw runtime_error("[ClearTextSeed::deserialize]" + " size mismatch in serialized seed"); + } + + switch (type) + { + case SeedType::Armory135: + { + ClearTextSeed_Armory135::LegacyType lType = + ClearTextSeed_Armory135::LegacyType::Armory200; + BinaryDataRef root; + BinaryDataRef chaincode; + while (!brr.isEndOfStream()) + { + auto prefix = (Prefix)brr.get_uint8_t(); + switch (prefix) + { + case Prefix::LegacyType: + { + lType = + (ClearTextSeed_Armory135::LegacyType)brr.get_uint8_t(); + break; + } + + case Prefix::Root: + { + auto rootLen = brr.get_var_int(); + root = brr.get_BinaryDataRef(rootLen); + break; + } + + case Prefix::Chaincode: + { + auto chLen = brr.get_var_int(); + chaincode = brr.get_BinaryDataRef(chLen); + break; + } + + default: + throw runtime_error("[ClearTextSeed::deserialize]" + " invalid prefix for Armory135 seed"); + } + } + return make_unique(root, chaincode, lType); + } + + case SeedType::BIP32_Structured: + case SeedType::BIP32_Virgin: + { + BinaryDataRef rawEntropy; + while (!brr.isEndOfStream()) + { + auto prefix = (Prefix)brr.get_uint8_t(); + switch (prefix) + { + case Prefix::RawEntropy: + { + auto entLen = brr.get_var_int(); + rawEntropy = brr.get_BinaryDataRef(entLen); + break; + } + + default: + throw runtime_error("[ClearTextSeed::deserialize]" + " invalid prefix for BIP32 seed"); + } + } + return make_unique(rawEntropy, type); + } + + case SeedType::BIP32_base58Root: + { + BinaryDataRef b58root; + while (!brr.isEndOfStream()) + { + auto prefix = (Prefix)brr.get_uint8_t(); + switch (prefix) + { + case Prefix::Base58Root: + { + auto entLen = brr.get_var_int(); + b58root = brr.get_BinaryDataRef(entLen); + break; + } + + default: + throw runtime_error("[ClearTextSeed::deserialize]" + " invalid prefix for BIP32 seed"); + } + } + return ClearTextSeed_BIP32::fromBase58(b58root); + } + + case SeedType::BIP39: + { + BinaryDataRef rawEntropy; + unsigned int dictionnary = 0; + while (!brr.isEndOfStream()) + { + auto prefix = (Prefix)brr.get_uint8_t(); + switch (prefix) + { + case Prefix::RawEntropy: + { + auto entLen = brr.get_var_int(); + rawEntropy = brr.get_BinaryDataRef(entLen); + break; + } + + case Prefix::Dictionnary: + { + dictionnary = brr.get_uint32_t(); + break; + } + + default: + throw runtime_error("[ClearTextSeed::deserialize]" + " invalid prefix for BIP39 seed"); + } + } + return make_unique(rawEntropy, dictionnary); + } + + default: + throw runtime_error("[ClearTextSeed::deserialize]" + " unexpected seed type"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +ClearTextSeed_Armory135::ClearTextSeed_Armory135(LegacyType lType) : + ClearTextSeed_Armory135(CryptoPRNG::generateRandom(32), lType) +{} + +ClearTextSeed_Armory135::ClearTextSeed_Armory135( + const SecureBinaryData& root, LegacyType lType) : + ClearTextSeed(SeedType::Armory135), + root_(root), chaincode_({}), + legacyType_(lType) +{} + +ClearTextSeed_Armory135::ClearTextSeed_Armory135(const SecureBinaryData& root, + const SecureBinaryData& chaincode, LegacyType lType) : + ClearTextSeed(SeedType::Armory135), + root_(root), chaincode_(chaincode), + legacyType_(lType) +{} + +ClearTextSeed_Armory135::~ClearTextSeed_Armory135() +{} + +//// +const SecureBinaryData& ClearTextSeed_Armory135::getRoot() const +{ + return root_; +} + +const SecureBinaryData& ClearTextSeed_Armory135::getChaincode() const +{ + return chaincode_; +} + +//// +string ClearTextSeed_Armory135::computeWalletId() const +{ + auto chaincodeCopy = chaincode_; + if (chaincode_.empty()) + chaincodeCopy = BtcUtils::computeChainCode_Armory135(root_); + + auto pubkey = CryptoECDSA().ComputePublicKey(root_); + return Armory::Wallets::generateWalletId(pubkey, chaincodeCopy, type()); +} + +string ClearTextSeed_Armory135::computeMasterId() const +{ + //uncompressed pubkey + auto pubkey = CryptoECDSA().ComputePublicKey(root_); + return generateMasterId(pubkey, chaincode_); +} + +//// +void ClearTextSeed_Armory135::serialize(BinaryWriter& bw) const +{ + /* serialize the seed */ + BinaryWriter inner; + + //legacy type + inner.put_uint8_t((uint8_t)Prefix::LegacyType); + inner.put_uint8_t((uint8_t)legacyType_); + + //root + inner.put_uint8_t((uint8_t)Prefix::Root); + inner.put_var_int(root_.getSize()); + inner.put_BinaryData(root_); + + //chaincode + inner.put_uint8_t((uint8_t)Prefix::Chaincode); + inner.put_var_int(chaincode_.getSize()); + if (!chaincode_.empty()) + inner.put_BinaryData(chaincode_); + + /* append to writer */ + + //seed type + bw.put_uint8_t((uint8_t)type()); + + //packet size + bw.put_var_int(inner.getSize()); + + //packet + bw.put_BinaryData(inner.getData()); +} + +//// +bool ClearTextSeed_Armory135::isBackupTypeEligible(BackupType bType) const +{ + switch (legacyType_) + { + case LegacyType::Armory135: + return bType == BackupType::Armory135; + + case LegacyType::Armory200: + return bType == BackupType::Armory200a; + + default: + break; + } + return false; +} + +BackupType ClearTextSeed_Armory135::getPreferedBackupType() const +{ + switch (legacyType_) + { + case LegacyType::Armory135: + return BackupType::Armory135; + + case LegacyType::Armory200: + return BackupType::Armory200a; + + default: + break; + } + return BackupType::Invalid; +} + +//////////////////////////////////////////////////////////////////////////////// +ClearTextSeed_BIP32::ClearTextSeed_BIP32(SeedType sType) : + ClearTextSeed_BIP32(CryptoPRNG::generateRandom(32), sType) +{} + +ClearTextSeed_BIP32::ClearTextSeed_BIP32(const SecureBinaryData& raw, + SeedType sType) : + ClearTextSeed(sType), rawEntropy_(raw) +{ + switch (sType) + { + case SeedType::BIP32_Structured: + case SeedType::BIP32_Virgin: + case SeedType::BIP32_base58Root: + case SeedType::BIP39: + break; + + default: + throw runtime_error("invalid bip32 seed type"); + } +} + +ClearTextSeed_BIP32::~ClearTextSeed_BIP32() +{} + +unique_ptr ClearTextSeed_BIP32::fromBase58( + const BinaryDataRef& b58) +{ + auto result = make_unique(SeedType::BIP32_base58Root); + result->rootNode_ = make_shared(); + result->rootNode_->initFromBase58(b58); + return move(result); +} + +//// +string ClearTextSeed_BIP32::computeWalletId() const +{ + const auto& rootNode = getRootNode(); + return Armory::Wallets::generateWalletId(rootNode->getPublicKey(), + rootNode->getChaincode(), type()); +} + +string ClearTextSeed_BIP32::computeMasterId() const +{ + //uncompressed pubkey + const auto& rootNode = getRootNode(); + return generateMasterId(rootNode->getPublicKey(), rootNode->getChaincode()); +} + +//// +std::shared_ptr ClearTextSeed_BIP32::getRootNode() const +{ + if (rootNode_ == nullptr) + { + rootNode_ = make_shared(); + rootNode_->initFromSeed(rawEntropy_); + } + return rootNode_; +} + +const SecureBinaryData& ClearTextSeed_BIP32::getRawEntropy() const +{ + return rawEntropy_; +} + +//// +void ClearTextSeed_BIP32::serialize(BinaryWriter& bw) const +{ + /* serialize the seed */ + BinaryWriter inner; + + //root + switch (type()) + { + case SeedType::BIP32_Structured: + case SeedType::BIP32_Virgin: + { + inner.put_uint8_t((uint8_t)Prefix::RawEntropy); + inner.put_var_int(rawEntropy_.getSize()); + inner.put_BinaryData(rawEntropy_); + break; + } + + case SeedType::BIP32_base58Root: + { + inner.put_uint8_t((uint8_t)Prefix::Base58Root); + auto root = getRootNode()->getBase58(); + inner.put_var_int(root.getSize()); + inner.put_BinaryData(root); + break; + } + + default: + throw runtime_error("[ClearTextSeed_BIP32::serialize]" + " unexpected seed type"); + } + + /* append to writer */ + + //seed type + bw.put_uint8_t((uint8_t)type()); + + //packet size + bw.put_var_int(inner.getSize()); + + //packet + bw.put_BinaryData(inner.getData()); +} + +//// +bool ClearTextSeed_BIP32::isBackupTypeEligible(BackupType bType) const +{ + switch (type()) + { + case SeedType::BIP32_Structured: + return bType == BackupType::Armory200b; + + case SeedType::BIP32_Virgin: + return bType == BackupType::Armory200c; + + case SeedType::BIP32_base58Root: + return bType == BackupType::Base58; + + default: + break; + } + + return false; +} + +BackupType ClearTextSeed_BIP32::getPreferedBackupType() const +{ + switch (type()) + { + case SeedType::BIP32_Structured: + return BackupType::Armory200b; + + case SeedType::BIP32_Virgin: + return BackupType::Armory200c; + + case SeedType::BIP32_base58Root: + return BackupType::Base58; + + default: + break; + } + return BackupType::Invalid; +} + +//////////////////////////////////////////////////////////////////////////////// +ClearTextSeed_BIP39::ClearTextSeed_BIP39(const SecureBinaryData& raw, + unsigned int dictId) : + ClearTextSeed_BIP32(raw, SeedType::BIP39), dictionnaryId_(dictId) +{ + if (dictionnaryId_ == 0) + throw runtime_error("[ClearTextSeed_BIP39] invalid dictionnary id"); +} + +ClearTextSeed_BIP39::~ClearTextSeed_BIP39() +{} + +//// +std::shared_ptr ClearTextSeed_BIP39::getRootNode() const +{ + if (rootNode_ == nullptr) + { + //convert raw entropy to BIP39 mnemonic phrase based on dictionnary + + //convert mnemonic to seed + SecureBinaryData seed{}; + + rootNode_ = make_shared(); + rootNode_->initFromSeed(seed); + } + return rootNode_; +} + +unsigned int ClearTextSeed_BIP39::getDictionnaryId(void) const +{ + return dictionnaryId_; +} + +//// +void ClearTextSeed_BIP39::serialize(BinaryWriter& bw) const +{ + /* serialize the seed */ + BinaryWriter inner; + + //root + inner.put_uint8_t((uint8_t)Prefix::RawEntropy); + inner.put_var_int(rawEntropy_.getSize()); + inner.put_BinaryData(rawEntropy_); + + //dictionnary id + inner.put_uint32_t(dictionnaryId_); + + /* append to writer */ + + //seed type + bw.put_uint8_t((uint8_t)type()); + + //packet size + bw.put_var_int(inner.getSize()); + + //packet + bw.put_BinaryData(inner.getData()); +} + +//// +bool ClearTextSeed_BIP39::isBackupTypeEligible(BackupType bType) const +{ + //BIP39 seeds can be backed up to either the easy16 format or the + //mnemonic phrase, interchangeably + return bType == BackupType::Armory200d || bType == BackupType::BIP39; +} + +BackupType ClearTextSeed_BIP39::getPreferedBackupType() const +{ + return BackupType::BIP39; +} diff --git a/cppForSwig/Wallets/Seeds/Seeds.h b/cppForSwig/Wallets/Seeds/Seeds.h index ade6fa9c60..6158561907 100644 --- a/cppForSwig/Wallets/Seeds/Seeds.h +++ b/cppForSwig/Wallets/Seeds/Seeds.h @@ -9,32 +9,257 @@ #pragma once #include "../AssetEncryption.h" +class BIP32_Node; namespace Armory { - namespace Seed + namespace Wallets { + namespace Encryption + { + class DecryptedDataContainer; + } + } + + /*** Wallet creation diagram *** + + WalletBackup <---> ClearTextSeed <-------- + | | + | | + v | + AssetWallet --> EncryptedSeed + ***/ + + namespace Seeds + { + ////////////////////////////////////////////////////////////////////////// + enum class SeedType : int + { + /* + Armory135: + For wallets using the legacy Armory derivation scheme. + */ + Armory135 = 0, + + /* + BIP32_Structured: + For wallets carrying BIP44/49/84 accounts. Restores to a bip32 wallet + with all these accounts. + */ + BIP32_Structured = 1, + + /* + BIP32_Virgin: + No info is provided about the wallet's structure, restores to an empty + bip32 wallet. + */ + BIP32_Virgin = 15, + + /* + BIP32_base58Root + From a base58 of the wallet root. No info about the wallet structure. + Cannot be extract as easy16. Mostly used to import HW roots. + */ + BIP32_base58Root = 16, + + /* + BIP39: + BIP39 seed. Can be outputed as either Easy16 or BIP39 english + dictionnary mnemonic. Backup string is always converted into the + BIP39 mnemonic then passed through PBKDF2 to generate the seed. + Yield a wallet with BIP44, 49 and 84 accounts. + */ + BIP39 = 8, + + /* + Raw: + Raw entropy. Used for wallet public data encryption and v1 seeds + */ + Raw = INT32_MAX - 1, + }; + enum class BackupType; + + ////////////////////////////////////////////////////////////////////////// + class ClearTextSeed + { + private: + const SeedType type_; + mutable std::string walletId_; + mutable std::string masterId_; + + protected: + enum class Prefix : int + { + Root = 0x11, + Chaincode = 0x22, + PublicKey = 0x33, + RawEntropy = 0x44, + Dictionnary = 0x55, + LegacyType = 0x66, + Base58Root = 0x77 + }; + + virtual std::string computeWalletId(void) const = 0; + virtual std::string computeMasterId(void) const = 0; + + public: + ClearTextSeed(SeedType); + virtual ~ClearTextSeed(void) = 0; + + SeedType type(void) const; + virtual bool isBackupTypeEligible(BackupType) const = 0; + virtual BackupType getPreferedBackupType(void) const = 0; + + const std::string& getWalletId(void) const; + const std::string& getMasterId(void) const; + + virtual void serialize(BinaryWriter&) const = 0; + static std::unique_ptr deserialize( + const SecureBinaryData&); + }; + + //////// + class ClearTextSeed_Armory135 : public ClearTextSeed + { + public: + enum class LegacyType : int + { + /* + Legacy type defines what kinda of backup can be created from this + seed. By default, legacy wallets would be created with a Armory200a + backup type, which would set the hash index to 3. + + A wallet restored from an older backup would then yield backups that + differ from the old paper. To avoid this, we track which legacy type + this seed is from. + + - seed type of LegacyType::Armory135 will generate + BackupType::Armory135 backups + - seed type of LegacyType::Armory200 will generate + BackupType::Armory200a backups + */ + Armory135 = 12, + Armory200 = 34 + }; + + private: + const SecureBinaryData root_; + const SecureBinaryData chaincode_; + const LegacyType legacyType_; + + protected: + std::string computeWalletId(void) const override; + std::string computeMasterId(void) const override; + + public: + //will generate random root + ClearTextSeed_Armory135(LegacyType lType = LegacyType::Armory200); + + //root + ClearTextSeed_Armory135(const SecureBinaryData&, + LegacyType lType = LegacyType::Armory200); + + //root + chaincode + ClearTextSeed_Armory135(const SecureBinaryData&, const SecureBinaryData&, + LegacyType lType = LegacyType::Armory135); + + //overrides + ~ClearTextSeed_Armory135(void) override; + void serialize(BinaryWriter&) const override; + bool isBackupTypeEligible(BackupType) const override; + BackupType getPreferedBackupType(void) const override; + + //local + const SecureBinaryData& getRoot(void) const; + const SecureBinaryData& getChaincode(void) const; + }; + + //////// + class ClearTextSeed_BIP32 : public ClearTextSeed + { + protected: + const SecureBinaryData rawEntropy_; + mutable std::shared_ptr rootNode_; + + protected: + std::string computeWalletId(void) const override; + std::string computeMasterId(void) const override; + + public: + //seed + ClearTextSeed_BIP32(SeedType); + ClearTextSeed_BIP32(const SecureBinaryData&, SeedType); + BackupType getPreferedBackupType(void) const override; + static std::unique_ptr fromBase58( + const BinaryDataRef&); + + //overrides + ~ClearTextSeed_BIP32(void) override; + virtual void serialize(BinaryWriter&) const override; + virtual bool isBackupTypeEligible(BackupType) const override; + + //locals + virtual std::shared_ptr getRootNode(void) const; + const SecureBinaryData& getRawEntropy(void) const; + }; + + //////// + class ClearTextSeed_BIP39 : public ClearTextSeed_BIP32 + { + private: + const unsigned int dictionnaryId_ = 1; + + public: + ClearTextSeed_BIP39(const SecureBinaryData&, unsigned int); + ~ClearTextSeed_BIP39(void) override; + + void serialize(BinaryWriter&) const override; + bool isBackupTypeEligible(BackupType) const override; + BackupType getPreferedBackupType(void) const override; + + std::shared_ptr getRootNode(void) const override; + unsigned int getDictionnaryId(void) const; + }; + ////////////////////////////////////////////////////////////////////////// class EncryptedSeed : public Wallets::Encryption::EncryptedAssetData { + /* + Carries the encrypted ClearTextSeed used to generate the wallet. + This class cannot be used to yield wallet seeds on its own, its + main purpose is disk IO. + + Convert to ClearTextSeed for seed/backup manipulations. + To convert, feed the decrypted the cipher text + to ClearTextSeed::deserialize + */ + private: + const SeedType type_; + public: + using CipherText = std::unique_ptr; static const Wallets::AssetId seedAssetId_; public: //tors - EncryptedSeed( - std::unique_ptr cipher) : - Wallets::Encryption::EncryptedAssetData(move(cipher)) - {} + EncryptedSeed(CipherText, SeedType); + ~EncryptedSeed(void) override; - //overrides + //utils + SeedType type(void) const; bool isSame(Wallets::Encryption::EncryptedAssetData* const) const override; - BinaryData serialize(void) const override; const Wallets::AssetId& getAssetId(void) const override; - //static + //for disk IO + BinaryData serialize(void) const override; static std::unique_ptr deserialize( const BinaryDataRef&); + + //used at wallet creation + static std::unique_ptr fromClearTextSeed( + std::unique_ptr, + std::unique_ptr, + std::shared_ptr); }; } } //namespace Armory \ No newline at end of file diff --git a/cppForSwig/Wallets/WalletFileInterface.cpp b/cppForSwig/Wallets/WalletFileInterface.cpp index 19d79f1503..6a7ee1e273 100644 --- a/cppForSwig/Wallets/WalletFileInterface.cpp +++ b/cppForSwig/Wallets/WalletFileInterface.cpp @@ -14,7 +14,7 @@ #include "Seeds/Seeds.h" using namespace std; -using namespace Armory::Seed; +using namespace Armory::Seeds; using namespace Armory::Wallets::IO; using namespace Armory::Wallets::Encryption; @@ -486,7 +486,8 @@ shared_ptr WalletDBInterface::setupControlDB( auto cipherCopy = keyStruct.cipher_->getCopy(); auto cipherText = decryptedData->encryptData(cipherCopy.get(), seed); auto cipherData = make_unique(cipherText, move(cipherCopy)); - auto encrSeed = make_shared(move(cipherData)); + auto encrSeed = make_shared( + move(cipherData), SeedType::Raw); //write seed to disk auto&& tx = beginWriteTransaction(CONTROL_DB_NAME); diff --git a/cppForSwig/Wallets/WalletFileInterface.h b/cppForSwig/Wallets/WalletFileInterface.h index 41ce89fffc..fbce882e3a 100644 --- a/cppForSwig/Wallets/WalletFileInterface.h +++ b/cppForSwig/Wallets/WalletFileInterface.h @@ -31,7 +31,7 @@ class PRNG_Fortuna; namespace Armory { - namespace Seed + namespace Seeds { class EncryptedSeed; }; @@ -160,7 +160,7 @@ namespace Armory std::unique_ptr decryptedData_; std::unique_ptr controlLock_; - std::unique_ptr controlSeed_; + std::unique_ptr controlSeed_; unsigned encryptionVersion_ = UINT32_MAX; std::unique_ptr fortuna_; diff --git a/cppForSwig/Wallets/WalletIdTypes.cpp b/cppForSwig/Wallets/WalletIdTypes.cpp index 869fd7d9f7..3662074344 100644 --- a/cppForSwig/Wallets/WalletIdTypes.cpp +++ b/cppForSwig/Wallets/WalletIdTypes.cpp @@ -1,12 +1,15 @@ //////////////////////////////////////////////////////////////////////////////// // // -// Copyright (C) 2021-2021, goatpig // +// Copyright (C) 2021-2023, goatpig // // Distributed under the MIT license // // See LICENSE-MIT or https://opensource.org/licenses/MIT // // // //////////////////////////////////////////////////////////////////////////////// #include "WalletIdTypes.h" +#include "DerivationScheme.h" +#include "Assets.h" +#include "WalletHeader.h" using namespace Armory::Wallets; @@ -655,3 +658,63 @@ EncryptionKeyId EncryptionKeyId::deserializeValue(BinaryRefReader& brr) throw IdException("EncryptionKeyId::deserializeValue"); } } + +///////////////////////// - wallet & master id - /////////////////////////////// +std::string Armory::Wallets::generateWalletId( + std::shared_ptr derScheme, + std::shared_ptr rootEntry, + Armory::Seeds::SeedType sType) +{ + auto addrVec = derScheme->extendPublicChain(rootEntry, + 1, 1 + (int)sType, nullptr); + if (addrVec.size() != (int)sType+1) + throw WalletException("unexpected chain derivation output"); + + auto entry = std::dynamic_pointer_cast( + addrVec[int(sType)]); + if (entry == nullptr) + throw WalletException("unexpected asset entry type"); + + return BtcUtils::computeID(entry->getPubKey()->getUncompressedKey()); +} + +//// +std::string Armory::Wallets::generateWalletId( + SecureBinaryData pubkey, + SecureBinaryData chaincode, + Armory::Seeds::SeedType sType) +{ + //sanity checks + if (pubkey.empty()) + throw WalletException("[generateWalletId] empty pubkey"); + + if (chaincode.empty()) + throw WalletException("[generateWalletId] empty chaincode"); + + //create legacy armory derviation scheme from chaincode + auto derScheme = std::make_shared< + Armory::Assets::DerivationScheme_ArmoryLegacy>(chaincode); + + //create root pubkey asset + auto asset_single = std::make_shared< + Armory::Assets::AssetEntry_Single>( + Armory::Wallets::AssetId::getRootAssetId(), + pubkey, + nullptr); + + //derive '(int)sType' amount of addresses, use last one as id + return Armory::Wallets::generateWalletId(derScheme, asset_single, sType); +} + +//////// +std::string Armory::Wallets::generateMasterId(const SecureBinaryData& pubkey, + const SecureBinaryData& chaincode) +{ + BinaryWriter bw; + bw.put_BinaryData(pubkey); + bw.put_BinaryData(chaincode); + auto hmacMasterMsg = SecureBinaryData::fromString("MetaEntry"); + auto masterID_long = BtcUtils::getHMAC256( + bw.getData(), hmacMasterMsg); + return BtcUtils::computeID(masterID_long); +} diff --git a/cppForSwig/Wallets/WalletIdTypes.h b/cppForSwig/Wallets/WalletIdTypes.h index b1aeaad633..b6c61ebd49 100644 --- a/cppForSwig/Wallets/WalletIdTypes.h +++ b/cppForSwig/Wallets/WalletIdTypes.h @@ -19,7 +19,18 @@ namespace Armory namespace Bridge { class BridgePassphrasePrompt; - }; + } + + namespace Assets + { + class AssetEntry; + class DerivationScheme; + } + + namespace Seeds + { + enum class SeedType; + } namespace Wallets { @@ -183,6 +194,15 @@ namespace Armory BinaryData getSerializedKey(uint8_t) const; static EncryptionKeyId deserializeValue(BinaryRefReader&); }; - }; -}; + + //////////////////////////////////////////////////////////////////////// + std::string generateWalletId(std::shared_ptr, + std::shared_ptr, Seeds::SeedType); + std::string generateWalletId(SecureBinaryData, SecureBinaryData, + Seeds::SeedType); + std::string generateMasterId(const SecureBinaryData&, + const SecureBinaryData&); + + }// namespace Wallets +} #endif \ No newline at end of file diff --git a/cppForSwig/Wallets/Wallets.cpp b/cppForSwig/Wallets/Wallets.cpp index cbe5d56874..851b365923 100644 --- a/cppForSwig/Wallets/Wallets.cpp +++ b/cppForSwig/Wallets/Wallets.cpp @@ -17,7 +17,7 @@ using namespace Armory::Signer; using namespace Armory::Assets; using namespace Armory::Accounts; using namespace Armory::Wallets; -using namespace Armory::Seed; +using namespace Armory::Seeds; //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -1122,36 +1122,71 @@ const AddressAccountId& AssetWallet_Single::createBIP32Account( return accountPtr->getID(); } +/////////////////////////////-- wallet creation --////////////////////////////// +shared_ptr AssetWallet_Single::createFromSeed( + std::unique_ptr seed, + const SecureBinaryData& passphrase, + const SecureBinaryData& controlPassphrase, + const string& folder, unsigned lookup) +{ + //sanity check + if (seed == nullptr) + throw WalletException("[AssetWallet_Single::createFromSeed] null seed"); + + //determine wallet type from seed type + shared_ptr result; + switch (seed->type()) + { + case Seeds::SeedType::Armory135: + { + auto seedA135 = dynamic_cast(seed.get()); + result = createFromSeed( + folder, seedA135, + passphrase, controlPassphrase, + lookup); + break; + } + + case Seeds::SeedType::BIP32_Structured: + case Seeds::SeedType::BIP32_Virgin: + case Seeds::SeedType::BIP32_base58Root: + case Seeds::SeedType::BIP39: + { + auto seedBip32 = dynamic_cast(seed.get()); + result = createFromSeed( + folder, seedBip32, + passphrase, controlPassphrase, + lookup); + break; + } + + default: + throw WalletException("[AssetWallet_Single::createFromSeed]" + " unexpected seed type"); + } + + //set the seed + result->setSeed(std::move(seed), passphrase); + return result; +} + //////////////////////////////////////////////////////////////////////////////// -shared_ptr AssetWallet_Single:: - createFromPrivateRoot_Armory135( +shared_ptr AssetWallet_Single::createFromSeed( const string& folder, - const SecureBinaryData& privateRoot, - SecureBinaryData chaincode, + ClearTextSeed_Armory135* seed, const SecureBinaryData& passphrase, const SecureBinaryData& controlPassphrase, unsigned lookup) { - /* - Pass the chaincode as it may be non deterministic for older Armory wallets. - To generate the chaincode from the private root, leave it empty. - */ + if (seed == nullptr) + throw WalletException("[createFromSeed] null root"); + const auto& privateRoot = seed->getRoot(); if (privateRoot.getSize() != 32) - throw WalletException("invalid root size"); - auto&& pubkey = CryptoECDSA().ComputePublicKey(privateRoot); + throw WalletException("[createFromSeed] invalid root size"); - //compute wallet ID - BinaryWriter masterIdPreimage; - masterIdPreimage.put_BinaryData(pubkey); - if (!chaincode.empty()) - masterIdPreimage.put_BinaryData(chaincode); - - //compute master ID as hmac256(root pubkey + chaincode, "MetaEntry") - auto hmacMasterMsg = SecureBinaryData::fromString("MetaEntry"); - auto&& masterID_long = BtcUtils::getHMAC256( - masterIdPreimage.getData(), hmacMasterMsg); - auto&& masterID = BtcUtils::computeID(masterID_long); + //TODO: need ID/backup pair tests (wallet ID should use fragments + //from derivation scheme, backup type should to different walletID) /* Create control passphrase lambda. It gets wiped after the wallet is setup @@ -1163,31 +1198,22 @@ shared_ptr AssetWallet_Single:: }; //create wallet file and dbenv - stringstream pathSS; - pathSS << folder << "/armory_" << masterID << "_wallet.lmdb"; - auto iface = getIfaceFromFile(pathSS.str(), false, controlPassLbd); + const auto& masterId = seed->getMasterId(); + std::string path{folder + "/armory_" + masterId + "_wallet.lmdb"}; + auto iface = getIfaceFromFile(path, false, controlPassLbd); - string walletID; + auto chaincode = seed->getChaincode(); + if (chaincode.empty()) { - //generate chaincode if it's not provided - if (chaincode.empty()) - chaincode = BtcUtils::computeChainCode_Armory135(privateRoot); - - auto chaincodeCopy = chaincode; - auto derScheme = make_shared( - chaincodeCopy); - - auto asset_single = make_shared( - AssetId::getRootAssetId(), - pubkey, nullptr); - - walletID = move(computeWalletID(derScheme, asset_single)); + //seed has no chaincode, generate deterministic one + chaincode = BtcUtils::computeChainCode_Armory135(privateRoot); } //create empty wallet + const auto& walletId = seed->getWalletId(); auto walletPtr = initWalletDb( iface, - masterID, walletID, + masterId, walletId, passphrase, controlPassphrase, privateRoot, @@ -1195,7 +1221,7 @@ shared_ptr AssetWallet_Single:: 0); //pass 0 for the fingerprint to signal legacy wallet //set as main - setMainWallet(iface, walletID); + setMainWallet(iface, walletId); //create account auto account135 = make_shared(); @@ -1222,82 +1248,16 @@ shared_ptr AssetWallet_Single:: } //////////////////////////////////////////////////////////////////////////////// -shared_ptr - AssetWallet_Single::createFromPublicRoot_Armory135( - const string& folder, - SecureBinaryData& pubRoot, - SecureBinaryData& chainCode, - const SecureBinaryData& controlPassphrase, - unsigned lookup) -{ - //compute master ID as hmac256(root pubkey, "MetaEntry") - auto hmacMasterMsg = SecureBinaryData::fromString("MetaEntry"); - auto&& masterID_long = BtcUtils::getHMAC256(pubRoot, hmacMasterMsg); - auto&& masterID = BtcUtils::computeID(masterID_long); - - /* - Create control passphrase lambda. It gets wiped after the wallet is setup - */ - auto controlPassLbd = - [&controlPassphrase](const set&)->SecureBinaryData - { - return controlPassphrase; - }; - - //create wallet file and dbenv - stringstream pathSS; - pathSS << folder << "/armory_" << masterID << "_WatchingOnly.lmdb"; - auto iface = getIfaceFromFile(pathSS.str(), false, controlPassLbd); - - string walletID; - shared_ptr rootPtr; - { - //walletID - auto chainCode_copy = chainCode; - auto derScheme = make_shared( - chainCode_copy); - - rootPtr = make_shared( - AssetId::getRootAssetId(), - pubRoot, nullptr, chainCode); - - walletID = move(computeWalletID(derScheme, rootPtr)); - } - - //create wallet - auto walletPtr = initWalletDbWithPubRoot( - iface, - controlPassphrase, - masterID, walletID, - rootPtr); - - //set as main - setMainWallet(iface, walletID); - - //add account - auto account135 = make_shared(); - account135->setMain(true); - - auto accountPtr = walletPtr->createAccount(account135); - accountPtr->extendPublicChain(iface, lookup - 1); - - return walletPtr; -} - -//////////////////////////////////////////////////////////////////////////////// -shared_ptr AssetWallet_Single::createFromSeed_BIP32( +shared_ptr AssetWallet_Single::createFromSeed( const string& folder, - const SecureBinaryData& seed, + Seeds::ClearTextSeed_BIP32* seed, const SecureBinaryData& passphrase, const SecureBinaryData& controlPassphrase, unsigned lookup) { - if (seed.empty()) - throw WalletException("[createFromSeed_BIP32] empty seed"); - - BIP32_Node rootNode; - rootNode.initFromSeed(seed); - + if (seed == nullptr) + throw WalletException("[createFromSeed] null seed"); + auto rootNode = seed->getRootNode(); auto coinType = Armory::Config::BitcoinSettings::getCoinType(); //address accounts @@ -1307,7 +1267,7 @@ shared_ptr AssetWallet_Single::createFromSeed_BIP32( //legacy account: 44 vector path = { 0x8000002C, coinType, 0x80000000 }; auto legacyAcc = AccountType_BIP32::makeFromDerPaths( - rootNode.getThisFingerprint(), {path}); + rootNode->getThisFingerprint(), {path}); //nodes legacyAcc->setNodes({ @@ -1335,7 +1295,7 @@ shared_ptr AssetWallet_Single::createFromSeed_BIP32( //nested sw account: 49 vector path = { 0x80000031, coinType, 0x80000000 }; auto nestedAcc = AccountType_BIP32::makeFromDerPaths( - rootNode.getThisFingerprint(), {path}); + rootNode->getThisFingerprint(), {path}); //nodes nestedAcc->setNodes({ @@ -1362,7 +1322,7 @@ shared_ptr AssetWallet_Single::createFromSeed_BIP32( //sw account: 84 vector path = { 0x80000054, coinType, 0x80000000 }; auto segwitAcc = AccountType_BIP32::makeFromDerPaths( - rootNode.getThisFingerprint(), {path}); + rootNode->getThisFingerprint(), {path}); //nodes segwitAcc->setNodes({ @@ -1383,76 +1343,78 @@ shared_ptr AssetWallet_Single::createFromSeed_BIP32( accountTypes.insert(segwitAcc); } + //create wallet file and dbenv + if (rootNode->isPublic()) + { + throw WalletException("[createFromSeed]" + " BIP32 seeds cannot lead to WO wallets"); + } - auto walletPtr = createFromBIP32Node( - rootNode, - accountTypes, - passphrase, - controlPassphrase, - folder); - - //save the seed - walletPtr->setSeed(seed, passphrase); + auto controlPassLbd = + [&controlPassphrase](const set&)->SecureBinaryData + { + return controlPassphrase; + }; - return walletPtr; -} + //db env + auto masterId = seed->getMasterId(); + auto walletId = seed->getWalletId(); + string path = folder + "/armory_" + masterId + "_wallet.lmdb"; + auto iface = getIfaceFromFile(path, false, controlPassLbd); -//////////////////////////////////////////////////////////////////////////////// -shared_ptr AssetWallet_Single::createFromSeed_BIP32_Blank( - const string& folder, - const SecureBinaryData& seed, - const SecureBinaryData& passphrase, - const SecureBinaryData& controlPassphrase) -{ - BIP32_Node rootNode; - if (seed.getSize() == 0) - throw WalletException("empty seed"); - rootNode.initFromSeed(seed); + //wallet object + auto walletPtr = initWalletDb(iface, + masterId, walletId, + passphrase, controlPassphrase, + rootNode->getPrivateKey(), + rootNode->getChaincode(), + rootNode->getThisFingerprint()); - //address accounts - set> accountTypes; + //set as main + setMainWallet(iface, walletId); - /* - no accounts are setup for a blank wallet - */ + //add accounts + auto passLbd =[&passphrase]( + const set&)->SecureBinaryData + { + return passphrase; + }; + walletPtr->setPassphrasePromptLambda(passLbd); - auto walletPtr = createFromBIP32Node( - rootNode, - accountTypes, - passphrase, - controlPassphrase, - folder); + switch (seed->type()) + { + case SeedType::BIP32_Structured: + case SeedType::BIP39: + { + for (auto accountPtr : accountTypes) + walletPtr->createBIP32Account(accountPtr); + break; + } - //save the seed - walletPtr->setSeed(seed, passphrase); + default: + //no accounts structure for these seeds + break; + } + walletPtr->resetPassphrasePromptLambda(); return walletPtr; } //////////////////////////////////////////////////////////////////////////////// -shared_ptr AssetWallet_Single::createFromBIP32Node( - const BIP32_Node& node, - set> accountTypes, - const SecureBinaryData& passphrase, +shared_ptr + AssetWallet_Single::createFromPublicRoot_Armory135( + const string& folder, + SecureBinaryData& pubRoot, + SecureBinaryData& chainCode, const SecureBinaryData& controlPassphrase, - const string& folder) + unsigned lookup) { - bool isPublic = false; - if (node.isPublic()) - isPublic = true; - - //compute wallet ID - auto pubkey = node.getPublicKey(); - - //compute master ID as hmac256(root pubkey, "MetaEntry") - auto hmacMasterMsg = SecureBinaryData::fromString("MetaEntry"); - auto&& masterID_long = BtcUtils::getHMAC256(pubkey, hmacMasterMsg); - auto&& masterID = BtcUtils::computeID(masterID_long); + auto masterID = generateMasterId(pubRoot, chainCode); /* Create control passphrase lambda. It gets wiped after the wallet is setup */ - auto controlPassLbd = + auto controlPassLbd = [&controlPassphrase](const set&)->SecureBinaryData { return controlPassphrase; @@ -1460,61 +1422,30 @@ shared_ptr AssetWallet_Single::createFromBIP32Node( //create wallet file and dbenv stringstream pathSS; - if (!isPublic) - pathSS << folder << "/armory_" << masterID << "_wallet.lmdb"; - else - pathSS << folder << "/armory_" << masterID << "_WatchingOnly.lmdb"; - + pathSS << folder << "/armory_" << masterID << "_WatchingOnly.lmdb"; auto iface = getIfaceFromFile(pathSS.str(), false, controlPassLbd); - string walletID; - { - //walletID - auto chaincode_copy = node.getChaincode(); - auto derScheme = - make_shared(chaincode_copy); - - auto asset_single = make_shared( - AssetId::getRootAssetId(), - pubkey, nullptr); - - walletID = move(computeWalletID(derScheme, asset_single)); - } + auto walletID = generateWalletId(pubRoot, chainCode, SeedType::Armory135); + auto rootPtr = make_shared( + AssetId::getRootAssetId(), pubRoot, nullptr, chainCode); //create wallet - shared_ptr walletPtr = nullptr; - - if (!isPublic) - { - walletPtr = initWalletDb( - iface, - masterID, walletID, - passphrase, - controlPassphrase, - node.getPrivateKey(), - node.getChaincode(), - node.getThisFingerprint()); - } - else - { - throw runtime_error("invalid for bip32 wallets"); - } + auto walletPtr = initWalletDbWithPubRoot( + iface, + controlPassphrase, + masterID, walletID, + rootPtr); //set as main setMainWallet(iface, walletID); - //add accounts - auto passLbd = - [&passphrase](const set&)->SecureBinaryData - { - return passphrase; - }; - walletPtr->setPassphrasePromptLambda(passLbd); + //add account + auto account135 = make_shared(); + account135->setMain(true); - for (auto accountPtr : accountTypes) - walletPtr->createBIP32Account(accountPtr); + auto accountPtr = walletPtr->createAccount(account135); + accountPtr->extendPublicChain(iface, lookup - 1); - walletPtr->resetPassphrasePromptLambda(); return walletPtr; } @@ -1528,7 +1459,7 @@ shared_ptr AssetWallet_Single::createBlank( /* Create control passphrase lambda. It gets wiped after the wallet is setup */ - auto controlPassLbd = + auto controlPassLbd = [&controlPassphrase](const set&)->SecureBinaryData { return controlPassphrase; @@ -1555,22 +1486,6 @@ shared_ptr AssetWallet_Single::createBlank( return walletPtr; } -//////////////////////////////////////////////////////////////////////////////// -string AssetWallet_Single::computeWalletID( - shared_ptr derScheme, - shared_ptr rootEntry) -{ - auto&& addrVec = derScheme->extendPublicChain(rootEntry, 1, 1, nullptr); - if (addrVec.size() != 1) - throw WalletException("unexpected chain derivation output"); - - auto firstEntry = dynamic_pointer_cast(addrVec[0]); - if (firstEntry == nullptr) - throw WalletException("unexpected asset entry type"); - - return BtcUtils::computeID(firstEntry->getPubKey()->getUncompressedKey()); -} - //////////////////////////////////////////////////////////////////////////////// shared_ptr AssetWallet_Single::initWalletDb( shared_ptr iface, @@ -2135,19 +2050,15 @@ WalletPublicData AssetWallet_Single::exportPublicData( //////////////////////////////////////////////////////////////////////////////// -void AssetWallet_Single::setSeed( - const SecureBinaryData& seed, +void AssetWallet_Single::setSeed(unique_ptr seedPtr, const SecureBinaryData& passphrase) { //copy root node cipher - auto rootPtr = dynamic_pointer_cast(root_); - if (rootPtr == nullptr) - throw WalletException("expected BIP32 root object"); - auto cipherCopy = - rootPtr->getPrivKey()->getCipherDataPtr()->cipher_->getCopy(); + auto cipherCopy = + root_->getPrivKey()->getCipherDataPtr()->cipher_->getCopy(); //if custom passphrase, set prompt lambda prior to encryption - if (passphrase.getSize() > 0) + if (!passphrase.empty()) { auto passphraseLambda = [&passphrase](const set&)->SecureBinaryData @@ -2161,11 +2072,8 @@ void AssetWallet_Single::setSeed( //create encrypted seed object { auto lock = lockDecryptedContainer(); - - auto cipherText = decryptedData_->encryptData(cipherCopy.get(), seed); - auto cipherData = make_unique( - cipherText, move(cipherCopy)); - seed_ = make_shared(move(cipherData)); + seed_ = EncryptedSeed::fromClearTextSeed(std::move(seedPtr), + std::move(cipherCopy), decryptedData_); } //write to disk diff --git a/cppForSwig/Wallets/Wallets.h b/cppForSwig/Wallets/Wallets.h index e5c69ace05..fd119d8eed 100644 --- a/cppForSwig/Wallets/Wallets.h +++ b/cppForSwig/Wallets/Wallets.h @@ -40,9 +40,12 @@ namespace Armory class BIP32_AssetPath; } - namespace Seed + namespace Seeds { class EncryptedSeed; + class ClearTextSeed; + class ClearTextSeed_Armory135; + class ClearTextSeed_BIP32; } namespace Wallets @@ -256,7 +259,7 @@ namespace Armory protected: std::shared_ptr root_ = nullptr; - std::shared_ptr seed_ = nullptr; + std::shared_ptr seed_ = nullptr; protected: //virtual @@ -284,7 +287,23 @@ namespace Armory static void importPublicData(const WalletPublicData&, std::shared_ptr); - void setSeed(const SecureBinaryData&, const SecureBinaryData&); + void setSeed(std::unique_ptr, + const SecureBinaryData&); + + //wallet creation private statics + static std::shared_ptr createFromSeed( + const std::string&, //folder + Seeds::ClearTextSeed_Armory135*, + const SecureBinaryData&, //pass + const SecureBinaryData&, //control pass + unsigned); //lookup + + static std::shared_ptr createFromSeed( + const std::string&, //folder + Seeds::ClearTextSeed_BIP32*, + const SecureBinaryData&, //pass + const SecureBinaryData&, //control pass + unsigned); //lookup public: //tors @@ -316,7 +335,7 @@ namespace Armory const SecureBinaryData& getDecryptedPrivateKeyForId( const AssetId&) const; - std::shared_ptr getEncryptedSeed(void) const; + std::shared_ptr getEncryptedSeed(void) const; Signer::BIP32_AssetPath getBip32PathForAsset( std::shared_ptr) const; @@ -332,21 +351,11 @@ namespace Armory std::shared_ptr); //static - static std::shared_ptr createFromBIP32Node( - const BIP32_Node& node, - std::set> accountTypes, - const SecureBinaryData& passphrase, - const SecureBinaryData& controlPassphrase, - const std::string& folder); - - static std::shared_ptr - createFromPrivateRoot_Armory135( - const std::string& folder, - const SecureBinaryData& privateRoot, - SecureBinaryData chaincode, - const SecureBinaryData& passphrase, - const SecureBinaryData& controlPassphrase, - unsigned lookup); + static std::shared_ptr createFromSeed( + std::unique_ptr, + const SecureBinaryData&, + const SecureBinaryData&, + const std::string&, unsigned lookup = 1000); static std::shared_ptr createFromPublicRoot_Armory135( @@ -356,28 +365,10 @@ namespace Armory const SecureBinaryData& controlPassphrase, unsigned lookup); - static std::shared_ptr createFromSeed_BIP32( - const std::string& folder, - const SecureBinaryData& seed, - const SecureBinaryData& passphrase, - const SecureBinaryData& controlPassphrase, - unsigned lookup); - - static std::shared_ptr - createFromSeed_BIP32_Blank( - const std::string& folder, - const SecureBinaryData& seed, - const SecureBinaryData& passphrase, - const SecureBinaryData& controlPassphrase); - static std::shared_ptr createBlank( const std::string& folder, const std::string& walletID, const SecureBinaryData& controlPassphrase); - - static std::string computeWalletID( - std::shared_ptr, - std::shared_ptr); }; ////////////////////////////////////////////////////////////////////////// diff --git a/cppForSwig/gtest/SignerTests.cpp b/cppForSwig/gtest/SignerTests.cpp index 8f9db4b89b..caa14db019 100644 --- a/cppForSwig/gtest/SignerTests.cpp +++ b/cppForSwig/gtest/SignerTests.cpp @@ -9,6 +9,7 @@ #include "TestUtils.h" #include "CoinSelection.h" +#include "../Wallets/Seeds/Seeds.h" using namespace std; using namespace Armory::Signer; @@ -388,14 +389,13 @@ TEST_F(SignerTest, SpendTest_SizeEstimates) //// create assetWlt //// - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), + {}, {}, homedir_, - move(wltRoot), //root as a r value - {}, - SecureBinaryData(), - SecureBinaryData(), - 5); //set lookup computation to 5 entries + 5); //register with db vector addrVec; @@ -790,13 +790,11 @@ TEST_F(SignerTest, SpendTest_P2WPKH) //// create assetWlt //// - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - move(wltRoot), //root as a rvalue - SecureBinaryData(), - SecureBinaryData(), - 5); //set lookup computation to 5 entries + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt = AssetWallet_Single::createFromSeed(move(seed), + {}, {}, homedir_, 5); //register with db vector> addrVec; @@ -1039,16 +1037,16 @@ TEST_F(SignerTest, SpendTest_MixedInputTypes) //// create assetWlt //// - auto&& wltRoot = CryptoPRNG::generateRandom(32); + auto rawEntropy = CryptoPRNG::generateRandom(32); BIP32_Node node; - node.initFromSeed(wltRoot); + node.initFromSeed(rawEntropy); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135(rawEntropy)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), + {}, {}, homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), 5); //set lookup computation to 3 entries //add a bip32 account @@ -1304,33 +1302,23 @@ TEST_F(SignerTest, SpendTest_MultipleSigners_1of3) scrAddrVec.push_back(TestChain::scrAddrE); //// create 3 assetWlt //// - - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt_1 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //set lookup computation to 3 entries - - wltRoot = move(CryptoPRNG::generateRandom(32)); - auto assetWlt_2 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //set lookup computation to 3 entries - - wltRoot = move(CryptoPRNG::generateRandom(32)); - auto assetWlt_3 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), {}, {}, homedir_, 3); + + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), {}, {}, homedir_, 3); + + unique_ptr seed3( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_3 = AssetWallet_Single::createFromSeed( + move(seed3), {}, {}, homedir_, 3); //create 1-of-3 multisig asset entry from 3 different wallets map> asset_single_map; @@ -1598,33 +1586,23 @@ TEST_F(SignerTest, SpendTest_MultipleSigners_2of3_NativeP2WSH) scrAddrVec.push_back(TestChain::scrAddrE); //// create 3 assetWlt //// - - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt_1 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //set lookup computation to 3 entries - - wltRoot = move(CryptoPRNG::generateRandom(32)); - auto assetWlt_2 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //set lookup computation to 3 entries - - wltRoot = move(CryptoPRNG::generateRandom(32)); - auto assetWlt_3 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), {}, {}, homedir_, 3); + + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), {}, {}, homedir_, 3); + + unique_ptr seed3( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_3 = AssetWallet_Single::createFromSeed( + move(seed3), {}, {}, homedir_, 3); //create 2-of-3 multisig asset entry from 3 different wallets map> asset_single_map; @@ -1992,22 +1970,15 @@ TEST_F(SignerTest, SpendTest_MultipleSigners_DifferentInputs) scrAddrVec.push_back(TestChain::scrAddrE); //// create 2 assetWlt //// + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), {}, {}, homedir_, 3); - auto assetWlt_1 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - CryptoPRNG::generateRandom(32), //root as rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //set lookup computation to 3 entries - - auto assetWlt_2 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(CryptoPRNG::generateRandom(32)), //root as rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), {}, {}, homedir_, 3); //register with db vector> addrVec_1; @@ -2289,22 +2260,15 @@ TEST_F(SignerTest, SpendTest_MultipleSigners_ParallelSigning) scrAddrVec.push_back(TestChain::scrAddrE); //// create 2 assetWlt //// + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), {}, {}, homedir_, 3); - auto assetWlt_1 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - CryptoPRNG::generateRandom(32), //root as rvalue - {}, - SecureBinaryData(), //empty passphrase - SecureBinaryData(), - 3); //set lookup computation to 3 entries - - auto assetWlt_2 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(CryptoPRNG::generateRandom(32)), //root as rvalue - {}, - SecureBinaryData(), //empty passphrase - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), {}, {}, homedir_, 3); //register with db vector> addrVec_1; @@ -2622,19 +2586,17 @@ TEST_F(SignerTest, SpendTest_MultipleSigners_ParallelSigning_GetUnsignedTx) scrAddrVec.push_back(TestChain::scrAddrE); //// create 2 assetWlt //// - auto assetWlt_1 = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - CryptoPRNG::generateRandom(32), //root as rvalue - SecureBinaryData(), //empty passphrase - SecureBinaryData(), - 3); //set lookup computation to 3 entries - - auto assetWlt_2 = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - move(CryptoPRNG::generateRandom(32)), //root as rvalue - SecureBinaryData(), //empty passphrase - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), {}, {}, homedir_, 3); + + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), {}, {}, homedir_, 3); //register with db vector> addrVec_1; @@ -2988,20 +2950,16 @@ TEST_F(SignerTest, SpendTest_MultipleSigners_ParallelSigning_GetUnsignedTx_Neste scrAddrVec.push_back(TestChain::scrAddrE); //// create 2 assetWlt //// - auto assetWlt_1 = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - CryptoPRNG::generateRandom(32), //root as rvalue - SecureBinaryData(), //empty passphrase - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), {}, {}, homedir_, 3); - auto assetWlt_2 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(CryptoPRNG::generateRandom(32)), //root as rvalue - {}, - SecureBinaryData(), //empty passphrase - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), {}, {}, homedir_, 3); //register with db auto addr_type_nested_p2sh = AddressEntryType(AddressEntryType_P2WPKH | AddressEntryType_P2SH); @@ -3412,20 +3370,16 @@ TEST_F(SignerTest, GetUnsignedTxId) scrAddrVec.push_back(TestChain::scrAddrE); //// create 2 assetWlt //// - auto assetWlt_1 = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - CryptoPRNG::generateRandom(32), //root as rvalue - SecureBinaryData(), //empty passphrase - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), {}, {}, homedir_, 3); - auto assetWlt_2 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(CryptoPRNG::generateRandom(32)), //root as rvalue - {}, - SecureBinaryData(), //empty passphrase - SecureBinaryData(), - 3); //set lookup computation to 3 entries + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), {}, {}, homedir_, 3); //register with db vector> addrVec_1; @@ -3796,12 +3750,11 @@ TEST_F(SignerTest, Wallet_SpendTest_Nested_P2WPKH) //// create assetWlt //// //create empty bip32 wallet - auto&& wltSeed = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, - wltSeed, - SecureBinaryData(), - SecureBinaryData()); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed(move(seed), + {}, {}, homedir_); //add p2sh-p2wpkh account vector derPath = { 0x800061a5, 0x80000000 }; @@ -4053,17 +4006,17 @@ TEST_F(SignerTest, Wallet_SpendTest_Nested_P2WPKH_WOResolution_fromWOCopy) //// create assetWlt //// - auto&& wltSeed = CryptoPRNG::generateRandom(32); + auto rawEntropy = CryptoPRNG::generateRandom(32); string woPath, wltPath; Signer signer3; { //create bip32 wallet - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, - wltSeed, - SecureBinaryData(), - SecureBinaryData()); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed(move(seed), + {}, {}, homedir_); //add p2sh-p2wpkh account vector derPath = { 0x800061a5, 0x80000000 }; @@ -4092,11 +4045,11 @@ TEST_F(SignerTest, Wallet_SpendTest_Nested_P2WPKH_WOResolution_fromWOCopy) AssetWallet::loadMainWalletFromFile(woPath, nullptr)); //recreate empty bip32 wallet - auto emptyWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, - wltSeed, - SecureBinaryData(), - SecureBinaryData()); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Virgin)); + auto emptyWlt = AssetWallet_Single::createFromSeed(move(seed), + {}, {}, homedir_); //// register with db //// vector addrVec; @@ -4343,12 +4296,12 @@ TEST_F(SignerTest, Wallet_SpendTest_Nested_P2WPKH_WOResolution_fromXPub) //// create assetWlt //// //create empty bip32 wallet - auto&& wltSeed = CryptoPRNG::generateRandom(32); - auto emptyWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, - wltSeed, - SecureBinaryData(), - SecureBinaryData()); + auto rawEntropy = CryptoPRNG::generateRandom(32); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Virgin)); + auto emptyWlt = AssetWallet_Single::createFromSeed(move(seed), + {}, {}, homedir_); //create empty WO wallet auto wltWO = AssetWallet_Single::createBlank( @@ -4357,7 +4310,7 @@ TEST_F(SignerTest, Wallet_SpendTest_Nested_P2WPKH_WOResolution_fromXPub) //derive public root vector derPath = { 0x800061a5, 0x80000000 }; BIP32_Node seedNode; - seedNode.initFromSeed(wltSeed); + seedNode.initFromSeed(rawEntropy); auto seedFingerprint = seedNode.getThisFingerprint(); for (auto& derId : derPath) seedNode.derivePrivate(derId); @@ -4613,15 +4566,13 @@ TEST_F(SignerTest, Wallet_SpendTest_Nested_P2PK) scrAddrVec.push_back(TestChain::scrAddrE); //// create assetWlt //// - - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), + {}, {}, homedir_, - move(wltRoot), //root as a r value - {}, - SecureBinaryData(), - SecureBinaryData(), - 3); //lookup computation + 3); //set lookup computation to 3 entries //register with db vector addrVec; @@ -4849,13 +4800,11 @@ TEST_F(SignerTest, SpendTest_FromAccount_Reload) scrAddrVec.push_back(TestChain::scrAddrE); //// create assetWlt //// - - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, - move(wltRoot), //root as a rvalue - SecureBinaryData(), - SecureBinaryData()); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed(move(seed), + {}, {}, homedir_); //add a bip32 account { @@ -5246,12 +5195,11 @@ TEST_F(SignerTest, SpendTest_BIP32_Accounts) //// create assetWlt //// auto passphrase = SecureBinaryData::fromString("test"); - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, - wltRoot, //root as a rvalue - SecureBinaryData(), - passphrase); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed(move(seed), + passphrase, {}, homedir_); auto rootBip32 = dynamic_pointer_cast< AssetEntry_BIP32Root>(assetWlt->getRoot()); @@ -5528,14 +5476,14 @@ TEST_F(SignerTest, SpendTest_FromExtendedAddress_Armory135) //// create assetWlt //// auto passphrase = SecureBinaryData::fromString("test"); - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, SecureBinaryData::fromString("control"), - 5); //set lookup computation to 5 entries + homedir_, + 5); //set lookup computation to 3 entries //register with db DBTestUtils::registerWallet(clients_, bdvID, scrAddrVec, "wallet1"); @@ -5777,13 +5725,11 @@ TEST_F(SignerTest, SpendTest_FromExtendedAddress_BIP32) //// create assetWlt //// auto passphrase = SecureBinaryData::fromString("test"); - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - move(wltRoot), //root as a rvalue - passphrase, - SecureBinaryData::fromString("control"), - 5); //set lookup computation to 5 entries + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt = AssetWallet_Single::createFromSeed(move(seed), + passphrase, SecureBinaryData::fromString("control"), homedir_, 5); //register with db DBTestUtils::registerWallet(clients_, bdvID, scrAddrVec, "wallet1"); @@ -6025,12 +5971,11 @@ TEST_F(SignerTest, SpendTest_FromExtendedAddress_Salted) //// create assetWlt //// auto passphrase = SecureBinaryData::fromString("test"); - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, - wltRoot, //root as a rvalue - passphrase, - SecureBinaryData::fromString("control")); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed(move(seed), + passphrase, SecureBinaryData::fromString("control"), homedir_); auto rootBip32 = dynamic_pointer_cast< AssetEntry_BIP32Root>(assetWlt->getRoot()); @@ -6305,12 +6250,11 @@ TEST_F(SignerTest, SpendTest_FromExtendedAddress_ECDH) //// create assetWlt //// auto passphrase = SecureBinaryData::fromString("test"); - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, - wltRoot, //root as a rvalue - passphrase, - SecureBinaryData::fromString("control")); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed(move(seed), + passphrase, SecureBinaryData::fromString("control"), homedir_); auto ecdhAccType = make_shared(privKey, pubKey); ecdhAccType->setDefaultAddressType( @@ -6576,14 +6520,11 @@ TEST_F(SignerTest, SpendTest_InjectSignature) scrAddrVec.push_back(TestChain::scrAddrE); //// create assetWlt //// - - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - move(wltRoot), //root as a rvalue - SecureBinaryData(), - SecureBinaryData(), - 5); //set lookup computation to 3 entries + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), {}, {}, homedir_, 5); //register with db vector> addrVec; @@ -6963,31 +6904,28 @@ TEST_F(SignerTest, SpendTest_InjectSignature_Multisig) //// create 3 assetWlt //// - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt_1 = AssetWallet_Single::createFromPrivateRoot_Armory135( + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), + {}, {}, homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), 3); //set lookup computation to 3 entries - wltRoot = move(CryptoPRNG::generateRandom(32)); - auto assetWlt_2 = AssetWallet_Single::createFromPrivateRoot_Armory135( + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), + {}, {}, homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), 3); //set lookup computation to 3 entries - wltRoot = move(CryptoPRNG::generateRandom(32)); - auto assetWlt_3 = AssetWallet_Single::createFromPrivateRoot_Armory135( + unique_ptr seed3( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_3 = AssetWallet_Single::createFromSeed( + move(seed3), + {}, {}, homedir_, - move(wltRoot), //root as a rvalue - {}, - SecureBinaryData(), - SecureBinaryData(), 3); //set lookup computation to 3 entries //create 2-of-3 multisig asset entry from 3 different wallets @@ -8652,8 +8590,8 @@ TEST_F(ExtrasTest, PSBT) auto txOut = tx.getTxOutCopy(index); UTXO utxo( - txOut.getValue(), - UINT32_MAX, UINT32_MAX, index, + txOut.getValue(), + UINT32_MAX, UINT32_MAX, index, hash, txOut.getScript()); return utxo; @@ -8713,16 +8651,17 @@ TEST_F(ExtrasTest, PSBT) auto b58seed = SecureBinaryData::fromString( "tprv8ZgxMBicQKsPd9TeAdPADNnSyH9SSUUbTVeFszDE23Ki6TBB5nCefAdHkK8Fm3qMQR6sHwA56zqRmKmxnHk37JkiFzvncDqoKmPWubu7hDF"); + //create a wallet from that seed to test bip32 on the fly derivation + auto wallet = AssetWallet_Single::createFromSeed( + Armory::Seeds::ClearTextSeed_BIP32::fromBase58(b58seed), + SecureBinaryData(), SecureBinaryData(), + homedir_); + + //create node BIP32_Node node; node.initFromBase58(b58seed); auto masterFingerprint = node.getThisFingerprint(); - //create a wallet from that seed to test bip32 on the fly derivation - auto wallet = AssetWallet_Single::createFromBIP32Node( - node, {}, - SecureBinaryData(), SecureBinaryData(), - homedir_); - // 0'/0' node.derivePrivate(0x80000000); node.derivePrivate(0x80000000); @@ -8764,7 +8703,7 @@ TEST_F(ExtrasTest, PSBT) EXPECT_EQ(psbtTestVal, signer2.toPSBT()); Signer signer3(signer.serializeState()); - EXPECT_EQ(psbtTestVal, signer3.toPSBT()); + EXPECT_EQ(psbtTestVal, signer3.toPSBT()); } //resolve scripts @@ -8997,7 +8936,7 @@ TEST_F(ExtrasTest, PSBT) EXPECT_EQ(psbtHalf2, signer2.toPSBT()); Signer signer3(signer.serializeState()); - EXPECT_EQ(psbtHalf2, signer3.toPSBT()); + EXPECT_EQ(psbtHalf2, signer3.toPSBT()); } //combine sigs & finalize inputs @@ -9158,10 +9097,10 @@ class ExtrasTest_Mainnet : public ::testing::Test //////////////////////////////////////////////////////////////////////////////// TEST_F(ExtrasTest_Mainnet, Bip32PathDiscovery) { - auto seed = CryptoPRNG::generateRandom(32); + auto rawEntropy = CryptoPRNG::generateRandom(32); BIP32_Node node; - node.initFromSeed(seed); + node.initFromSeed(rawEntropy); auto masterFingerprint = node.getThisFingerprint(); vector derPath = { 0x8000002C, 0x80000000, 0x80000000 }; @@ -9191,10 +9130,11 @@ TEST_F(ExtrasTest_Mainnet, Bip32PathDiscovery) string wltPath; { - auto wallet = AssetWallet_Single::createFromSeed_BIP32( - homedir_, seed, - SecureBinaryData(), SecureBinaryData(), - 10); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Structured)); + auto wallet = AssetWallet_Single::createFromSeed( + move(seed), {}, {}, homedir_, 10); wltPath = wallet->getDbFilename(); auto woWalletPath = wallet->forkWatchingOnly(wltPath, passLbd); diff --git a/cppForSwig/gtest/SupernodeTests.cpp b/cppForSwig/gtest/SupernodeTests.cpp index 94f1296d79..db28f45b23 100644 --- a/cppForSwig/gtest/SupernodeTests.cpp +++ b/cppForSwig/gtest/SupernodeTests.cpp @@ -12,6 +12,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "TestUtils.h" +#include "../Wallets/Seeds/Seeds.h" using namespace std; using namespace Armory::Signer; using namespace Armory::Config; @@ -1762,31 +1763,31 @@ TEST_F(BlockUtilsWithWalletTest, MultipleSigners_2of3_NativeP2WSH) //// create 3 assetWlt //// //create a root private key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt_1 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, + unique_ptr seed1( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_1 = AssetWallet_Single::createFromSeed( + move(seed1), + SecureBinaryData(), SecureBinaryData(), - SecureBinaryData(), + homedir_, 3); //set lookup computation to 3 entries - wltRoot = move(CryptoPRNG::generateRandom(32)); - auto assetWlt_2 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, + unique_ptr seed2( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_2 = AssetWallet_Single::createFromSeed( + move(seed2), + SecureBinaryData(), SecureBinaryData(), - SecureBinaryData(), + homedir_, 3); //set lookup computation to 3 entries - wltRoot = move(CryptoPRNG::generateRandom(32)); - auto assetWlt_3 = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, + unique_ptr seed3( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt_3 = AssetWallet_Single::createFromSeed( + move(seed3), SecureBinaryData(), - SecureBinaryData(), + SecureBinaryData(), + homedir_, 3); //set lookup computation to 3 entries //create 2-of-3 multisig asset entry from 3 different wallets diff --git a/cppForSwig/gtest/WalletTests.cpp b/cppForSwig/gtest/WalletTests.cpp index d169d82ea6..4bfb1415bf 100644 --- a/cppForSwig/gtest/WalletTests.cpp +++ b/cppForSwig/gtest/WalletTests.cpp @@ -12,6 +12,7 @@ #include "../Wallets/Seeds/Backups.h" #include "../Wallets/Seeds/Seeds.h" #include "../Wallets/WalletFileInterface.h" +#include "protobuf/BridgeProto.pb.h" using namespace std; using namespace Armory::Signer; @@ -20,7 +21,7 @@ using namespace Armory::Assets; using namespace Armory::Accounts; using namespace Armory::Wallets; using namespace Armory::Wallets::Encryption; -using namespace Armory::Seed; +using namespace Armory::Seeds; //////////////////////////////////////////////////////////////////////////////// #define METHOD_ASSERT_EQ(a, b) \ @@ -4148,14 +4149,14 @@ TEST_F(WalletsTest, CreateCloseOpen_Test) //create 3 wallets for (unsigned i = 0; i < 1; i++) { - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), controlPass_, - 4); //set lookup computation to 4 entries + homedir_, + 4); //set lookup computation to 3 entries //get AddrVec auto&& hashSet = assetWlt->getAddrHashSet(); @@ -4197,14 +4198,14 @@ TEST_F(WalletsTest, CreateCloseOpen_Test) TEST_F(WalletsTest, CreateWOCopy_Test) { //create 1 wallet from priv key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), - 4); //set lookup computation to 4 entries + homedir_, + 4); //set lookup computation to 3 entries auto filename = assetWlt->getDbFilename(); //get AddrVec @@ -4276,35 +4277,24 @@ TEST_F(WalletsTest, CreateWOCopy_Test) //////////////////////////////////////////////////////////////////////////////// TEST_F(WalletsTest, IDs) { - auto computeId = []( - const SecureBinaryData& root, const SecureBinaryData& chaincode)->string + auto rawEntropy = CryptoPRNG::generateRandom(32); + auto rawPubkey = CryptoECDSA().ComputePublicKey(rawEntropy); + string id; { - auto ccCopy = chaincode; - if (chaincode.empty()) - ccCopy = BtcUtils::computeChainCode_Armory135(root); - - auto derScheme = - make_shared(ccCopy); - - auto pubkey = CryptoECDSA().ComputePublicKey(root); - auto asset_single = make_shared( - AssetId::getRootAssetId(), pubkey, nullptr); - - return AssetWallet_Single::computeWalletID(derScheme, asset_single); - }; - - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto id = computeId(wltRoot, {}); + auto chaincode = BtcUtils::computeChainCode_Armory135(rawEntropy); + id = generateWalletId(rawPubkey, chaincode, SeedType::Armory135); + } ASSERT_FALSE(id.empty()); //legacy wallet { - auto wlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135(rawEntropy)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries EXPECT_EQ(wlt->getID(), id); @@ -4312,18 +4302,23 @@ TEST_F(WalletsTest, IDs) //bip32 wallet { - auto wlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - wltRoot, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, + Armory::Seeds::SeedType::BIP32_Structured)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries //wallet id BIP32_Node node; - node.initFromSeed(wltRoot); + node.initFromSeed(rawEntropy); - auto idBip32 = computeId(node.getPrivateKey(), node.getChaincode()); + auto idBip32 = generateWalletId(node.getPublicKey(), node.getChaincode(), + SeedType::BIP32_Structured); EXPECT_EQ(wlt->getID(), idBip32); //account ids @@ -4403,41 +4398,103 @@ TEST_F(WalletsTest, IDs) //legacy with chaincode auto chaincode = CryptoPRNG::generateRandom(32); - auto idcc = computeId(wltRoot, chaincode); + auto idcc = generateWalletId(rawPubkey, chaincode, + SeedType::Armory135); ASSERT_NE(id, idcc); { - auto wlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, - chaincode, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135( + rawEntropy, chaincode)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries EXPECT_EQ(wlt->getID(), idcc); } } +//////////////////////////////////////////////////////////////////////////////// +TEST_F(WalletsTest, ID_fromSeeds) +{ + auto rawSBD = SecureBinaryData::CreateFromHex( + "0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20"); + + //Armory135 + { + auto seed135 = make_unique( + rawSBD, ClearTextSeed_Armory135::LegacyType::Armory135); + EXPECT_EQ(seed135->getWalletId(), "2vrVAxyHR"); + EXPECT_EQ(seed135->getMasterId(), "LZxsEgeT"); + } + + //Armory200a + { + auto seed135 = make_unique( + rawSBD, ClearTextSeed_Armory135::LegacyType::Armory200); + EXPECT_EQ(seed135->getWalletId(), "2vrVAxyHR"); + EXPECT_EQ(seed135->getMasterId(), "LZxsEgeT"); + } + + //BIP32 structured + { + auto bip32 = make_unique( + rawSBD, SeedType::BIP32_Structured); + EXPECT_EQ(bip32->getWalletId(), "2BuhCGwV9"); + EXPECT_EQ(bip32->getMasterId(), "2d9H95rzK"); + } + + //BIP32 virgin + { + auto bip32 = make_unique( + rawSBD, SeedType::BIP32_Virgin); + EXPECT_EQ(bip32->getWalletId(), "22bd31PB5"); + EXPECT_EQ(bip32->getMasterId(), "2d9H95rzK"); + } + + //xpriv + { + BIP32_Node node; + node.initFromSeed(rawSBD); + auto xpriv = node.getBase58(); + + auto base58 = ClearTextSeed_BIP32::fromBase58(xpriv); + EXPECT_EQ(base58->getWalletId(), "33qBfTB51"); + EXPECT_EQ(base58->getMasterId(), "2d9H95rzK"); + } + + /* + //BIP39 + auto bip32 = make_unique(rawSBD, 1); + EXPECT_EQ(bip32->getWalletId(), "abcd"); + EXPECT_EQ(bip32->getMasterId(), "abcd"); + */ +} + //////////////////////////////////////////////////////////////////////////////// TEST_F(WalletsTest, Encryption_Test) { //#1: check deriving from an encrypted root yield correct chain //create 1 wallet from priv key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, - {}, + auto rawEntropy = CryptoPRNG::generateRandom(32); + + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135(rawEntropy)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries //derive private chain from root - auto&& chaincode = BtcUtils::computeChainCode_Armory135(wltRoot); + auto&& chaincode = BtcUtils::computeChainCode_Armory135(rawEntropy); vector privateKeys; - auto currentPrivKey = &wltRoot; + auto currentPrivKey = &rawEntropy; for (int i = 0; i < 4; i++) { @@ -4529,21 +4586,28 @@ TEST_F(WalletsTest, SeedEncryption) auto&& passphrase = SecureBinaryData::fromString("password"); //create regular wallet - auto&& seed = CryptoPRNG::generateRandom(32); - auto wlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, seed, passphrase, - SecureBinaryData::fromString("control"), 10); + auto rawEntropy = CryptoPRNG::generateRandom(32); + + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Structured)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), + passphrase, + SecureBinaryData::fromString("control"), + homedir_, + 10); //check clear text seed does not exist on disk auto filename = wlt->getDbFilename(); - ASSERT_FALSE(TestUtils::searchFile(filename, seed)); + ASSERT_FALSE(TestUtils::searchFile(filename, rawEntropy)); //grab without passphrase lbd, should fail try { auto lock = wlt->lockDecryptedContainer(); auto decryptedSeed = wlt->getDecryptedValue(wlt->getEncryptedSeed()); - EXPECT_EQ(decryptedSeed, seed); + EXPECT_EQ(decryptedSeed, rawEntropy); ASSERT_TRUE(false); } catch (DecryptedDataContainerException&) @@ -4562,7 +4626,7 @@ TEST_F(WalletsTest, SeedEncryption) try { auto decryptedSeed = wlt->getDecryptedValue(wlt->getEncryptedSeed()); - EXPECT_EQ(decryptedSeed, seed); + EXPECT_EQ(decryptedSeed, rawEntropy); ASSERT_TRUE(false); } catch (DecryptedDataContainerException&) @@ -4572,8 +4636,10 @@ TEST_F(WalletsTest, SeedEncryption) try { auto lock = wlt->lockDecryptedContainer(); - auto decryptedSeed = wlt->getDecryptedValue(wlt->getEncryptedSeed()); - EXPECT_EQ(decryptedSeed, seed); + auto clearTextSeed = ClearTextSeed::deserialize( + wlt->getDecryptedValue(wlt->getEncryptedSeed())); + auto seedBip32 = dynamic_cast(clearTextSeed.get()); + EXPECT_EQ(seedBip32->getRawEntropy(), rawEntropy); } catch (DecryptedDataContainerException&) { @@ -4586,7 +4652,7 @@ TEST_F(WalletsTest, SeedEncryption) { auto lock = wlt->lockDecryptedContainer(); auto decryptedSeed = wlt->getDecryptedValue(wlt->getEncryptedSeed()); - EXPECT_EQ(decryptedSeed, seed); + EXPECT_EQ(decryptedSeed, rawEntropy); ASSERT_TRUE(false); } catch (DecryptedDataContainerException&) @@ -4616,8 +4682,10 @@ TEST_F(WalletsTest, SeedEncryption) try { auto lock = wlt->lockDecryptedContainer(); - auto decryptedSeed = wlt->getDecryptedValue(wlt->getEncryptedSeed()); - EXPECT_EQ(decryptedSeed, seed); + auto clearTextSeed = ClearTextSeed::deserialize( + wlt->getDecryptedValue(wlt->getEncryptedSeed())); + auto seedBip32 = dynamic_cast(clearTextSeed.get()); + EXPECT_EQ(seedBip32->getRawEntropy(), rawEntropy); } catch (DecryptedDataContainerException&) { @@ -4629,13 +4697,15 @@ TEST_F(WalletsTest, SeedEncryption) TEST_F(WalletsTest, LockAndExtend_Test) { //create wallet from priv key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, //root as a r value - {}, + auto rawEntropy = CryptoPRNG::generateRandom(32); + + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135(rawEntropy)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), //root as a r value SecureBinaryData::fromString("passphrase"), //set passphrase to "test" controlPass_, + homedir_, 4); //set lookup computation to 4 entries auto passLbd = [] @@ -4647,10 +4717,10 @@ TEST_F(WalletsTest, LockAndExtend_Test) assetWlt->setPassphrasePromptLambda(passLbd); //derive private chain from root - auto&& chaincode = BtcUtils::computeChainCode_Armory135(wltRoot); + auto&& chaincode = BtcUtils::computeChainCode_Armory135(rawEntropy); vector privateKeys; - auto currentPrivKey = &wltRoot; + auto currentPrivKey = &rawEntropy; for (int i = 0; i < 10; i++) { @@ -4840,13 +4910,13 @@ TEST_F(WalletsTest, ControlPassphrase_Test) string filename; set addrSet; { - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("test"), //set passphrase to "test" SecureBinaryData::fromString("control"), //control passphrase + homedir_, 4); //set lookup computation to 4 entries filename = assetWlt->getDbFilename(); addrSet = assetWlt->getAddrHashSet(); @@ -5011,12 +5081,14 @@ TEST_F(WalletsTest, ControlPassphrase_Test) string filename2; { - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - wltRoot, //root as a r value + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("test"), //set passphrase to "test" SecureBinaryData(), //empty control passphrase + homedir_, 4); //set lookup computation to 4 entries filename2 = assetWlt->getDbFilename(); addrSet = assetWlt->getAddrHashSet(); @@ -5177,13 +5249,15 @@ TEST_F(WalletsTest, ControlPassphrase_Test) TEST_F(WalletsTest, SignPassphrase_Test) { //create wallet from priv key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, //root as a r value - {}, + auto rawEntropy = CryptoPRNG::generateRandom(32); + + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135(rawEntropy)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("test"), //set passphrase to "test" SecureBinaryData::fromString("control"), //control passphrase + homedir_, 4); //set lookup computation to 4 entries unsigned passphraseCount = 0; @@ -5247,9 +5321,9 @@ TEST_F(WalletsTest, SignPassphrase_Test) auto& privkey = assetWlt->getDecryptedValue(asset_single->getPrivKey()); //make sure decrypted privkey is valid - auto&& chaincode = BtcUtils::computeChainCode_Armory135(wltRoot); + auto&& chaincode = BtcUtils::computeChainCode_Armory135(rawEntropy); auto&& privkey_ex = - CryptoECDSA().ComputeChainedPrivateKey(wltRoot, chaincode); + CryptoECDSA().ComputeChainedPrivateKey(rawEntropy, chaincode); ASSERT_EQ(privkey, privkey_ex); } @@ -5265,13 +5339,16 @@ TEST_F(WalletsTest, SignPassphrase_Test) TEST_F(WalletsTest, WrongPassphrase_BIP32_Test) { //create wallet from priv key - auto&& wltRoot = CryptoPRNG::generateRandom(32); + auto rawEntropy = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - wltRoot, //root as a r value + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), //root as a r value SecureBinaryData::fromString("test"), //set passphrase to "test" SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries unsigned passphraseCount = 0; @@ -5336,7 +5413,7 @@ TEST_F(WalletsTest, WrongPassphrase_BIP32_Test) //make sure decrypted privkey is valid BIP32_Node node; - node.initFromSeed(wltRoot); + node.initFromSeed(rawEntropy); node.derivePrivate(0x8000002C); node.derivePrivate(0x80000000); @@ -5407,7 +5484,7 @@ TEST_F(WalletsTest, WrongPassphrase_BIP32_Test) //make sure decrypted privkey is valid BIP32_Node node; - node.initFromSeed(wltRoot); + node.initFromSeed(rawEntropy); for (auto& der : derPath2) node.derivePrivate(der); @@ -5428,18 +5505,20 @@ TEST_F(WalletsTest, WrongPassphrase_BIP32_Test) TEST_F(WalletsTest, ChangePassphrase_Test) { //create wallet from priv key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, //root as a r value - {}, + auto rawEntropy = CryptoPRNG::generateRandom(32); + + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135(rawEntropy)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("test"), //set passphrase to "test" controlPass_, + homedir_, 4); //set lookup computation to 4 entries - auto&& chaincode = BtcUtils::computeChainCode_Armory135(wltRoot); + auto&& chaincode = BtcUtils::computeChainCode_Armory135(rawEntropy); auto&& privkey_ex = - CryptoECDSA().ComputeChainedPrivateKey(wltRoot, chaincode); + CryptoECDSA().ComputeChainedPrivateKey(rawEntropy, chaincode); auto filename = assetWlt->getDbFilename(); @@ -5734,18 +5813,20 @@ TEST_F(WalletsTest, ChangePassphrase_Test) TEST_F(WalletsTest, ChangePassphrase_FromUnencryptedWallet_Test) { //create wallet from priv key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, //root as a r value - {}, + auto rawEntropy = CryptoPRNG::generateRandom(32); + + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135(rawEntropy)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData(), //set passphrase to "test" SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries - auto&& chaincode = BtcUtils::computeChainCode_Armory135(wltRoot); + auto&& chaincode = BtcUtils::computeChainCode_Armory135(rawEntropy); auto&& privkey_ex = - CryptoECDSA().ComputeChainedPrivateKey(wltRoot, chaincode); + CryptoECDSA().ComputeChainedPrivateKey(rawEntropy, chaincode); auto filename = assetWlt->getDbFilename(); auto newPass = SecureBinaryData::fromString("newpass"); @@ -5937,18 +6018,20 @@ TEST_F(WalletsTest, ChangePassphrase_FromUnencryptedWallet_Test) //////////////////////////////////////////////////////////////////////////////// TEST_F(WalletsTest, ChangeControlPassphrase_Test) { - + auto&& newPass = SecureBinaryData::fromString("newpass"); //create wallet string filename; { - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - wltRoot, //root as a r value + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), //root as a r value SecureBinaryData::fromString("test"), //set passphrase to "test" SecureBinaryData::fromString("control"), + homedir_, 40); //set lookup computation to 4 entries filename = assetWlt->getDbFilename(); @@ -6092,13 +6175,13 @@ TEST_F(WalletsTest, ChangeControlPassphrase_Test) TEST_F(WalletsTest, MultiplePassphrase_Test) { //create wallet from priv key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("test"), //set passphrase to "test" controlPass_, + homedir_, 4); //set lookup computation to 4 entries auto passLbd1 = [](const set&)->SecureBinaryData @@ -6201,9 +6284,14 @@ TEST_F(WalletsTest, BIP32_Chain) account->setMain(true); account->setAddressLookup(4); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, wltSeed, - SecureBinaryData::fromString("test"), controlPass_); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + wltSeed, Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), + SecureBinaryData::fromString("test"), + controlPass_, + homedir_); { auto passphraseLbd = [] @@ -6282,7 +6370,7 @@ TEST_F(WalletsTest, BIP32_Public_Chain) ASSERT_NE(assetSingle, nullptr); BIP32_Node pubNode; - auto&& pub_b58 = + auto&& pub_b58 = SecureBinaryData::fromString("xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"); pubNode.initFromBase58(pub_b58); @@ -6292,19 +6380,22 @@ TEST_F(WalletsTest, BIP32_Public_Chain) //////////////////////////////////////////////////////////////////////////////// TEST_F(WalletsTest, BIP32_ArmoryDefault) { - vector derivationPath = + vector derivationPath = { 0x8000002C, 0x80000000, 0x80000000 }; - auto&& seed = CryptoPRNG::generateRandom(32); + auto rawEntropy = CryptoPRNG::generateRandom(32); //create empty wallet auto&& passphrase = SecureBinaryData::fromString("password"); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, seed, passphrase, controlPass_, 5); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_, 5); auto mainAcc = assetWlt->getAccountForID(assetWlt->getMainAccountID()); auto outerAcc = mainAcc->getOuterAccount(); @@ -6312,7 +6403,7 @@ TEST_F(WalletsTest, BIP32_ArmoryDefault) outerAcc->getRoot()); BIP32_Node node; - node.initFromSeed(seed); + node.initFromSeed(rawEntropy); for (auto id : derivationPath) node.derivePrivate(id); node.derivePrivate(0); @@ -6344,12 +6435,15 @@ TEST_F(WalletsTest, BIP32_Chain_AddAccount) }; //random seed - auto&& seed = CryptoPRNG::generateRandom(32); + auto rawEntropy = CryptoPRNG::generateRandom(32); //create empty wallet - auto&& passphrase = SecureBinaryData::fromString("password"); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, seed, passphrase, controlPass_); + auto passphrase = SecureBinaryData::fromString("password"); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_); //this is a hard derivation scenario, the wallet needs to be able to //decrypt its root's private key @@ -6372,7 +6466,7 @@ TEST_F(WalletsTest, BIP32_Chain_AddAccount) //derive bip32 node BIP32_Node seedNode; - seedNode.initFromSeed(seed); + seedNode.initFromSeed(rawEntropy); for (auto& derId : derivationPath1) seedNode.derivePrivate(derId); @@ -6441,7 +6535,7 @@ TEST_F(WalletsTest, BIP32_Chain_AddAccount) auto accountID2 = assetWlt->createBIP32Account(accountTypePtr); BIP32_Node seedNode2; - seedNode2.initFromSeed(seed); + seedNode2.initFromSeed(rawEntropy); for (auto& derId : derivationPath2) seedNode2.derivePrivate(derId); seedNode2.derivePrivate(50); @@ -6530,9 +6624,11 @@ TEST_F(WalletsTest, BIP32_Fork_WatchingOnly) auto&& passphrase = SecureBinaryData::fromString("password"); //create regular wallet - auto&& seed = CryptoPRNG::generateRandom(32); - auto wlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, seed, passphrase, controlPass_, 10); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_, 10); //create WO copy auto woCopyPath = AssetWallet::forkWatchingOnly( @@ -6622,13 +6718,16 @@ TEST_F(WalletsTest, BIP32_WatchingOnly_FromXPub) auto&& passphrase = SecureBinaryData::fromString("password"); //create regular wallet - auto&& seed = CryptoPRNG::generateRandom(32); - auto wlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, seed, passphrase, controlPass_, 10); + auto rawEntropy = CryptoPRNG::generateRandom(32); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Structured)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_, 10); //get xpub for main account BIP32_Node seedNode; - seedNode.initFromSeed(seed); + seedNode.initFromSeed(rawEntropy); auto seedFingerprint = seedNode.getThisFingerprint(); for (auto& derId : derPath) seedNode.derivePrivate(derId); @@ -6692,9 +6791,11 @@ TEST_F(WalletsTest, AddressEntryTypes) auto&& passphrase = SecureBinaryData::fromString("password"); //create regular wallet - auto&& seed = CryptoPRNG::generateRandom(32); - auto wlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, seed, passphrase, controlPass_, 10); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_, 10); //grab a bunch of addresses of various types set addrHashes; @@ -6772,9 +6873,12 @@ TEST_F(WalletsTest, LegacyUncompressedAddressTypes) auto&& passphrase = SecureBinaryData::fromString("password"); //create regular wallet - auto&& seed = CryptoPRNG::generateRandom(32); - auto wlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, seed, passphrase, controlPass_); + auto rawEntropy = CryptoPRNG::generateRandom(32); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Virgin)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_); //create account with all common uncompressed address types & their //compressed counterparts @@ -6814,7 +6918,7 @@ TEST_F(WalletsTest, LegacyUncompressedAddressTypes) //derive the keys locally and reproduce the addresses BIP32_Node bip32Node; - bip32Node.initFromSeed(seed); + bip32Node.initFromSeed(rawEntropy); for (auto& der : derPath) bip32Node.derivePrivate(der); bip32Node.derivePublic(0); //spender leaf @@ -6884,9 +6988,9 @@ TEST_F(WalletsTest, BIP32_SaltedAccount) 327 }; - auto&& seed = CryptoPRNG::generateRandom(32); - auto&& salt1 = CryptoPRNG::generateRandom(32); - auto&& salt2 = CryptoPRNG::generateRandom(32); + auto rawEntropy = CryptoPRNG::generateRandom(32); + auto salt1 = CryptoPRNG::generateRandom(32); + auto salt2 = CryptoPRNG::generateRandom(32); string filename; AddressAccountId accountID1; @@ -6896,9 +7000,12 @@ TEST_F(WalletsTest, BIP32_SaltedAccount) { //create empty wallet + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Virgin)); auto&& passphrase = SecureBinaryData::fromString("password"); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, seed, passphrase, controlPass_); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_); auto rootbip32 = dynamic_pointer_cast( assetWlt->getRoot()); @@ -6944,7 +7051,7 @@ TEST_F(WalletsTest, BIP32_SaltedAccount) //derive from seed { BIP32_Node seedNode; - seedNode.initFromSeed(seed); + seedNode.initFromSeed(rawEntropy); for (auto& derId : derivationPath1) seedNode.derivePrivate(derId); @@ -6961,7 +7068,7 @@ TEST_F(WalletsTest, BIP32_SaltedAccount) { BIP32_Node seedNode; - seedNode.initFromSeed(seed); + seedNode.initFromSeed(rawEntropy); for (auto& derId : derivationPath2) seedNode.derivePrivate(derId); @@ -7002,7 +7109,7 @@ TEST_F(WalletsTest, BIP32_SaltedAccount) //derive from seed { BIP32_Node seedNode; - seedNode.initFromSeed(seed); + seedNode.initFromSeed(rawEntropy); for (auto& derId : derivationPath1) seedNode.derivePrivate(derId); @@ -7019,7 +7126,7 @@ TEST_F(WalletsTest, BIP32_SaltedAccount) { BIP32_Node seedNode; - seedNode.initFromSeed(seed); + seedNode.initFromSeed(rawEntropy); for (auto& derId : derivationPath2) seedNode.derivePrivate(derId); @@ -7064,7 +7171,7 @@ TEST_F(WalletsTest, BIP32_SaltedAccount) //derive from seed { BIP32_Node seedNode; - seedNode.initFromSeed(seed); + seedNode.initFromSeed(rawEntropy); for (auto& derId : derivationPath1) seedNode.derivePrivate(derId); @@ -7081,7 +7188,7 @@ TEST_F(WalletsTest, BIP32_SaltedAccount) { BIP32_Node seedNode; - seedNode.initFromSeed(seed); + seedNode.initFromSeed(rawEntropy); for (auto& derId : derivationPath2) seedNode.derivePrivate(derId); @@ -7104,8 +7211,6 @@ TEST_F(WalletsTest, ECDH_Account) //create blank wallet string filename, woFilename; - auto&& seed = CryptoPRNG::generateRandom(32); - auto&& privKey1 = READHEX( "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); auto&& pubKey1 = CryptoECDSA().ComputePublicKey(privKey1, true); @@ -7125,8 +7230,11 @@ TEST_F(WalletsTest, ECDH_Account) { //create empty wallet - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, seed, passphrase, controlPass_); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Virgin)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_); auto passphraseLbd = [&passphrase] (const set&) @@ -7374,7 +7482,7 @@ TEST_F(WalletsTest, ECDH_Account) TEST_F(WalletsTest, AssetPathResolution) { //seed shared across all wallet instances - auto seed = CryptoPRNG::generateRandom(32); + auto rawEntropy = CryptoPRNG::generateRandom(32); vector derPath = { 0x800012ab, @@ -7383,7 +7491,7 @@ TEST_F(WalletsTest, AssetPathResolution) }; BIP32_Node node; - node.initFromSeed(seed); + node.initFromSeed(rawEntropy); auto seedFingerprint = node.getThisFingerprint(); for (auto& step : derPath) @@ -7432,9 +7540,11 @@ TEST_F(WalletsTest, AssetPathResolution) { //empty wallet + custom account - auto wlt = AssetWallet_Single::createFromSeed_BIP32_Blank( - homedir_, seed, - SecureBinaryData(), SecureBinaryData()); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + rawEntropy, Armory::Seeds::SeedType::BIP32_Virgin)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), {}, {}, homedir_); auto account = wlt->makeNewBip32AccTypeObject(derPath); account->setMain(true); @@ -7472,26 +7582,9 @@ TEST_F(WalletsTest, AssetPathResolution) //empty WO wallet auto wltWO = AssetWallet_Single::createBlank(homedir_, "walletWO1", {}); - auto pubkey = pubNode.getPublicKey(); - auto chaincode = pubNode.getChaincode(); - - auto pubRootAsset = make_shared( - AssetId(0, 0, 0), //not relevant, this stuff is ignored in this context - - pubkey, //pub key - nullptr, //no priv key, this is a public node - chaincode, //have to pass the chaincode too - - //aesthetical stuff, not mandatory, not useful for the crypto side of things - pubNode.getDepth(), pubNode.getLeafID(), pubNode.getParentFingerprint(), seedFingerprint, - - //derivation path for this root, used for path discovery & PSBT - derPath - ); - //add account - auto mainAccType = - AccountType_BIP32::makeFromDerPaths(seedFingerprint, {derPath}); + auto mainAccType = AccountType_BIP32::makeFromDerPaths( + seedFingerprint, {derPath}); mainAccType->setMain(true); mainAccType->setAddressLookup(10); mainAccType->setNodes({0}); @@ -7515,9 +7608,11 @@ TEST_F(WalletsTest, isAssetIdInUse) auto passphrase = SecureBinaryData::fromString("password"); //create regular wallet - auto seed = CryptoPRNG::generateRandom(32); - auto wlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, seed, passphrase, controlPass_, 10); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass_, homedir_, 10); //grab a bunch of addresses of various types map addrHashesInUse; @@ -8330,9 +8425,11 @@ TEST_F(WalletMetaDataTest, Comments) //create regular wallet string filename; { - auto&& seed = CryptoPRNG::generateRandom(32); - auto wlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, seed, passphrase, controlPass, 10); + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto wlt = AssetWallet_Single::createFromSeed( + move(seed), passphrase, controlPass, homedir_, 10); filename = wlt->getDbFilename(); //set comments @@ -8402,7 +8499,8 @@ class BackupTests : public ::testing::Test Armory::Config::parseArgs({ "--offline", - "--datadir=./fakehomedir" }, + "--datadir=./fakehomedir", + "--testnet" }, Armory::Config::ProcessType::DB); } @@ -8417,7 +8515,7 @@ class BackupTests : public ::testing::Test ///////////////////////////////////////////////////////////////////////////// bool compareWalletWithBackup( std::shared_ptr assetWlt, - const string& path, + const string& path, const SecureBinaryData& pass, const SecureBinaryData& control) { unsigned controlPassCount = 0; @@ -8494,19 +8592,19 @@ class BackupTests : public ::testing::Test //////////////////////////////////////////////////////////////////////////////// TEST_F(BackupTests, Easy16) { - for (const auto& index : Armory::Seeds::BackupEasy16::eligibleIndexes_) + for (const auto& index : Armory::Seeds::Easy16Codec::eligibleIndexes_) { auto root = CryptoPRNG::generateRandom(32); //encode the root - auto encoded = Armory::Seeds::BackupEasy16::encode(root.getRef(), index); + auto encoded = Armory::Seeds::Easy16Codec::encode(root.getRef(), index); ASSERT_EQ(encoded.size(), 2ULL); - auto decoded = Armory::Seeds::BackupEasy16::decode(encoded); + auto decoded = Armory::Seeds::Easy16Codec::decode(encoded); ASSERT_EQ(decoded.checksumIndexes_.size(), 2ULL); - EXPECT_EQ(decoded.checksumIndexes_[0], index); - EXPECT_EQ(decoded.checksumIndexes_[1], index); + EXPECT_EQ(decoded.checksumIndexes_[0], (uint8_t)index); + EXPECT_EQ(decoded.checksumIndexes_[1], (uint8_t)index); EXPECT_EQ(decoded.data_, root); } @@ -8516,7 +8614,7 @@ TEST_F(BackupTests, Easy16) TEST_F(BackupTests, Easy16_Repair) { /*NOTE: this test will lead to a lot of hashing*/ - auto corruptLine = [](vector& lines, + auto corruptLine = [](vector& lines, uint8_t lineSelect, uint8_t wordSelect, uint8_t charSelect, uint8_t newVal) { auto& line = lines[lineSelect]; @@ -8530,7 +8628,7 @@ TEST_F(BackupTests, Easy16_Repair) char newChar; while (true) { - newChar = Armory::Seeds::BackupEasy16::e16chars_[newVal % 16]; + newChar = Armory::Seeds::Easy16Codec::e16chars_[newVal % 16]; if (newChar != val) break; @@ -8547,9 +8645,10 @@ TEST_F(BackupTests, Easy16_Repair) for (unsigned i=0; i<64; i++) { auto root = prng.generateRandom(32); - + //encode the root - auto encoded = Armory::Seeds::BackupEasy16::encode(root.getRef(), 0); + auto encoded = Armory::Seeds::Easy16Codec::encode(root.getRef(), + Armory::Seeds::BackupType::Armory135); ASSERT_EQ(encoded.size(), 2ULL); //corrupt one character in one line @@ -8565,7 +8664,7 @@ TEST_F(BackupTests, Easy16_Repair) ASSERT_NE(encoded[lineSelect], corrupted[lineSelect]); //decode the corrupted data, should yield an incorrect value - auto decoded = Armory::Seeds::BackupEasy16::decode(corrupted); + auto decoded = Armory::Seeds::Easy16Codec::decode(corrupted); ASSERT_EQ(decoded.checksumIndexes_.size(), 2ULL); if (lineSelect == 0) { @@ -8583,7 +8682,7 @@ TEST_F(BackupTests, Easy16_Repair) //attempt to repair, may fail because of collisions (no unique solution) try { - auto result = Armory::Seeds::BackupEasy16::repair(decoded); + auto result = Armory::Seeds::Easy16Codec::repair(decoded); if (result) { ASSERT_EQ(decoded.repairedIndexes_.size(), 2ULL); @@ -8606,7 +8705,8 @@ TEST_F(BackupTests, Easy16_Repair) auto root = prng.generateRandom(32); //encode the root - auto encoded = Armory::Seeds::BackupEasy16::encode(root.getRef(), 0); + auto encoded = Armory::Seeds::Easy16Codec::encode(root.getRef(), + Armory::Seeds::BackupType::Armory135); ASSERT_EQ(encoded.size(), 2ULL); //corrupt 2 characters in one line @@ -8629,7 +8729,7 @@ TEST_F(BackupTests, Easy16_Repair) ASSERT_NE(encoded[lineSelect], corrupted[lineSelect]); //decode, should yield an incorrect value - auto decoded = Armory::Seeds::BackupEasy16::decode(corrupted); + auto decoded = Armory::Seeds::Easy16Codec::decode(corrupted); ASSERT_EQ(decoded.checksumIndexes_.size(), 2ULL); if (lineSelect == 0) { @@ -8645,7 +8745,7 @@ TEST_F(BackupTests, Easy16_Repair) EXPECT_NE(root, decoded.data_); //attempt to repair, should fail - auto result = Armory::Seeds::BackupEasy16::repair(decoded); + auto result = Armory::Seeds::Easy16Codec::repair(decoded); if (result) { EXPECT_NE(decoded.data_, root); @@ -8659,7 +8759,8 @@ TEST_F(BackupTests, Easy16_Repair) auto root = prng.generateRandom(32); //encode the root - auto encoded = Armory::Seeds::BackupEasy16::encode(root.getRef(), 0); + auto encoded = Armory::Seeds::Easy16Codec::encode(root.getRef(), + Armory::Seeds::BackupType::Armory135); ASSERT_EQ(encoded.size(), 2ULL); //corrupt 1 character per line @@ -8678,7 +8779,7 @@ TEST_F(BackupTests, Easy16_Repair) corruptLine(corrupted, 1, wordSelect2, charSelect2, newVal2); //decode, should yield an incorrect value - auto decoded = Armory::Seeds::BackupEasy16::decode(corrupted); + auto decoded = Armory::Seeds::Easy16Codec::decode(corrupted); ASSERT_EQ(decoded.checksumIndexes_.size(), 2ULL); EXPECT_NE(decoded.checksumIndexes_[0], 0); EXPECT_NE(decoded.checksumIndexes_[1], 0); @@ -8686,7 +8787,7 @@ TEST_F(BackupTests, Easy16_Repair) //attempt to repair, may fail because of collisions (no evident solution) try { - auto result = Armory::Seeds::BackupEasy16::repair(decoded); + auto result = Armory::Seeds::Easy16Codec::repair(decoded); if (result) { ASSERT_EQ(decoded.repairedIndexes_.size(), 2ULL); @@ -8806,13 +8907,14 @@ TEST_F(BackupTests, SecurePrint) TEST_F(BackupTests, BackupStrings_Legacy) { //create a legacy wallet - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135( + Armory::Seeds::ClearTextSeed_Armory135::LegacyType::Armory135)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries auto passLbd = [](const set&)->SecureBinaryData @@ -8820,43 +8922,135 @@ TEST_F(BackupTests, BackupStrings_Legacy) return SecureBinaryData::fromString("passphrase"); }; assetWlt->setPassphrasePromptLambda(passLbd); - auto backupData = Armory::Seeds::Helpers::getWalletBackup(assetWlt); + auto backupEasy16 = dynamic_cast(backupData.get()); auto newPass = CryptoPRNG::generateRandom(10); auto newCtrl = CryptoPRNG::generateRandom(10); auto callback = [&backupData, &newPass, &newCtrl]( - const Armory::Seeds::RestorePromptType promptType, - const vector checksums, SecureBinaryData& extra)->bool + BridgeProto::RestorePrompt prompt)->BridgeProto::RestoreReply { - switch (promptType) + BridgeProto::RestoreReply reply; + switch (prompt.prompt_case()) { - case Armory::Seeds::RestorePromptType::Passphrase: + case BridgeProto::RestorePrompt::kGetPassphrases: { - extra = newPass; - return true; + auto passphrases = reply.mutable_passphrases(); + passphrases->set_privkey(newPass.toCharPtr(), newPass.getSize()); + passphrases->set_control(newCtrl.toCharPtr(), newCtrl.getSize()); + reply.set_success(true); + break; } - case Armory::Seeds::RestorePromptType::Control: + case BridgeProto::RestorePrompt::kCheckWalletId: { - extra = newCtrl; - return true; + auto checkWalleIdMsg = prompt.check_wallet_id(); + EXPECT_EQ(checkWalleIdMsg.wallet_id(), backupData->getWalletId()); + + EXPECT_EQ(checkWalleIdMsg.backup_type(), + (int)Armory::Seeds::BackupType::Armory135); + reply.set_success(true); + break; + } + + default: + reply.set_success(false); } + return reply; + }; + + string newHomeDir("./newhomedir"); + DBUtils::removeDirectory(newHomeDir); + mkdir(newHomeDir); + + string filename; + { + //restore wallet + auto backupCopy = Backup_Easy16::fromLines({ + backupEasy16->getRoot(Backup_Easy16::LineIndex::One, false), + backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, false), + }); + auto newWltPtr = Armory::Seeds::Helpers::restoreFromBackup( + move(backupCopy), newHomeDir, callback); + EXPECT_NE(newWltPtr, nullptr); - case Armory::Seeds::RestorePromptType::Id: + auto passLbd2 = [&newPass](const set&)->SecureBinaryData { - EXPECT_EQ(extra, SecureBinaryData::fromString(backupData.wltId_)); - - EXPECT_EQ(checksums.size(), 2ULL); - for (const auto& chksum : checksums) - EXPECT_EQ(chksum, 0); + return newPass; + }; + newWltPtr->setPassphrasePromptLambda(passLbd2); - return true; + auto newWalletSingle = dynamic_pointer_cast(newWltPtr); + auto backupData2 = Armory::Seeds::Helpers::getWalletBackup(newWalletSingle); + auto backupEasy16_2 = dynamic_cast(backupData2.get()); + + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::One, false), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::One, false)); + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, false), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::Two, false)); + + EXPECT_EQ(backupEasy16->getWalletId(), backupEasy16_2->getWalletId()); + + filename = newWltPtr->getDbFilename(); + } + + EXPECT_TRUE(compareWalletWithBackup(assetWlt, filename, newPass, newCtrl)); + DBUtils::removeDirectory(newHomeDir); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(BackupTests, BackupStrings_Legacy_Armory200a) +{ + //create a legacy wallet + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), + SecureBinaryData::fromString("passphrase"), + SecureBinaryData::fromString("control"), + homedir_, + 4); //set lookup computation to 4 entries + + auto passLbd = [](const set&)->SecureBinaryData + { + return SecureBinaryData::fromString("passphrase"); + }; + assetWlt->setPassphrasePromptLambda(passLbd); + auto backupData = Armory::Seeds::Helpers::getWalletBackup(assetWlt); + auto backupEasy16 = dynamic_cast(backupData.get()); + + auto newPass = CryptoPRNG::generateRandom(10); + auto newCtrl = CryptoPRNG::generateRandom(10); + auto callback = [&backupData, &newPass, &newCtrl]( + BridgeProto::RestorePrompt prompt)->BridgeProto::RestoreReply + { + BridgeProto::RestoreReply reply; + switch (prompt.prompt_case()) + { + case BridgeProto::RestorePrompt::kGetPassphrases: + { + auto passphrases = reply.mutable_passphrases(); + passphrases->set_privkey(newPass.toCharPtr(), newPass.getSize()); + passphrases->set_control(newCtrl.toCharPtr(), newCtrl.getSize()); + reply.set_success(true); + break; + } + + case BridgeProto::RestorePrompt::kCheckWalletId: + { + auto checkWalleIdMsg = prompt.check_wallet_id(); + EXPECT_EQ(checkWalleIdMsg.wallet_id(), backupData->getWalletId()); + + EXPECT_EQ(checkWalleIdMsg.backup_type(), + (int)Armory::Seeds::BackupType::Armory200a); + reply.set_success(true); + break; } default: - return false; + reply.set_success(false); } + return reply; }; string newHomeDir("./newhomedir"); @@ -8866,10 +9060,31 @@ TEST_F(BackupTests, BackupStrings_Legacy) string filename; { //restore wallet + auto backupCopy = Backup_Easy16::fromLines({ + backupEasy16->getRoot(Backup_Easy16::LineIndex::One, false), + backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, false), + }); auto newWltPtr = Armory::Seeds::Helpers::restoreFromBackup( - backupData.rootClear_, {}, newHomeDir, callback); + move(backupCopy), newHomeDir, callback); EXPECT_NE(newWltPtr, nullptr); - + + auto passLbd2 = [&newPass](const set&)->SecureBinaryData + { + return newPass; + }; + newWltPtr->setPassphrasePromptLambda(passLbd2); + + auto newWalletSingle = dynamic_pointer_cast(newWltPtr); + auto backupData2 = Armory::Seeds::Helpers::getWalletBackup(newWalletSingle); + auto backupEasy16_2 = dynamic_cast(backupData2.get()); + + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::One, false), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::One, false)); + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, false), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::Two, false)); + + EXPECT_EQ(backupEasy16->getWalletId(), backupEasy16_2->getWalletId()); + filename = newWltPtr->getDbFilename(); } @@ -8877,18 +9092,18 @@ TEST_F(BackupTests, BackupStrings_Legacy) DBUtils::removeDirectory(newHomeDir); } - //////////////////////////////////////////////////////////////////////////////// TEST_F(BackupTests, BackupStrings_Legacy_SecurePrint) { //create a legacy wallet - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135( + Armory::Seeds::ClearTextSeed_Armory135::LegacyType::Armory135)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries auto passLbd = [](const set&)->SecureBinaryData @@ -8898,42 +9113,38 @@ TEST_F(BackupTests, BackupStrings_Legacy_SecurePrint) assetWlt->setPassphrasePromptLambda(passLbd); auto backupData = Armory::Seeds::Helpers::getWalletBackup(assetWlt); + auto backupEasy16 = dynamic_cast(backupData.get()); auto newPass = CryptoPRNG::generateRandom(10); auto newCtrl = CryptoPRNG::generateRandom(10); auto callback = [&backupData, &newPass, &newCtrl]( - const Armory::Seeds::RestorePromptType promptType, - const vector checksums, SecureBinaryData& extra)->bool + BridgeProto::RestorePrompt prompt)->BridgeProto::RestoreReply { - switch (promptType) + BridgeProto::RestoreReply reply; + switch (prompt.prompt_case()) { - case Armory::Seeds::RestorePromptType::Passphrase: + case BridgeProto::RestorePrompt::kGetPassphrases: { - extra = newPass; - return true; + auto passphrases = reply.mutable_passphrases(); + passphrases->set_privkey(newPass.toCharPtr(), newPass.getSize()); + passphrases->set_control(newCtrl.toCharPtr(), newCtrl.getSize()); + reply.set_success(true); + break; } - case Armory::Seeds::RestorePromptType::Control: + case BridgeProto::RestorePrompt::kCheckWalletId: { - extra = newCtrl; - return true; - } - - case Armory::Seeds::RestorePromptType::Id: - { - if (extra != SecureBinaryData::fromString(backupData.wltId_)) - return false; - - EXPECT_EQ(checksums.size(), 2ULL); - for (const auto& chksum : checksums) - EXPECT_EQ(chksum, 0); - - return true; + auto checkWalleIdMsg = prompt.check_wallet_id(); + EXPECT_EQ(checkWalleIdMsg.backup_type(), + (int)Armory::Seeds::BackupType::Armory135); + reply.set_success(checkWalleIdMsg.wallet_id() == backupData->getWalletId()); + break; } default: - return false; + reply.set_success(false); } + return reply; }; string newHomeDir("./newhomedir"); @@ -8945,8 +9156,12 @@ TEST_F(BackupTests, BackupStrings_Legacy_SecurePrint) //try without sp pass try { + auto backupCopy = Backup_Easy16::fromLines({ + backupEasy16->getRoot(Backup_Easy16::LineIndex::One, true), + backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, true), + }); Armory::Seeds::Helpers::restoreFromBackup( - backupData.rootEncr_, {}, newHomeDir, callback); + move(backupCopy), newHomeDir, callback); ASSERT_TRUE(false); } catch (const Armory::Seeds::RestoreUserException& e) @@ -8955,10 +9170,30 @@ TEST_F(BackupTests, BackupStrings_Legacy_SecurePrint) } //try with secure print now + auto backupCopy = Backup_Easy16::fromLines({ + backupEasy16->getRoot(Backup_Easy16::LineIndex::One, true), + backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, true)}, + backupEasy16->getSpPass()); auto newWltPtr = Armory::Seeds::Helpers::restoreFromBackup( - backupData.rootEncr_, backupData.spPass_, newHomeDir, callback); + move(backupCopy), newHomeDir, callback); EXPECT_NE(newWltPtr, nullptr); - + + auto passLbd2 = [&newPass](const set&)->SecureBinaryData + { + return newPass; + }; + newWltPtr->setPassphrasePromptLambda(passLbd2); + + auto newWalletSingle = dynamic_pointer_cast(newWltPtr); + auto backupData2 = Armory::Seeds::Helpers::getWalletBackup(newWalletSingle); + auto backupEasy16_2 = dynamic_cast(backupData2.get()); + + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::One, true), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::One, true)); + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, true), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::Two, true)); + EXPECT_EQ(backupEasy16->getWalletId(), backupEasy16_2->getWalletId()); + filename = newWltPtr->getDbFilename(); } @@ -8970,7 +9205,7 @@ TEST_F(BackupTests, BackupStrings_Legacy_SecurePrint) TEST_F(BackupTests, Easy16_AutoRepair) { /*NOTE: this test will lead to a lot of hashing*/ - auto corruptLine = [](vector& lines, + auto corruptLine = [](vector& lines, uint8_t lineSelect, uint8_t wordSelect, uint8_t charSelect, uint8_t newVal) { auto& line = lines[lineSelect]; @@ -8984,7 +9219,7 @@ TEST_F(BackupTests, Easy16_AutoRepair) char newChar; while (true) { - newChar = Armory::Seeds::BackupEasy16::e16chars_[newVal % 16]; + newChar = Armory::Seeds::Easy16Codec::e16chars_[newVal % 16]; if (newChar != val) break; @@ -9003,7 +9238,15 @@ TEST_F(BackupTests, Easy16_AutoRepair) auto asset_single = make_shared( AssetId::getRootAssetId(), pubkey, nullptr); - return AssetWallet_Single::computeWalletID(derScheme, asset_single); + auto addrVec = derScheme->extendPublicChain(asset_single, 1, 1, nullptr); + if (addrVec.size() != 1) + throw runtime_error("unexpected chain derivation output"); + + auto firstEntry = dynamic_pointer_cast(addrVec[0]); + if (firstEntry == nullptr) + throw runtime_error("unexpected asset entry type"); + + return BtcUtils::computeID(firstEntry->getPubKey()->getUncompressedKey()); }; PRNG_Fortuna prng; @@ -9016,7 +9259,7 @@ TEST_F(BackupTests, Easy16_AutoRepair) auto wltID = computeWalletID(root); //encode the root - auto encoded = Armory::Seeds::BackupEasy16::encode(root.getRef(), 0); + auto encoded = Easy16Codec::encode(root.getRef(), BackupType::Armory135); ASSERT_EQ(encoded.size(), 2ULL); //corrupt one character in one line @@ -9032,7 +9275,7 @@ TEST_F(BackupTests, Easy16_AutoRepair) ASSERT_NE(encoded[lineSelect], corrupted[lineSelect]); //decode the corrupted data, should yield an incorrect value - auto decoded = Armory::Seeds::BackupEasy16::decode(corrupted); + auto decoded = Easy16Codec::decode(corrupted); ASSERT_EQ(decoded.checksumIndexes_.size(), 2ULL); if (lineSelect == 0) { @@ -9051,35 +9294,42 @@ TEST_F(BackupTests, Easy16_AutoRepair) try { auto userPrompt = [&wltID, &decoded, &succesfulRepairs]( - Armory::Seeds::RestorePromptType promptType, - const vector& chksumIndexes, - SecureBinaryData& extra)->bool + BridgeProto::RestorePrompt prompt)->BridgeProto::RestoreReply { - switch (promptType) + BridgeProto::RestoreReply reply; + switch (prompt.prompt_case()) { - case Armory::Seeds::RestorePromptType::ChecksumError: + case BridgeProto::RestorePrompt::kChecksumError: { - EXPECT_EQ(chksumIndexes, decoded.checksumIndexes_); - return false; + EXPECT_EQ(prompt.checksum_error().index(0), decoded.checksumIndexes_[0]); + EXPECT_EQ(prompt.checksum_error().index(1), decoded.checksumIndexes_[1]); + reply.set_success(false); + break; } - case Armory::Seeds::RestorePromptType::Id: + case BridgeProto::RestorePrompt::kCheckWalletId: { - EXPECT_EQ(chksumIndexes, decoded.checksumIndexes_); - string extraStr(extra.toCharPtr(), extra.getSize()); - if (extraStr == wltID) + EXPECT_EQ(prompt.check_wallet_id().backup_type(), + (int)BackupType::Armory135); + if (prompt.check_wallet_id().wallet_id() == wltID) ++succesfulRepairs; - return false; + reply.set_success(false); + break; } default: - return true; + reply.set_success(true); } + return reply; }; + auto backup = Backup_Easy16::fromLines({ + string_view(corrupted[0].toCharPtr(), corrupted[0].getSize()), + string_view(corrupted[1].toCharPtr(), corrupted[1].getSize()) + }); Armory::Seeds::Helpers::restoreFromBackup( - corrupted, BinaryDataRef(), string(), userPrompt); + move(backup), homedir_, userPrompt); } catch (const exception&) {} @@ -9092,14 +9342,14 @@ TEST_F(BackupTests, Easy16_AutoRepair) TEST_F(BackupTests, BackupStrings_LegacyWithChaincode_SecurePrint) { //create a legacy wallet - auto wltRoot = CryptoPRNG::generateRandom(32); - auto chaincode = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - wltRoot, //root as a r value - chaincode, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135( + CryptoPRNG::generateRandom(32), CryptoPRNG::generateRandom(32))); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), //root as a r value SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries auto passLbd = [](const set&)->SecureBinaryData @@ -9109,42 +9359,38 @@ TEST_F(BackupTests, BackupStrings_LegacyWithChaincode_SecurePrint) assetWlt->setPassphrasePromptLambda(passLbd); auto backupData = Armory::Seeds::Helpers::getWalletBackup(assetWlt); + auto backupEasy16 = dynamic_cast(backupData.get()); auto newPass = CryptoPRNG::generateRandom(10); auto newCtrl = CryptoPRNG::generateRandom(10); auto callback = [&backupData, &newPass, &newCtrl]( - const Armory::Seeds::RestorePromptType promptType, - const vector checksums, SecureBinaryData& extra)->bool + BridgeProto::RestorePrompt prompt)->BridgeProto::RestoreReply { - switch (promptType) + BridgeProto::RestoreReply reply; + switch (prompt.prompt_case()) { - case Armory::Seeds::RestorePromptType::Passphrase: + case BridgeProto::RestorePrompt::kGetPassphrases: { - extra = newPass; - return true; - } - - case Armory::Seeds::RestorePromptType::Control: - { - extra = newCtrl; - return true; + auto passphrases = reply.mutable_passphrases(); + passphrases->set_privkey(newPass.toCharPtr(), newPass.getSize()); + passphrases->set_control(newCtrl.toCharPtr(), newCtrl.getSize()); + reply.set_success(true); + break; } - case Armory::Seeds::RestorePromptType::Id: + case BridgeProto::RestorePrompt::kCheckWalletId: { - if (extra != SecureBinaryData::fromString(backupData.wltId_)) - return false; - - EXPECT_EQ(checksums.size(), 4ULL); - for (const auto& chksum : checksums) - EXPECT_EQ(chksum, 0); - - return true; + auto checkWalleIdMsg = prompt.check_wallet_id(); + EXPECT_EQ(checkWalleIdMsg.backup_type(), + (int)Armory::Seeds::BackupType::Armory135); + reply.set_success(checkWalleIdMsg.wallet_id() == backupData->getWalletId()); + break; } default: - return false; + reply.set_success(false); } + return reply; }; string newHomeDir("./newhomedir"); @@ -9153,21 +9399,17 @@ TEST_F(BackupTests, BackupStrings_LegacyWithChaincode_SecurePrint) string filename; { - vector rootData; - auto insertVector = [&rootData](const vector& vec) - { - for (const auto& str : vec) - rootData.emplace_back((const uint8_t*)str.c_str(), str.size()); - }; - - insertVector(backupData.rootEncr_); - insertVector(backupData.chaincodeEncr_); - //try without sp pass try { + auto backupCopy = Backup_Easy16::fromLines({ + backupEasy16->getRoot(Backup_Easy16::LineIndex::One, true), + backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, true), + backupEasy16->getChaincode(Backup_Easy16::LineIndex::One, true), + backupEasy16->getChaincode(Backup_Easy16::LineIndex::Two, true), + }); Armory::Seeds::Helpers::restoreFromBackup( - backupData.rootEncr_, {}, newHomeDir, callback); + move(backupCopy), newHomeDir, callback); ASSERT_TRUE(false); } catch (const Armory::Seeds::RestoreUserException& e) @@ -9176,10 +9418,39 @@ TEST_F(BackupTests, BackupStrings_LegacyWithChaincode_SecurePrint) } //try with secure print now + auto backupCopy = Backup_Easy16::fromLines({ + backupEasy16->getRoot(Backup_Easy16::LineIndex::One, true), + backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, true), + backupEasy16->getChaincode(Backup_Easy16::LineIndex::One, true), + backupEasy16->getChaincode(Backup_Easy16::LineIndex::Two, true)}, + backupEasy16->getSpPass() + ); auto newWltPtr = Armory::Seeds::Helpers::restoreFromBackup( - rootData, backupData.spPass_, newHomeDir, callback); + move(backupCopy), newHomeDir, callback); EXPECT_NE(newWltPtr, nullptr); - + + auto passLbd2 = [&newPass](const set&)->SecureBinaryData + { + return newPass; + }; + newWltPtr->setPassphrasePromptLambda(passLbd2); + + auto newWalletSingle = dynamic_pointer_cast(newWltPtr); + auto backupData2 = Armory::Seeds::Helpers::getWalletBackup(newWalletSingle); + auto backupEasy16_2 = dynamic_cast(backupData2.get()); + + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::One, true), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::One, true)); + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, true), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::Two, true)); + + EXPECT_EQ(backupEasy16->getChaincode(Backup_Easy16::LineIndex::One, true), + backupEasy16_2->getChaincode(Backup_Easy16::LineIndex::One, true)); + EXPECT_EQ(backupEasy16->getChaincode(Backup_Easy16::LineIndex::Two, true), + backupEasy16_2->getChaincode(Backup_Easy16::LineIndex::Two, true)); + + EXPECT_EQ(backupEasy16->getWalletId(), backupEasy16_2->getWalletId()); + filename = newWltPtr->getDbFilename(); } @@ -9191,12 +9462,14 @@ TEST_F(BackupTests, BackupStrings_LegacyWithChaincode_SecurePrint) TEST_F(BackupTests, BackupStrings_BIP32) { //create a legacy wallet - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - move(wltRoot), //root as a r value + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Structured)); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), //root as a r value SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries auto passLbd = [](const set&)->SecureBinaryData @@ -9206,41 +9479,40 @@ TEST_F(BackupTests, BackupStrings_BIP32) assetWlt->setPassphrasePromptLambda(passLbd); auto backupData = Armory::Seeds::Helpers::getWalletBackup(assetWlt); + auto backupEasy16 = dynamic_cast(backupData.get()); auto newPass = CryptoPRNG::generateRandom(10); auto newCtrl = CryptoPRNG::generateRandom(10); auto callback = [&backupData, &newPass, &newCtrl]( - const Armory::Seeds::RestorePromptType promptType, - const vector checksums, SecureBinaryData& extra)->bool + BridgeProto::RestorePrompt prompt)->BridgeProto::RestoreReply { - switch (promptType) + BridgeProto::RestoreReply reply; + switch (prompt.prompt_case()) { - case Armory::Seeds::RestorePromptType::Passphrase: + case BridgeProto::RestorePrompt::kGetPassphrases: { - extra = newPass; - return true; + auto passphrases = reply.mutable_passphrases(); + passphrases->set_privkey(newPass.toCharPtr(), newPass.getSize()); + passphrases->set_control(newCtrl.toCharPtr(), newCtrl.getSize()); + reply.set_success(true); + break; } - case Armory::Seeds::RestorePromptType::Control: + case BridgeProto::RestorePrompt::kCheckWalletId: { - extra = newCtrl; - return true; - } + auto checkWalleIdMsg = prompt.check_wallet_id(); + EXPECT_EQ(checkWalleIdMsg.wallet_id(), backupData->getWalletId()); - case Armory::Seeds::RestorePromptType::Id: - { - EXPECT_EQ(extra, SecureBinaryData::fromString(backupData.wltId_)); - - EXPECT_EQ(checksums.size(), 2U); - for (const auto& chksum : checksums) - EXPECT_EQ(chksum, 1); - - return true; + EXPECT_EQ(checkWalleIdMsg.backup_type(), + (int)Armory::Seeds::BackupType::Armory200b); + reply.set_success(true); + break; } default: - return false; + reply.set_success(false); } + return reply; }; string newHomeDir("./newhomedir"); @@ -9250,10 +9522,31 @@ TEST_F(BackupTests, BackupStrings_BIP32) string filename; { //restore wallet + auto backupCopy = Backup_Easy16::fromLines({ + backupEasy16->getRoot(Backup_Easy16::LineIndex::One, false), + backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, false), + }); auto newWltPtr = Armory::Seeds::Helpers::restoreFromBackup( - backupData.rootClear_, {}, newHomeDir, callback); - EXPECT_NE(newWltPtr, nullptr); - + move(backupCopy), newHomeDir, callback); + ASSERT_NE(newWltPtr, nullptr); + + auto passLbd2 = [&newPass](const set&)->SecureBinaryData + { + return newPass; + }; + newWltPtr->setPassphrasePromptLambda(passLbd2); + + auto newWalletSingle = dynamic_pointer_cast(newWltPtr); + auto backupData2 = Armory::Seeds::Helpers::getWalletBackup(newWalletSingle); + auto backupEasy16_2 = dynamic_cast(backupData2.get()); + + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::One, false), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::One, false)); + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, false), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::Two, false)); + + EXPECT_EQ(backupEasy16->getWalletId(), backupEasy16_2->getWalletId()); + filename = newWltPtr->getDbFilename(); } @@ -9262,15 +9555,18 @@ TEST_F(BackupTests, BackupStrings_BIP32) } //////////////////////////////////////////////////////////////////////////////// -TEST_F(BackupTests, BackupStrings_BIP32_Custom) +TEST_F(BackupTests, BackupStrings_BIP32_Virgin) { //create a legacy wallet - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromSeed_BIP32( - homedir_, - move(wltRoot), //root as a r value + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_BIP32( + Armory::Seeds::SeedType::BIP32_Virgin + )); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData::fromString("passphrase"), SecureBinaryData::fromString("control"), + homedir_, 4); //set lookup computation to 4 entries auto passLbd = [](const set&)->SecureBinaryData @@ -9279,43 +9575,42 @@ TEST_F(BackupTests, BackupStrings_BIP32_Custom) }; assetWlt->setPassphrasePromptLambda(passLbd); - auto backupData = Armory::Seeds::Helpers::getWalletBackup( - assetWlt, Armory::Seeds::BackupType::BIP32_Seed_Virgin); + auto backupData = Armory::Seeds::Helpers::getWalletBackup(assetWlt); + auto backupEasy16 = dynamic_cast( + backupData.get()); auto newPass = CryptoPRNG::generateRandom(10); auto newCtrl = CryptoPRNG::generateRandom(10); auto callback = [&backupData, &newPass, &newCtrl]( - const Armory::Seeds::RestorePromptType promptType, - const vector checksums, SecureBinaryData& extra)->bool + BridgeProto::RestorePrompt prompt)->BridgeProto::RestoreReply { - switch (promptType) - { - case Armory::Seeds::RestorePromptType::Passphrase: + BridgeProto::RestoreReply reply; + switch (prompt.prompt_case()) { - extra = newPass; - return true; - } - - case Armory::Seeds::RestorePromptType::Control: + case BridgeProto::RestorePrompt::kGetPassphrases: { - extra = newCtrl; - return true; + auto passphrases = reply.mutable_passphrases(); + passphrases->set_privkey(newPass.toCharPtr(), newPass.getSize()); + passphrases->set_control(newCtrl.toCharPtr(), newCtrl.getSize()); + reply.set_success(true); + break; } - case Armory::Seeds::RestorePromptType::Id: + case BridgeProto::RestorePrompt::kCheckWalletId: { - EXPECT_EQ(extra, SecureBinaryData::fromString(backupData.wltId_)); - - EXPECT_EQ(checksums.size(), 2U); - for (const auto& chksum : checksums) - EXPECT_EQ(chksum, 15); + auto checkWalleIdMsg = prompt.check_wallet_id(); + EXPECT_EQ(checkWalleIdMsg.wallet_id(), backupData->getWalletId()); - return true; + EXPECT_EQ(checkWalleIdMsg.backup_type(), + (int)Armory::Seeds::BackupType::Armory200c); + reply.set_success(true); + break; } default: - return false; + reply.set_success(false); } + return reply; }; string newHomeDir("./newhomedir"); @@ -9323,10 +9618,14 @@ TEST_F(BackupTests, BackupStrings_BIP32_Custom) mkdir(newHomeDir); //restore wallet + auto backupCopy = Backup_Easy16::fromLines({ + backupEasy16->getRoot(Backup_Easy16::LineIndex::One, false), + backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, false), + }); auto newWltPtr = Armory::Seeds::Helpers::restoreFromBackup( - backupData.rootClear_, {}, newHomeDir, callback); - EXPECT_NE(newWltPtr, nullptr); - + move(backupCopy), newHomeDir, callback); + ASSERT_NE(newWltPtr, nullptr); + //check wallet id EXPECT_EQ(assetWlt->getID(), newWltPtr->getID()); @@ -9334,9 +9633,112 @@ TEST_F(BackupTests, BackupStrings_BIP32_Custom) auto loadedIDs = newWltPtr->getAccountIDs(); EXPECT_EQ(loadedIDs.size(), 0ULL); + auto passLbd2 = [&newPass](const set&)->SecureBinaryData + { + return newPass; + }; + newWltPtr->setPassphrasePromptLambda(passLbd2); + + auto newWalletSingle = dynamic_pointer_cast(newWltPtr); + auto backupData2 = Armory::Seeds::Helpers::getWalletBackup(newWalletSingle); + auto backupEasy16_2 = dynamic_cast( + backupData2.get()); + + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::One, false), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::One, false)); + EXPECT_EQ(backupEasy16->getRoot(Backup_Easy16::LineIndex::Two, false), + backupEasy16_2->getRoot(Backup_Easy16::LineIndex::Two, false)); + + EXPECT_EQ(backupEasy16->getWalletId(), backupEasy16_2->getWalletId()); + DBUtils::removeDirectory(newHomeDir); } +//////////////////////////////////////////////////////////////////////////////// +TEST_F(BackupTests, BackupStrings_BIP32_FromBase58) +{ + auto b58seed = string_view{ + "tprv8ZgxMBicQKsPd9TeAdPADNnSyH9SSUUbTVeFszDE23Ki6TBB5nCefAdHkK8Fm3qMQR6sHwA56zqRmKmxnHk37JkiFzvncDqoKmPWubu7hDF"}; + + auto newPass = CryptoPRNG::generateRandom(10); + auto newCtrl = CryptoPRNG::generateRandom(10); + auto callback = [&newPass, &newCtrl]( + BridgeProto::RestorePrompt prompt)->BridgeProto::RestoreReply + { + BridgeProto::RestoreReply reply; + switch (prompt.prompt_case()) + { + case BridgeProto::RestorePrompt::kGetPassphrases: + { + auto passphrases = reply.mutable_passphrases(); + passphrases->set_privkey(newPass.toCharPtr(), newPass.getSize()); + passphrases->set_control(newCtrl.toCharPtr(), newCtrl.getSize()); + reply.set_success(true); + break; + } + + case BridgeProto::RestorePrompt::kCheckWalletId: + { + auto checkWalleIdMsg = prompt.check_wallet_id(); + EXPECT_EQ(checkWalleIdMsg.wallet_id(), "poUtmfmp"); + + EXPECT_EQ(checkWalleIdMsg.backup_type(), (int)BackupType::Base58); + reply.set_success(true); + break; + } + + default: + reply.set_success(false); + } + return reply; + }; + + //create bip32 wallet from xpriv, check it yields same xpriv + string filename; + { + auto backup = Backup_Base58::fromString(b58seed); + auto wallet = Helpers::restoreFromBackup( + move(backup), homedir_, callback); + ASSERT_NE(wallet, nullptr); + + auto passLbd = [newPass](const set&)->SecureBinaryData + { + return newPass; + }; + wallet->setPassphrasePromptLambda(passLbd); + + auto walletSingle = dynamic_pointer_cast(wallet); + auto backupData = Helpers::getWalletBackup(walletSingle); + auto backupBase58 = dynamic_cast(backupData.get()); + EXPECT_EQ(backupBase58->getBase58String(), b58seed); + filename = wallet->getDbFilename(); + } + + //load wallet from file and check xpriv again + { + auto controlPassLbd = [&newCtrl]( + const set&)->SecureBinaryData + { + return newCtrl; + }; + + //load it, newCtrl should work for the control passphrase + auto loadedWlt = AssetWallet::loadMainWalletFromFile( + filename, controlPassLbd); + ASSERT_NE(loadedWlt, nullptr); + + auto passLbd = [newPass](const set&)->SecureBinaryData + { + return newPass; + }; + loadedWlt->setPassphrasePromptLambda(passLbd); + + auto walletSingle = dynamic_pointer_cast(loadedWlt); + auto backupData = Helpers::getWalletBackup(walletSingle); + auto backupBase58 = dynamic_cast(backupData.get()); + EXPECT_EQ(backupBase58->getBase58String(), b58seed); + } +} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// diff --git a/cppForSwig/gtest/ZeroConfTests.cpp b/cppForSwig/gtest/ZeroConfTests.cpp index 792b918981..a3834aa113 100644 --- a/cppForSwig/gtest/ZeroConfTests.cpp +++ b/cppForSwig/gtest/ZeroConfTests.cpp @@ -12,6 +12,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "TestUtils.h" +#include "../Wallets/Seeds/Seeds.h" using namespace std; using namespace Armory::Signer; using namespace Armory::Config; @@ -2364,15 +2365,15 @@ TEST_F(ZeroConfTests_FullNode, Replace_ZC_Test) //// create assetWlt //// //create a root private key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData(), SecureBinaryData(), + homedir_, 10); //set lookup computation to 5 entries - + //register with db vector addrVec; @@ -2877,13 +2878,13 @@ TEST_F(ZeroConfTests_FullNode, RegisterAddress_AfterZC) //// create assetWlt //// //create a root private key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData(), SecureBinaryData(), + homedir_, 3); //set lookup computation to 3 entries //register with db @@ -3095,14 +3096,14 @@ TEST_F(ZeroConfTests_FullNode, ChainZC_RBFchild_Test) //// create assetWlt //// //create a root private key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData(), - SecureBinaryData(), - 10); //set lookup computation to 10 entries + SecureBinaryData(), + homedir_, + 10); //set lookup computation to 3 entries //register with db vector addrVec; @@ -3576,14 +3577,14 @@ TEST_F(ZeroConfTests_FullNode, TwoZC_CheckLedgers) //// create assetWlt //// //create a root private key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), - {}, - SecureBinaryData(), //empty passphrase + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), + SecureBinaryData(), SecureBinaryData(), - 5); + homedir_, + 5); //set lookup computation to 3 entries //register with db vector addrVec; @@ -4375,13 +4376,13 @@ TEST_F(ZeroConfTests_Supernode, ZC_Reorg) theBDMt_->start(DBSettings::initMode()); auto&& bdvID = DBTestUtils::registerBDV(clients_, BitcoinSettings::getMagicBytes()); - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a rvalue - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData(), SecureBinaryData(), + homedir_, 3); //set lookup computation to 3 entries auto addr1_ptr = assetWlt->getNewAddress(); auto addr2_ptr = assetWlt->getNewAddress(); @@ -4546,14 +4547,14 @@ TEST_F(ZeroConfTests_Supernode, ChainZC_RBFchild_Test) //// create assetWlt //// //create a root private key - auto&& wltRoot = CryptoPRNG::generateRandom(32); - auto assetWlt = AssetWallet_Single::createFromPrivateRoot_Armory135( - homedir_, - move(wltRoot), //root as a r value - {}, + unique_ptr seed( + new Armory::Seeds::ClearTextSeed_Armory135()); + auto assetWlt = AssetWallet_Single::createFromSeed( + move(seed), SecureBinaryData(), - SecureBinaryData(), - 10); //set lookup computation to 5 entries + SecureBinaryData(), + homedir_, + 10); //set lookup computation to 3 entries //register with db vector addrVec; diff --git a/cppForSwig/protobuf/BridgeProto.proto b/cppForSwig/protobuf/BridgeProto.proto index 04ada39724..abbd439417 100644 --- a/cppForSwig/protobuf/BridgeProto.proto +++ b/cppForSwig/protobuf/BridgeProto.proto @@ -11,32 +11,47 @@ message RestoreWalletPayload optional string spPass = 3; } -enum RestorePromptType -{ - FormatError = 1; - Failure = 2; - ChecksumError = 3; - DecryptError = 4; - Passphrase = 5; - Control = 6; - Id = 7; - TypeError = 8; - Success = 9; - UnknownError = 10; -} - message RestorePrompt { + message TypeError + { + required string error = 1; + } + + message ChecksumIndexes + { + repeated int32 index = 1; + } + + message CheckWalletId + { + required string wallet_id = 1; + required int32 backup_type = 2; + } + + oneof prompt { + CheckWalletId check_wallet_id = 10; + bool get_passphrases = 11; + bool decrypt_error = 12; - required RestorePromptType promptType = 1; - repeated int32 checksums = 2; - optional string extra = 3; + TypeError type_error = 20; + ChecksumIndexes checksum_error = 21; + ChecksumIndexes checksum_mismatch = 22; + } } message RestoreReply { - required bool result = 1; - optional bytes extra = 2; + message Passphrases + { + required string control = 1; + required string privkey = 2; + } + + required bool success = 1; + oneof reply { + Passphrases passphrases = 11; + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/qtdialogs/DlgRestore.py b/qtdialogs/DlgRestore.py index a63090efc4..2f535684bf 100644 --- a/qtdialogs/DlgRestore.py +++ b/qtdialogs/DlgRestore.py @@ -160,7 +160,7 @@ def __init__(self, parent, main, thisIsATest=False, expectWltID=None): self.setWindowTitle(self.tr('Test Single-Sheet Backup')) else: self.setWindowTitle(self.tr('Restore Single-Sheet Backup')) - self.connect(self.chkEncrypt, SIGNAL("clicked()"), self.onEncryptCheckboxChange) + self.chkEncrypt.clicked.connect(self.onEncryptCheckboxChange) self.setMinimumWidth(500) self.layout().setSizeConstraint(QLayout.SetFixedSize) @@ -469,8 +469,8 @@ def __init__(self, parent, main, thisIsATest=False, expectWltID=None): self.btnAddFrag = QPushButton(self.tr('+Frag')) self.btnRmFrag = QPushButton(self.tr('-Frag')) self.btnRmFrag.setVisible(False) - self.connect(self.btnAddFrag, SIGNAL(CLICKED), self.addFragment) - self.connect(self.btnRmFrag, SIGNAL(CLICKED), self.removeFragment) + self.btnAddFrag.clicked.connect(self.addFragment) + self.btnRmFrag.clicked.connect(self.removeFragment) self.chkEncrypt = QCheckBox(self.tr('Encrypt Restored Wallet')) self.chkEncrypt.setChecked(True) frmAddRm = makeHorizFrame([self.chkEncrypt, STRETCH, self.btnRmFrag, self.btnAddFrag]) @@ -484,8 +484,8 @@ def __init__(self, parent, main, thisIsATest=False, expectWltID=None): btnExit = QPushButton(self.tr('Cancel')) self.btnRestore = QPushButton(doItText) - self.connect(btnExit, SIGNAL(CLICKED), self.reject) - self.connect(self.btnRestore, SIGNAL(CLICKED), self.processFrags) + btnExit.clicked.connect(self.reject) + self.btnRestore.clicked.connect(self.processFrags) frmBtns = makeHorizFrame([btnExit, STRETCH, self.btnRestore]) self.lblRightFrm = QRichLabel('', hAlign=Qt.AlignHCenter) @@ -538,7 +538,7 @@ def __init__(self, parent, main, thisIsATest=False, expectWltID=None): self.chkEncrypt.setVisible(not thisIsATest) self.advancedOptionsTab.setEnabled(not thisIsATest) if not thisIsATest: - self.connect(self.chkEncrypt, SIGNAL(CLICKED), self.onEncryptCheckboxChange) + self.chkEncrypt.clicked.connect(self.onEncryptCheckboxChange) layout = QVBoxLayout() layout.addWidget(walletRestoreTabs) @@ -578,12 +578,9 @@ def makeFragInputTable(self, addCount=0): lblFragID.setText('' + fid + '', color='TextWarn') - self.connect(btnEnter, SIGNAL(CLICKED), \ - functools.partial(self.dataEnter, fnum=i)) - self.connect(btnLoad, SIGNAL(CLICKED), \ - functools.partial(self.dataLoad, fnum=i)) - self.connect(btnClear, SIGNAL(CLICKED), \ - functools.partial(self.dataClear, fnum=i)) + btnEnter.clicked.connect(functools.partial(self.dataEnter, fnum=i)) + btnLoad.clicked.connect(functools.partial(self.dataLoad, fnum=i)) + btnClear.clicked.connect(functools.partial(self.dataClear, fnum=i)) newLayout.addWidget(btnEnter, 2 * i + 1, 0) @@ -1025,7 +1022,7 @@ def __init__(self, parent, main, fragList=[], wltType=UNKNOWN, securePrintCode=N self.backupTypeButtonGroup.addButton(self.version135cButton) self.backupTypeButtonGroup.addButton(self.version135cSPButton) self.version135cButton.setChecked(True) - self.connect(self.backupTypeButtonGroup, SIGNAL('buttonClicked(int)'), self.changeType) + self.backupTypeButtonGroup.buttonClicked.connect(self.changeType) # This value will be locked after the first fragment is entered. if wltType == UNKNOWN: @@ -1104,8 +1101,8 @@ def __init__(self, parent, main, fragList=[], wltType=UNKNOWN, securePrintCode=N self.btnAccept = QPushButton(self.tr("Done")) self.btnCancel = QPushButton(self.tr("Cancel")) - self.connect(self.btnAccept, SIGNAL(CLICKED), self.verifyUserInput) - self.connect(self.btnCancel, SIGNAL(CLICKED), self.reject) + self.btnAccept.clicked.connect(self.verifyUserInput) + self.btnCancel.clicked.connect(self.reject) buttonBox = QDialogButtonBox() buttonBox.addButton(self.btnAccept, QDialogButtonBox.AcceptRole) buttonBox.addButton(self.btnCancel, QDialogButtonBox.RejectRole) @@ -1291,9 +1288,9 @@ def __init__(self, parent, main, thisIsATest=False, expectWltID=None): self.btnLoad = QPushButton(self.tr("Load From Text File")) self.btnAccept = QPushButton(doItText) self.btnCancel = QPushButton(self.tr("Cancel")) - self.connect(self.btnLoad, SIGNAL("clicked()"), self.loadWODataFile) - self.connect(self.btnAccept, SIGNAL("clicked()"), self.verifyUserInput) - self.connect(self.btnCancel, SIGNAL("clicked()"), self.reject) + self.btnLoad.clicked.connect(self.loadWODataFile) + self.btnAccept.clicked.connect(self.verifyUserInput) + self.btnCancel.clicked.connect(self.reject) buttonBox = QDialogButtonBox() buttonBox.addButton(self.btnLoad, QDialogButtonBox.AcceptRole) buttonBox.addButton(self.btnAccept, QDialogButtonBox.AcceptRole) @@ -1490,8 +1487,8 @@ def __init__(self, parent, main): self.btnAccept = QPushButton(self.tr("Done")) self.btnCancel = QPushButton(self.tr("Cancel")) - self.connect(self.btnAccept, SIGNAL(CLICKED), self.verifySecurePrintCode) - self.connect(self.btnCancel, SIGNAL(CLICKED), self.reject) + self.btnAccept.clicked.connect(self.verifySecurePrintCode) + self.btnCancel.clicked.connect(self.reject) buttonBox = QDialogButtonBox() buttonBox.addButton(self.btnAccept, QDialogButtonBox.AcceptRole) buttonBox.addButton(self.btnCancel, QDialogButtonBox.RejectRole) diff --git a/qtdialogs/DlgUniversalRestoreSelect.py b/qtdialogs/DlgUniversalRestoreSelect.py index 6e74b8427b..84dde4f1ae 100644 --- a/qtdialogs/DlgUniversalRestoreSelect.py +++ b/qtdialogs/DlgUniversalRestoreSelect.py @@ -21,7 +21,7 @@ ################################################################################ class DlgUniversalRestoreSelect(ArmoryDialog): - ############################################################################# + ############################################################################# def __init__(self, parent, main): super(DlgUniversalRestoreSelect, self).__init__(parent, main)