1
0
Fork 0
forked from lthn/blockchain

Merge branch 'develop' into release

This commit is contained in:
cryptozoidberg 2020-12-09 21:31:48 +01:00
commit 9b5b61d2df
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
64 changed files with 2644 additions and 485 deletions

View file

@ -3379,6 +3379,12 @@ namespace tools
return wordsArray[n];
}
bool valid_word(const std::string& w)
{
auto it = wordsMap.find(w);
return it != wordsMap.end();
}
uint64_t num_by_word(const std::string& w)
{
auto it = wordsMap.find(w);

View file

@ -46,5 +46,6 @@ namespace tools
std::string binary2text(const std::vector<unsigned char>& binary);
std::string word_by_num(uint32_t n);
uint64_t num_by_word(const std::string& w);
bool valid_word(const std::string& w);
}
}

View file

@ -60,18 +60,42 @@ namespace currency
return m_keys;
}
//-----------------------------------------------------------------
std::string account_base::get_seed_phrase() const
void crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password)
{
crypto::chacha8_key key = AUTO_VAL_INIT(key);
crypto::generate_chacha8_key(password, key);
crypto::hash pass_hash = crypto::cn_fast_hash(password.data(), password.size());
crypto::chacha8_iv iv = AUTO_VAL_INIT(iv);
CHECK_AND_ASSERT_THROW_MES(sizeof(pass_hash) >= sizeof(iv), "Invalid configuration: hash size is less than keys_file_data.iv");
iv = *((crypto::chacha8_iv*)&pass_hash);
crypto::chacha8(scr_data, src_length, key, iv, (char*)dst_data);
}
std::string account_base::get_seed_phrase(const std::string& password) const
{
if (m_keys_seed_binary.empty())
return "";
std::string keys_seed_text = tools::mnemonic_encoding::binary2text(m_keys_seed_binary);
std::string timestamp_word = currency::get_word_from_timstamp(m_creation_timestamp);
std::vector<unsigned char> processed_seed_binary = m_keys_seed_binary;
if (!password.empty())
{
//encrypt seed phrase binary data
crypt_with_pass(&m_keys_seed_binary[0], m_keys_seed_binary.size(), &processed_seed_binary[0], password);
}
std::string keys_seed_text = tools::mnemonic_encoding::binary2text(processed_seed_binary);
std::string timestamp_word = currency::get_word_from_timstamp(m_creation_timestamp, !password.empty());
// floor creation time to WALLET_BRAIN_DATE_QUANTUM to make checksum calculation stable
uint64_t creation_timestamp_rounded = get_timstamp_from_word(timestamp_word);
bool self_check_is_password_used = false;
uint64_t creation_timestamp_rounded = get_timstamp_from_word(timestamp_word, self_check_is_password_used);
CHECK_AND_ASSERT_THROW_MES(self_check_is_password_used == !password.empty(), "Account seed phrase internal error: password flag encoded wrong");
constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum
crypto::hash h = crypto::cn_fast_hash(m_keys_seed_binary.data(), m_keys_seed_binary.size());
std::string binary_for_check_sum((const char*)&m_keys_seed_binary[0], m_keys_seed_binary.size());
binary_for_check_sum.append(password);
crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size());
*reinterpret_cast<uint64_t*>(&h) = creation_timestamp_rounded;
h = crypto::cn_fast_hash(&h, sizeof h);
uint64_t h_64 = *reinterpret_cast<uint64_t*>(&h);
@ -104,7 +128,7 @@ namespace currency
return true;
}
//-----------------------------------------------------------------
bool account_base::restore_from_seed_phrase(const std::string& seed_phrase)
bool account_base::restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password)
{
//cut the last timestamp word from restore_dats
std::list<std::string> words;
@ -135,12 +159,39 @@ namespace currency
uint64_t auditable_flag_and_checksum = UINT64_MAX;
if (!auditable_flag_and_checksum_word.empty())
auditable_flag_and_checksum = tools::mnemonic_encoding::num_by_word(auditable_flag_and_checksum_word);
{
try {
auditable_flag_and_checksum = tools::mnemonic_encoding::num_by_word(auditable_flag_and_checksum_word);
}
catch (...)
{
return false;
}
}
std::vector<unsigned char> keys_seed_binary = tools::mnemonic_encoding::text2binary(keys_seed_text);
CHECK_AND_ASSERT_MES(keys_seed_binary.size(), false, "text2binary failed to convert the given text"); // don't prints event incorrect seed into the log for security
std::vector<unsigned char> keys_seed_processed_binary = keys_seed_binary;
m_creation_timestamp = get_timstamp_from_word(timestamp_word);
bool has_password = false;
try {
m_creation_timestamp = get_timstamp_from_word(timestamp_word, has_password);
}
catch (...)
{
return false;
}
//double check is password setting from timestamp word match with passed parameters
CHECK_AND_ASSERT_MES(has_password != seed_password.empty(), false, "Seed phrase password wrong interpretation");
if (has_password)
{
CHECK_AND_ASSERT_MES(!seed_password.empty(), false, "Seed phrase password wrong interpretation: internal error");
crypt_with_pass(&keys_seed_binary[0], keys_seed_binary.size(), &keys_seed_processed_binary[0], seed_password);
}
CHECK_AND_ASSERT_MES(keys_seed_processed_binary.size(), false, "text2binary failed to convert the given text"); // don't prints event incorrect seed into the log for security
bool auditable_flag = false;
@ -150,18 +201,24 @@ namespace currency
auditable_flag = (auditable_flag_and_checksum & 1) != 0; // auditable flag is the lower 1 bit
uint16_t checksum = static_cast<uint16_t>(auditable_flag_and_checksum >> 1); // checksum -- everything else
constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum
crypto::hash h = crypto::cn_fast_hash(keys_seed_binary.data(), keys_seed_binary.size());
std::string binary_for_check_sum((const char*)&keys_seed_processed_binary[0], keys_seed_processed_binary.size());
binary_for_check_sum.append(seed_password);
crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size());
*reinterpret_cast<uint64_t*>(&h) = m_creation_timestamp;
h = crypto::cn_fast_hash(&h, sizeof h);
uint64_t h_64 = *reinterpret_cast<uint64_t*>(&h);
uint16_t checksum_calculated = h_64 % (checksum_max + 1);
CHECK_AND_ASSERT_MES(checksum == checksum_calculated, false, "seed phase has invalid checksum: " << checksum_calculated << ", while " << checksum << " is expected, check your words");
if (checksum != checksum_calculated)
{
LOG_PRINT_L0("seed phase has invalid checksum: " << checksum_calculated << ", while " << checksum << " is expected, check your words");
return false;
}
}
bool r = restore_keys(keys_seed_binary);
bool r = restore_keys(keys_seed_processed_binary);
CHECK_AND_ASSERT_MES(r, false, "restore_keys failed");
m_keys_seed_binary = keys_seed_binary;
m_keys_seed_binary = keys_seed_processed_binary;
if (auditable_flag)
m_keys.account_address.flags |= ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE;
@ -169,6 +226,41 @@ namespace currency
return true;
}
//-----------------------------------------------------------------
bool account_base::is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected)
{
//cut the last timestamp word from restore_dats
std::list<std::string> words;
boost::split(words, seed_phrase, boost::is_space());
//let's validate each word
for (const auto& w: words)
{
if (!tools::mnemonic_encoding::valid_word(w))
return false;
}
std::string timestamp_word;
if (words.size() == SEED_PHRASE_V1_WORDS_COUNT)
{
// 24 seed words + one timestamp word = 25 total
timestamp_word = words.back();
}
else if (words.size() == SEED_PHRASE_V2_WORDS_COUNT)
{
// 24 seed words + one timestamp word + one flags & checksum = 26 total
words.erase(--words.end());
timestamp_word = words.back();
}
else
{
return false;
}
get_timstamp_from_word(timestamp_word, is_password_protected);
return true;
}
//-----------------------------------------------------------------
bool account_base::restore_from_tracking_seed(const std::string& tracking_seed)
{
set_null();

View file

@ -53,9 +53,9 @@ namespace currency
const account_public_address& get_public_address() const { return m_keys.account_address; };
std::string get_public_address_str() const;
std::string get_seed_phrase() const;
std::string get_seed_phrase(const std::string& seed_password) const;
std::string get_tracking_seed() const;
bool restore_from_seed_phrase(const std::string& seed_phrase);
bool restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password);
bool restore_from_tracking_seed(const std::string& tracking_seed);
uint64_t get_createtime() const { return m_creation_timestamp; }
@ -78,6 +78,7 @@ namespace currency
static std::string vector_of_chars_to_string(const std::vector<unsigned char>& v) { return std::string(v.begin(), v.end()); }
static std::vector<unsigned char> string_to_vector_of_chars(const std::string& v) { return std::vector<unsigned char>(v.begin(), v.end()); }
static bool is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected);
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_keys)

View file

@ -150,9 +150,10 @@
#define WALLET_FILE_MAX_KEYS_SIZE 10000 //
#define WALLET_BRAIN_DATE_OFFSET 1543622400
#define WALLET_BRAIN_DATE_QUANTUM 604800 //by last word we encode a number of week since launch of the project,
#define WALLET_BRAIN_DATE_QUANTUM 604800 //by last word we encode a number of week since launch of the project AND password flag,
//which let us to address tools::mnemonic_encoding::NUMWORDS weeks after project launch
//which is about 30 years
//which is about 15 years
#define WALLET_BRAIN_DATE_MAX_WEEKS_COUNT 800
#define OFFER_MAXIMUM_LIFE_TIME (60*60*24*30) // 30 days

View file

@ -1316,21 +1316,34 @@ namespace currency
return reward;
}
//---------------------------------------------------------------
std::string get_word_from_timstamp(uint64_t timestamp)
std::string get_word_from_timstamp(uint64_t timestamp, bool use_password)
{
uint64_t date_offset = timestamp > WALLET_BRAIN_DATE_OFFSET ? timestamp - WALLET_BRAIN_DATE_OFFSET : 0;
uint64_t weeks_count = date_offset / WALLET_BRAIN_DATE_QUANTUM;
CHECK_AND_ASSERT_THROW_MES(weeks_count < WALLET_BRAIN_DATE_MAX_WEEKS_COUNT, "SEED PHRASE need to be extended or refactored");
if (use_password)
weeks_count += WALLET_BRAIN_DATE_MAX_WEEKS_COUNT;
CHECK_AND_ASSERT_THROW_MES(weeks_count < std::numeric_limits<uint32_t>::max(), "internal error: unable to convert to uint32, val = " << weeks_count);
uint32_t weeks_count_32 = static_cast<uint32_t>(weeks_count);
return tools::mnemonic_encoding::word_by_num(weeks_count_32);
}
//---------------------------------------------------------------
uint64_t get_timstamp_from_word(std::string word)
uint64_t get_timstamp_from_word(std::string word, bool& password_used)
{
uint64_t count_of_weeks = tools::mnemonic_encoding::num_by_word(word);
if (count_of_weeks >= WALLET_BRAIN_DATE_MAX_WEEKS_COUNT)
{
count_of_weeks -= WALLET_BRAIN_DATE_MAX_WEEKS_COUNT;
password_used = true;
}
else {
password_used = false;
}
uint64_t timestamp = count_of_weeks * WALLET_BRAIN_DATE_QUANTUM + WALLET_BRAIN_DATE_OFFSET;
return timestamp;
}
//---------------------------------------------------------------

View file

@ -291,8 +291,8 @@ namespace currency
bool fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short = false);
bool fill_block_rpc_details(block_rpc_extended_info& pei_rpc, const block_extended_info& bei_chain, const crypto::hash& h);
void append_per_block_increments_for_tx(const transaction& tx, std::unordered_map<uint64_t, uint32_t>& gindices);
std::string get_word_from_timstamp(uint64_t timestamp);
uint64_t get_timstamp_from_word(std::string word);
std::string get_word_from_timstamp(uint64_t timestamp, bool use_password);
uint64_t get_timstamp_from_word(std::string word, bool& password_used);
template<class t_txin_v>
typename std::conditional<std::is_const<t_txin_v>::value, const std::vector<txin_etc_details_v>, std::vector<txin_etc_details_v> >::type& get_txin_etc_options(t_txin_v& in)

View file

@ -4,9 +4,7 @@
<dict>
<key>LSEnvironment</key>
<dict>
<key>QTWEBENGINE_REMOTE_DEBUGGING</key>
<string>11113</string>
</dict>
</dict>
<key>BuildMachineOSBuild</key>
<string>14E46</string>
<key>CFBundleDevelopmentRegion</key>

View file

@ -1676,7 +1676,7 @@ QString MainWindow::restore_wallet(const QString& param)
//return que_call2<view::restore_wallet_request>("restore_wallet", param, [this](const view::restore_wallet_request& owd, view::api_response& ar){
PREPARE_ARG_FROM_JSON(view::restore_wallet_request, owd);
PREPARE_RESPONSE(view::open_wallet_response, ar);
ar.error_code = m_backend.restore_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.restore_key, ar.response_data);
ar.error_code = m_backend.restore_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.seed_phrase, owd.seed_pass, ar.response_data);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
@ -1877,9 +1877,9 @@ QString MainWindow::get_smart_wallet_info(const QString& param)
{
TRY_ENTRY();
LOG_API_TIMING();
PREPARE_ARG_FROM_JSON(view::wallet_id_obj, wo);
PREPARE_ARG_FROM_JSON(view::request_get_smart_wallet_info, wo);
PREPARE_RESPONSE(view::get_restore_info_response, ar);
ar.error_code = m_backend.get_wallet_restore_info(wo.wallet_id, ar.response_data.restore_key);
ar.error_code = m_backend.get_wallet_restore_info(wo.wallet_id, ar.response_data.seed_phrase, wo.seed_password);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
@ -1973,14 +1973,27 @@ QString MainWindow::open_url_in_browser(const QString& param)
CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR);
}
QString MainWindow::is_valid_restore_wallet_text(const QString& param)
{
TRY_ENTRY();
LOG_API_TIMING();
return m_backend.is_valid_brain_restore_data(param.toStdString()).c_str();
PREPARE_ARG_FROM_JSON(view::seed_info_param, rwtp);
return m_backend.is_valid_brain_restore_data(rwtp.seed_phrase, rwtp.seed_password).c_str();
CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR);
}
QString MainWindow::get_seed_phrase_info(const QString& param)
{
TRY_ENTRY();
LOG_API_TIMING();
PREPARE_ARG_FROM_JSON(view::seed_info_param, rwtp);
PREPARE_RESPONSE(view::seed_phrase_info, ar);
ar.error_code = m_backend.get_seed_phrase_info(rwtp.seed_phrase, rwtp.seed_password, ar.response_data).c_str();
LOG_PRINT_CYAN("[get_seed_phrase_info]:" << epee::serialization::store_t_to_json(ar), LOG_LEVEL_0);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
void MainWindow::contextMenuEvent(QContextMenuEvent * event)
{
TRY_ENTRY();

View file

@ -137,6 +137,7 @@ public:
QString is_autostart_enabled();
QString toggle_autostart(const QString& param);
QString is_valid_restore_wallet_text(const QString& param);
QString get_seed_phrase_info(const QString& param);
QString print_text(const QString& param);
QString print_log(const QString& param);
QString set_clipboard(const QString& param);

View file

@ -120,13 +120,16 @@
"BUTTON_CREATE": "Create wallet",
"NOT_CORRECT_FILE_OR_PASSWORD": "Invalid wallet file or password does not match",
"CHOOSE_PATH": "Please choose a path",
"SEED_PASSWORD": "Seed password",
"OK": "OK",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required",
"NAME_DUPLICATE": "Name is duplicate",
"MAX_LENGTH": "Maximum name length reached",
"CONFIRM_NOT_MATCH": "Confirm password not match",
"KEY_REQUIRED": "Key is required",
"KEY_NOT_VALID": "Key not valid"
"KEY_NOT_VALID": "Key not valid",
"INCORRECT_PASSWORD": "Incorrect password"
}
},
"SEED_PHRASE": {
@ -214,10 +217,21 @@
"SEED_PHRASE_HINT": "Click to reveal the seed phrase",
"BUTTON_SAVE": "Save",
"BUTTON_REMOVE": "Close wallet",
"CREATE_PASSWORD_SECURE": "Create a password to secure your seed",
"INFO": "info",
"SEED_IS_UNSECURED": "Seed is unsecured",
"SEED_IS_SECURED": "Seed is secured",
"REMEMBER_YOU_WILL_REQUIRE": "Remember, you will require the password to restore it.",
"FORM": {
"CONFIRM_PASSWORD": "Confirm password",
"GENERATE_SECURE_SEED": "Generate Secure Seed",
"SECURED_SEED_WILL_REQUIRE": "Secured seed will require this password to restore."
},
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required",
"NAME_DUPLICATE": "Name is duplicate",
"MAX_LENGTH": "Maximum name length reached"
"MAX_LENGTH": "Maximum name length reached",
"PASSWORDS_DONT_MATCH": "Passwords don't match"
}
},
"ASSIGN_ALIAS": {

View file

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{fill:#fff;}</style></defs><title>info</title><path class="cls-1" d="M192,42.05476A149.94522,149.94522,0,0,1,298.02728,298.02728,149.94522,149.94522,0,1,1,85.97271,85.97271,148.96374,148.96374,0,0,1,192,42.05476M192,0C85.96132,0,0,85.96132,0,192S85.96132,384,192,384s192-85.96132,192-192S298.03868,0,192,0Z"/><rect class="cls-1" x="171" y="247.94524" width="42" height="42"/><rect class="cls-1" x="171" y="94.05476" width="42" height="120"/></svg>

After

Width:  |  Height:  |  Size: 561 B

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 320 384" style="enable-background:new 0 0 320 384;" xml:space="preserve">
<style type="text/css">
.st0{clip-path:url(#SVGID_2_);}
.st1{clip-path:url(#SVGID_2_);fill:none;stroke:#000000;stroke-width:42;stroke-miterlimit:10;}
</style>
<g>
<defs>
<rect id="SVGID_1_" width="320" height="384"/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<path class="st0" d="M159.9,44.5L278,85.8V232c0,0.9-0.4,9.2-14.7,26c-11,13-26.9,27.1-47.3,42c-20.6,15.1-41.6,27.8-56.1,36.1
c-14.5-8.3-35.4-21-56-36.1c-20.3-14.9-36.2-29-47.2-42C42.4,241.2,42,232.9,42,232V85.8L159.9,44.5 M159.9,0L0,56v176
c0,70.7,159.9,152,159.9,152S320,302.7,320,232V56L159.9,0z"/>
<polyline class="st1" points="100.1,182.2 142.3,218.5 220.3,145.1 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M26,15v-5c0-5.5-4.5-10-10-10h-0.1c-5.5,0-10,4.5-10,10v5H3v17h12.9H16h13V15H26z M9.9,10c0-3.3,2.7-6,6-6H16
c3.3,0,6,2.7,6,6v5H9.9V10z M14,27v-7h4v7H14z"/>
</svg>

After

Width:  |  Height:  |  Size: 582 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M22,15h-3.4H9.9v-5c0-3.3,2.7-6,6-6H16c3,0,5.4,2.2,5.9,5h4c-0.5-5-4.8-9-9.9-9h-0.1c-5.5,0-10,4.5-10,10v5H3
v17h12.9H16h13V15h-3H22z M18,27h-4v-7h4V27z"/>
</svg>

After

Width:  |  Height:  |  Size: 581 B

View file

@ -205,6 +205,16 @@ button {
color: themed(redTextColor);
}
}
.success-block {
font-size: 1rem;
line-height: 1.4rem;
align-self: flex-end;
text-align: right;
@include themify($themes) {
color: themed(greenTextColor);
}
}
}
.error-text {

View file

@ -18,6 +18,14 @@ app-wallet {
background-color: themed(blueTextColor);
}
}
&:disabled {
background-color: transparent !important;
.icon {
@include themify($themes) {
background-color: themed(accountOptionalTextColor);
}
}
}
}
.alias {

View file

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{fill:#fff;}</style></defs><title>info</title><path class="cls-1" d="M192,42.05476A149.94522,149.94522,0,0,1,298.02728,298.02728,149.94522,149.94522,0,1,1,85.97271,85.97271,148.96374,148.96374,0,0,1,192,42.05476M192,0C85.96132,0,0,85.96132,0,192S85.96132,384,192,384s192-85.96132,192-192S298.03868,0,192,0Z"/><rect class="cls-1" x="171" y="247.94524" width="42" height="42"/><rect class="cls-1" x="171" y="94.05476" width="42" height="120"/></svg>

After

Width:  |  Height:  |  Size: 561 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -649,7 +649,7 @@ module.exports = function (NAME, wrapper, methods, common, IS_MAP, IS_WEAK) {
/*! no static exports found */
/***/ (function(module, exports) {
var core = module.exports = { version: '2.6.11' };
var core = module.exports = { version: '2.6.12' };
if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef
@ -1823,7 +1823,7 @@ var store = global[SHARED] || (global[SHARED] = {});
})('versions', []).push({
version: core.version,
mode: __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js") ? 'pure' : 'global',
copyright: '© 2019 Denis Pushkarev (zloirock.ru)'
copyright: '© 2020 Denis Pushkarev (zloirock.ru)'
});
@ -5800,8 +5800,8 @@ __webpack_require__.r(__webpack_exports__);
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
__webpack_require__(/*! /Users/mekasan/Projects/Projects/zano_v1/src/gui/qt-daemon/html_source/src/polyfills.ts */"./src/polyfills.ts");
module.exports = __webpack_require__(/*! /Users/mekasan/Projects/Projects/zano_v1/src/gui/qt-daemon/html_source/node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills.js */"./node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills.js");
__webpack_require__(/*! D:\Project\WORK_NEW\zano\src\gui\qt-daemon\html_source\src\polyfills.ts */"./src/polyfills.ts");
module.exports = __webpack_require__(/*! D:\Project\WORK_NEW\zano\src\gui\qt-daemon\html_source\node_modules\@angular-devkit\build-angular\src\angular-cli-files\models\jit-polyfills.js */"./node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills.js");
/***/ })

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 320 384" style="enable-background:new 0 0 320 384;" xml:space="preserve">
<style type="text/css">
.st0{clip-path:url(#SVGID_2_);}
.st1{clip-path:url(#SVGID_2_);fill:none;stroke:#000000;stroke-width:42;stroke-miterlimit:10;}
</style>
<g>
<defs>
<rect id="SVGID_1_" width="320" height="384"/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<path class="st0" d="M159.9,44.5L278,85.8V232c0,0.9-0.4,9.2-14.7,26c-11,13-26.9,27.1-47.3,42c-20.6,15.1-41.6,27.8-56.1,36.1
c-14.5-8.3-35.4-21-56-36.1c-20.3-14.9-36.2-29-47.2-42C42.4,241.2,42,232.9,42,232V85.8L159.9,44.5 M159.9,0L0,56v176
c0,70.7,159.9,152,159.9,152S320,302.7,320,232V56L159.9,0z"/>
<polyline class="st1" points="100.1,182.2 142.3,218.5 220.3,145.1 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M26,15v-5c0-5.5-4.5-10-10-10h-0.1c-5.5,0-10,4.5-10,10v5H3v17h12.9H16h13V15H26z M9.9,10c0-3.3,2.7-6,6-6H16
c3.3,0,6,2.7,6,6v5H9.9V10z M14,27v-7h4v7H14z"/>
</svg>

After

Width:  |  Height:  |  Size: 582 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M22,15h-3.4H9.9v-5c0-3.3,2.7-6,6-6H16c3,0,5.4,2.2,5.9,5h4c-0.5-5-4.8-9-9.9-9h-0.1c-5.5,0-10,4.5-10,10v5H3
v17h12.9H16h13V15h-3H22z M18,27h-4v-7h4V27z"/>
</svg>

After

Width:  |  Height:  |  Size: 581 B

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -36,6 +36,7 @@
"ngx-contextmenu": "^5.1.1",
"ngx-papaparse": "^3.0.3",
"qrcode": "^1.3.0",
"node-sass": "^4.0.0",
"rxjs": "~6.3.3",
"zone.js": "~0.8.26"
},

View file

@ -412,23 +412,28 @@ export class BackendService {
this.runCommand('close_wallet', {wallet_id: +wallet_id}, callback);
}
getSmartWalletInfo(wallet_id, callback) {
this.runCommand('get_smart_wallet_info', {wallet_id: +wallet_id}, callback);
getSmartWalletInfo({wallet_id, seed_password}, callback) {
this.runCommand('get_smart_wallet_info', {wallet_id: +wallet_id, seed_password}, callback);
}
getSeedPhraseInfo(param, callback) {
this.runCommand('get_seed_phrase_info', param, callback);
}
runWallet(wallet_id, callback?) {
this.runCommand('run_wallet', {wallet_id: +wallet_id}, callback);
}
isValidRestoreWalletText(text, callback) {
this.runCommand('is_valid_restore_wallet_text', text, callback);
isValidRestoreWalletText(param, callback) {
this.runCommand('is_valid_restore_wallet_text', param, callback);
}
restoreWallet(path, pass, restore_key, callback) {
restoreWallet(path, pass, seed_phrase, seed_pass, callback) {
const params = {
restore_key: restore_key,
seed_phrase: seed_phrase,
path: path,
pass: pass
pass: pass,
seed_pass
};
this.runCommand('restore_wallet', params, callback);
}

View file

@ -62,9 +62,20 @@
</div>
</div>
<div class="input-block half-block" *ngIf="this.seedPhraseInfo?.syntax_correct && this.seedPhraseInfo?.require_password">
<label for="seed-password">{{ 'RESTORE_WALLET.SEED_PASSWORD' | translate }}</label>
<input type="password" id="seed-password" formControlName="seedPassword">
<div class="error-block" *ngIf="(restoreForm.controls['seedPassword'].dirty || restoreForm.controls['seedPassword'].touched) && !this.seedPhraseInfo?.hash_sum_matched">
<span>{{ 'RESTORE_WALLET.FORM_ERRORS.INCORRECT_PASSWORD' | translate }}</span>
</div>
<div class="success-block" *ngIf="this.seedPhraseInfo?.hash_sum_matched">
<span>{{ 'RESTORE_WALLET.OK' | translate }}</span>
</div>
</div>
<div class="wrap-buttons">
<button type="button" class="transparent-button" *ngIf="walletSaved" disabled><i class="icon"></i>{{walletSavedName}}</button>
<button type="button" class="blue-button select-button" (click)="saveWallet()" [disabled]="!restoreForm.valid" *ngIf="!walletSaved">{{ 'RESTORE_WALLET.BUTTON_SELECT' | translate }}</button>
<button type="button" class="blue-button select-button" (click)="saveWallet()" [disabled]="(!this.seedPhraseInfo?.syntax_correct || !this.seedPhraseInfo?.require_password || !this.seedPhraseInfo?.hash_sum_matched) && (!this.seedPhraseInfo?.syntax_correct || this.seedPhraseInfo?.require_password)" *ngIf="!walletSaved">{{ 'RESTORE_WALLET.BUTTON_SELECT' | translate }}</button>
<button type="button" class="blue-button create-button" (click)="createWallet()" [disabled]="!walletSaved">{{ 'RESTORE_WALLET.BUTTON_CREATE' | translate }}</button>
</div>

View file

@ -1,42 +1,57 @@
import {Component, NgZone, OnInit} from '@angular/core';
import {FormGroup, FormControl, Validators} from '@angular/forms';
import {Router} from '@angular/router';
import {BackendService} from '../_helpers/services/backend.service';
import {VariablesService} from '../_helpers/services/variables.service';
import {ModalService} from '../_helpers/services/modal.service';
import {Wallet} from '../_helpers/models/wallet.model';
import {TranslateService} from '@ngx-translate/core';
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { BackendService } from '../_helpers/services/backend.service';
import { VariablesService } from '../_helpers/services/variables.service';
import { ModalService } from '../_helpers/services/modal.service';
import { Wallet } from '../_helpers/models/wallet.model';
import { TranslateService } from '@ngx-translate/core';
import { Subject } from 'rxjs/internal/Subject';
import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-restore-wallet',
templateUrl: './restore-wallet.component.html',
styleUrls: ['./restore-wallet.component.scss']
styleUrls: ['./restore-wallet.component.scss'],
})
export class RestoreWalletComponent implements OnInit {
restoreForm = new FormGroup({
name: new FormControl('', [Validators.required, (g: FormControl) => {
for (let i = 0; i < this.variablesService.wallets.length; i++) {
if (g.value === this.variablesService.wallets[i].name) {
return {'duplicate': true};
}
}
return null;
}]),
key: new FormControl('', Validators.required),
password: new FormControl('', Validators.pattern(this.variablesService.pattern)),
confirm: new FormControl('')
}, function (g: FormGroup) {
return g.get('password').value === g.get('confirm').value ? null : {'confirm_mismatch': true};
});
export class RestoreWalletComponent implements OnInit, OnDestroy {
restoreForm = new FormGroup(
{
name: new FormControl('', [
Validators.required,
(g: FormControl) => {
for (let i = 0; i < this.variablesService.wallets.length; i++) {
if (g.value === this.variablesService.wallets[i].name) {
return { duplicate: true };
}
}
return null;
},
]),
key: new FormControl('', Validators.required),
password: new FormControl(
'',
Validators.pattern(this.variablesService.pattern)
),
confirm: new FormControl(''),
seedPassword: new FormControl('', Validators.pattern(this.variablesService.pattern)),
},
function (g: FormGroup) {
return g.get('password').value === g.get('confirm').value
? null
: { confirm_mismatch: true };
}
);
wallet = {
id: ''
id: '',
};
walletSaved = false;
walletSavedName = '';
progressWidth = '9rem';
seedPhraseInfo = null;
unsubscribeAll = new Subject<boolean>();
constructor(
private router: Router,
@ -47,7 +62,36 @@ export class RestoreWalletComponent implements OnInit {
private translate: TranslateService
) {}
ngOnInit() {}
ngOnInit() {
this.checkValidSeedPhrasePassword();
this.changeDetectionSeedPhrasePassword();
}
ngOnDestroy() {
this.unsubscribeAll.next(true);
this.unsubscribeAll.complete();
}
changeDetectionSeedPhrasePassword() {
this.restoreForm.controls.seedPassword.valueChanges
.pipe(debounceTime(0), distinctUntilChanged(), takeUntil(this.unsubscribeAll))
.subscribe(() => {
this.checkValidSeedPhrasePassword();
});
this.restoreForm.controls.key.valueChanges
.pipe(debounceTime(0), distinctUntilChanged(), takeUntil(this.unsubscribeAll))
.subscribe(() => {
this.checkValidSeedPhrasePassword();
});
}
checkValidSeedPhrasePassword() {
const seed_password = this.restoreForm.controls.seedPassword.value;
const seed_phrase = this.restoreForm.controls.key.value;
this.backend.getSeedPhraseInfo({seed_phrase, seed_password}, (status, data) => {
this.seedPhraseInfo = data;
});
}
createWallet() {
this.ngZone.run(() => {
@ -57,66 +101,133 @@ export class RestoreWalletComponent implements OnInit {
}
saveWallet() {
if (this.restoreForm.valid && this.restoreForm.get('name').value.length <= this.variablesService.maxWalletNameLength) {
this.backend.isValidRestoreWalletText(this.restoreForm.get('key').value, (valid_status, valid_data) => {
if (valid_data !== 'TRUE') {
this.ngZone.run(() => {
this.restoreForm.get('key').setErrors({key_not_valid: true});
});
} else {
this.backend.saveFileDialog(this.translate.instant('RESTORE_WALLET.CHOOSE_PATH'), '*', this.variablesService.settings.default_path, (save_status, save_data) => {
if (save_status) {
this.variablesService.settings.default_path = save_data.path.substr(0, save_data.path.lastIndexOf('/'));
this.walletSavedName = save_data.path.substr(save_data.path.lastIndexOf('/') + 1, save_data.path.length - 1);
this.backend.restoreWallet(save_data.path, this.restoreForm.get('password').value, this.restoreForm.get('key').value, (restore_status, restore_data) => {
if (restore_status) {
this.wallet.id = restore_data.wallet_id;
this.variablesService.opening_wallet = new Wallet(
restore_data.wallet_id,
this.restoreForm.get('name').value,
this.restoreForm.get('password').value,
restore_data['wi'].path,
restore_data['wi'].address,
restore_data['wi'].balance,
restore_data['wi'].unlocked_balance,
restore_data['wi'].mined_total,
restore_data['wi'].tracking_hey
if (
this.restoreForm.valid &&
this.restoreForm.get('name').value.length <=
this.variablesService.maxWalletNameLength
) {
this.backend.isValidRestoreWalletText(
{
seed_phrase: this.restoreForm.get('key').value,
seed_password: this.restoreForm.get('seedPassword').value,
},
(valid_status, valid_data) => {
if (valid_data !== 'TRUE') {
this.ngZone.run(() => {
this.restoreForm.get('key').setErrors({ key_not_valid: true });
});
} else {
this.backend.saveFileDialog(
this.translate.instant('RESTORE_WALLET.CHOOSE_PATH'),
'*',
this.variablesService.settings.default_path,
(save_status, save_data) => {
if (save_status) {
this.variablesService.settings.default_path = save_data.path.substr(
0,
save_data.path.lastIndexOf('/')
);
this.variablesService.opening_wallet.is_auditable = restore_data['wi'].is_auditable;
this.variablesService.opening_wallet.is_watch_only = restore_data['wi'].is_watch_only;
this.variablesService.opening_wallet.currentPage = 1;
this.variablesService.opening_wallet.alias = this.backend.getWalletAlias(this.variablesService.opening_wallet.address);
this.variablesService.opening_wallet.pages = new Array(1).fill(1);
this.variablesService.opening_wallet.totalPages = 1;
this.variablesService.opening_wallet.currentPage = 1;
this.variablesService.opening_wallet.total_history_item = 0;
this.variablesService.opening_wallet.restore = true;
if (restore_data.recent_history && restore_data.recent_history.history) {
this.variablesService.opening_wallet.totalPages = Math.ceil( restore_data.recent_history.total_history_items / this.variablesService.count);
this.variablesService.opening_wallet.totalPages > this.variablesService.maxPages
? this.variablesService.opening_wallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: this.variablesService.opening_wallet.pages = new Array(this.variablesService.opening_wallet.totalPages).fill(1).map((value, index) => value + index);
this.variablesService.opening_wallet.prepareHistory(restore_data.recent_history.history);
}
this.backend.getContracts(this.variablesService.opening_wallet.wallet_id, (contracts_status, contracts_data) => {
if (contracts_status && contracts_data.hasOwnProperty('contracts')) {
this.ngZone.run(() => {
this.variablesService.opening_wallet.prepareContractsAfterOpen(contracts_data.contracts, this.variablesService.exp_med_ts, this.variablesService.height_app, this.variablesService.settings.viewedContracts, this.variablesService.settings.notViewedContracts);
});
this.walletSavedName = save_data.path.substr(
save_data.path.lastIndexOf('/') + 1,
save_data.path.length - 1
);
this.backend.restoreWallet(
save_data.path,
this.restoreForm.get('password').value,
this.restoreForm.get('key').value,
this.restoreForm.get('seedPassword').value,
(restore_status, restore_data) => {
if (restore_status) {
this.wallet.id = restore_data.wallet_id;
this.variablesService.opening_wallet = new Wallet(
restore_data.wallet_id,
this.restoreForm.get('name').value,
this.restoreForm.get('password').value,
restore_data['wi'].path,
restore_data['wi'].address,
restore_data['wi'].balance,
restore_data['wi'].unlocked_balance,
restore_data['wi'].mined_total,
restore_data['wi'].tracking_hey
);
this.variablesService.opening_wallet.is_auditable =
restore_data['wi'].is_auditable;
this.variablesService.opening_wallet.is_watch_only =
restore_data['wi'].is_watch_only;
this.variablesService.opening_wallet.currentPage = 1;
this.variablesService.opening_wallet.alias = this.backend.getWalletAlias(
this.variablesService.opening_wallet.address
);
this.variablesService.opening_wallet.pages = new Array(
1
).fill(1);
this.variablesService.opening_wallet.totalPages = 1;
this.variablesService.opening_wallet.currentPage = 1;
this.variablesService.opening_wallet.total_history_item = 0;
this.variablesService.opening_wallet.restore = true;
if (
restore_data.recent_history &&
restore_data.recent_history.history
) {
this.variablesService.opening_wallet.totalPages = Math.ceil(
restore_data.recent_history.total_history_items /
this.variablesService.count
);
this.variablesService.opening_wallet.totalPages >
this.variablesService.maxPages
? (this.variablesService.opening_wallet.pages = new Array(
5
)
.fill(1)
.map((value, index) => value + index))
: (this.variablesService.opening_wallet.pages = new Array(
this.variablesService.opening_wallet.totalPages
)
.fill(1)
.map((value, index) => value + index));
this.variablesService.opening_wallet.prepareHistory(
restore_data.recent_history.history
);
}
this.backend.getContracts(
this.variablesService.opening_wallet.wallet_id,
(contracts_status, contracts_data) => {
if (
contracts_status &&
contracts_data.hasOwnProperty('contracts')
) {
this.ngZone.run(() => {
this.variablesService.opening_wallet.prepareContractsAfterOpen(
contracts_data.contracts,
this.variablesService.exp_med_ts,
this.variablesService.height_app,
this.variablesService.settings
.viewedContracts,
this.variablesService.settings
.notViewedContracts
);
});
}
}
);
this.ngZone.run(() => {
this.walletSaved = true;
this.progressWidth = '50%';
});
} else {
this.modalService.prepareModal(
'error',
'RESTORE_WALLET.NOT_CORRECT_FILE_OR_PASSWORD'
);
}
}
});
this.ngZone.run(() => {
this.walletSaved = true;
this.progressWidth = '50%';
});
} else {
this.modalService.prepareModal('error', 'RESTORE_WALLET.NOT_CORRECT_FILE_OR_PASSWORD');
);
}
});
}
});
}
);
}
}
});
);
}
}
@ -132,7 +243,9 @@ export class RestoreWalletComponent implements OnInit {
if (!exists) {
this.backend.runWallet(this.wallet.id, (run_status, run_data) => {
if (run_status) {
this.variablesService.wallets.push(this.variablesService.opening_wallet);
this.variablesService.wallets.push(
this.variablesService.opening_wallet
);
if (this.variablesService.appPass) {
this.backend.storeSecureAppData();
}
@ -145,7 +258,10 @@ export class RestoreWalletComponent implements OnInit {
});
} else {
this.variablesService.opening_wallet = null;
this.modalService.prepareModal('error', 'OPEN_WALLET.WITH_ADDRESS_ALREADY_OPEN');
this.modalService.prepareModal(
'error',
'OPEN_WALLET.WITH_ADDRESS_ALREADY_OPEN'
);
this.backend.closeWallet(this.wallet.id, () => {
this.ngZone.run(() => {
this.router.navigate(['/']);

View file

@ -32,9 +32,9 @@ export class SeedPhraseComponent implements OnInit, OnDestroy {
if (params.wallet_id) {
this.wallet_id = params.wallet_id;
this.backend.getSmartWalletInfo(params.wallet_id, (status, data) => {
if (data.hasOwnProperty('restore_key')) {
if (data.hasOwnProperty('seed_phrase')) {
this.ngZone.run(() => {
this.seedPhrase = data['restore_key'].trim();
this.seedPhrase = data['seed_phrase'].trim();
});
}
});

View file

@ -15,8 +15,10 @@
<div class="input-block">
<label for="wallet-name">{{ 'WALLET_DETAILS.LABEL_NAME' | translate }}</label>
<input type="text" id="wallet-name" formControlName="name" [maxLength]="variablesService.maxWalletNameLength" (contextmenu)="variablesService.onContextMenu($event)">
<div class="error-block" *ngIf="detailsForm.controls['name'].invalid && (detailsForm.controls['name'].dirty || detailsForm.controls['name'].touched)">
<input type="text" id="wallet-name" formControlName="name" [maxLength]="variablesService.maxWalletNameLength"
(contextmenu)="variablesService.onContextMenu($event)">
<div class="error-block"
*ngIf="detailsForm.controls['name'].invalid && (detailsForm.controls['name'].dirty || detailsForm.controls['name'].touched)">
<div *ngIf="detailsForm.controls['name'].errors['required']">
{{ 'WALLET_DETAILS.FORM_ERRORS.NAME_REQUIRED' | translate }}
</div>
@ -33,24 +35,57 @@
<label for="wallet-location">{{ 'WALLET_DETAILS.LABEL_FILE_LOCATION' | translate }}</label>
<input type="text" id="wallet-location" formControlName="path" readonly>
</div>
<div class="input-block textarea">
<label for="seed-phrase">{{ 'WALLET_DETAILS.LABEL_SEED_PHRASE' | translate }}</label>
<div class="seed-phrase" id="seed-phrase">
<div class="seed-phrase-hint" (click)="showSeedPhrase()" *ngIf="!showSeed">{{ 'WALLET_DETAILS.SEED_PHRASE_HINT' | translate }}</div>
<div class="seed-phrase-content" *ngIf="showSeed" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, seedPhrase)">
<ng-container *ngFor="let word of seedPhrase.split(' '); let index = index">
<div class="word">{{(index + 1) + '. ' + word}}</div>
</ng-container>
</div>
</div>
</div>
<div class="wallet-buttons">
<button type="submit" class="blue-button" [disabled]="!detailsForm.valid">{{ 'WALLET_DETAILS.BUTTON_SAVE' | translate }}</button>
<button type="button" class="blue-button" (click)="closeWallet()">{{ 'WALLET_DETAILS.BUTTON_REMOVE' | translate }}</button>
</div>
</form>
</div>
<ng-container *ngIf="!showSeed else seedPhraseContent">
<form class="form-seed mt-2" [formGroup]="seedPhraseForm" (ngSubmit)="onSubmitSeed()">
<label>{{ 'WALLET_DETAILS.LABEL_SEED_PHRASE' | translate }}</label>
<div class="form-content">
<div class="seed-phrase-form">
<div class="input-block">
<label for="create-password">{{ 'WALLET_DETAILS.CREATE_PASSWORD_SECURE' | translate }} (<a class="text-coral">{{ 'WALLET_DETAILS.INFO' | translate }}</a>)</label>
<input type="password" id="create-password" formControlName="password">
</div>
<div class="input-block">
<label for="confirm-password">{{ 'WALLET_DETAILS.FORM.CONFIRM_PASSWORD' | translate }}</label>
<input type="password" id="confirm-password" formControlName="confirmPassword">
</div>
<span class="error-message" *ngIf="!seedPhraseForm.valid">
{{ 'WALLET_DETAILS.FORM_ERRORS.PASSWORDS_DONT_MATCH' | translate }}
</span>
<button type="submit" class="blue-button" [disabled]="!seedPhraseForm.valid"><i class="icon safety"></i>
{{ 'WALLET_DETAILS.FORM.GENERATE_SECURE_SEED' | translate }}</button>
<a class="secured-seed"><i class="icon info"></i>{{ 'WALLET_DETAILS.FORM.SECURED_SEED_WILL_REQUIRE' | translate }}</a>
</div>
</div>
</form>
</ng-container>
<ng-template #seedPhraseContent>
<div class="seed-phrase mt-2">
<div class="seed-phrase-title">
<span>{{ 'WALLET_DETAILS.LABEL_SEED_PHRASE' | translate }}</span>
<p class="right-part">
<i class="icon" [class.copy]="!copyAnimation" [class.copied]="copyAnimation" (click)="copySeedPhrase()"></i>
<span
*ngIf="seedPhraseForm.controls.password.value.length == 0">{{ 'WALLET_DETAILS.SEED_IS_UNSECURED' | translate }} <i class="icon unsecured"></i></span>
<span
*ngIf="seedPhraseForm.controls.password.value.length > 0">{{ 'WALLET_DETAILS.SEED_IS_SECURED' | translate }} <i class="icon secured"></i></span>
</p>
</div>
<div class="seed-phrase-content">
<ng-container *ngFor="let word of seedPhrase.split(' '); let index = index">
<div class="item"
[class.dark]="(index + 1) >= 1 && (index + 1) <= 7 || (index + 1) >= 15 && (index + 1) <= 21">
{{(index + 1) + '. ' + word}}</div>
</ng-container>
</div>
<div class="seed-phrase-footer"
*ngIf="seedPhraseForm.controls.password.value.length > 0">
<span class="title">{{ 'WALLET_DETAILS.REMEMBER_YOU_WILL_REQUIRE' | translate }}</span>
</div>
</div>
</ng-template>
</div>

View file

@ -8,32 +8,6 @@
}
}
.seed-phrase {
display: flex;
font-size: 1.4rem;
line-height: 1.5rem;
padding: 1.4rem;
width: 100%;
height: 8.8rem;
.seed-phrase-hint {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
width: 100%;
height: 100%;
}
.seed-phrase-content {
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 100%;
height: 100%;
}
}
.wallet-buttons {
display: flex;
align-items: center;
@ -45,5 +19,190 @@
max-width: 15rem;
}
}
}
.mt-2 {
margin-top: 2rem;
}
.form-seed {
label {
color: #556576;
font-size: 1.3rem;
line-height: 2.4rem;
}
.form-content {
border: 2px solid #2b3644;
display: flex;
justify-content: center;
padding: 3rem;
width: 100%;
}
.text-coral {
color: #4db1ff;
text-decoration: none;
}
.seed-phrase-form {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 38rem;
width: 100%;
.input-block,
button {
width: 100%;
}
.error-message {
margin-top: 1rem;
color: #ff6f00;
}
.secured-seed {
color: #e0e0e0;
font-size: 1.2rem;
line-height: 2.4rem;
text-decoration: none;
display: flex;
align-items: center;
cursor: pointer;
.icon {
margin-right: 1.2rem;
}
}
button {
display: flex;
justify-content: center;
align-items: center;
margin-top: 3rem;
margin-bottom: 2rem;
.icon {
margin-right: 1.2rem;
}
}
}
}
.seed-phrase {
display: flex;
flex-direction: column;
width: 100%;
background-color: transparent !important;
&-title {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.right-part {
display: inline-flex;
align-items: center;
.icon {
cursor: pointer;
width: 1.7rem;
height: 1.7rem;
&.copy {
background-color: #4caefb;
margin-right: 1.2rem;
mask: url(~src/assets/icons/copy.svg) no-repeat center;
&:hover {
opacity: 0.75;
}
}
&.copied {
margin-right: 1.2rem;
background-color: #4caefb;
mask: url(~src/assets/icons/complete-testwallet.svg) no-repeat center;
}
&.secured, &.unsecured {
margin-left: 1.2rem;
}
}
span {
color: #556576;
font-size: 1.3rem;
line-height: 2.4rem;
display: inline-flex;
align-items: center;
.icon {
margin-left: 1.2rem;
}
}
}
}
&-content {
border-top: 2px solid #2b3644;
border-bottom: 2px solid #2b3644;
padding: 1rem 0 1rem;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 1rem;
.item {
display: flex;
align-items: center;
padding: 0 2rem;
width: calc(100% / 7);
min-height: 35px;
font-size: 1.3rem;
color: #e0e0e0;
&.dark {
background-color: #18202a;
}
}
}
&-footer {
text-align: center;
.title {
color: #556576;
font-size: 1.3rem;
line-height: 2.4rem;
}
}
}
.icon {
display: inline-flex;
width: 1.6rem;
height: 1.6rem;
&.secured {
background-color: #5cda9d;
mask: url(~src/assets/icons/secured.svg) no-repeat center;
}
&.info {
background-color: #4caefb;
mask: url(~src/assets/icons/info.svg) no-repeat center;
}
&.unsecured {
background-color: #ff6f00;
mask: url(~src/assets/icons/unsecured.svg) no-repeat center;
}
&.safety {
background-color: #111921;
mask: url(~src/assets/icons/safety.svg) no-repeat center;
}
}

View file

@ -1,35 +1,64 @@
import {Component, NgZone, OnDestroy, OnInit} from '@angular/core';
import {FormGroup, FormControl, Validators} from '@angular/forms';
import {BackendService} from '../_helpers/services/backend.service';
import {VariablesService} from '../_helpers/services/variables.service';
import {Router} from '@angular/router';
import {Location} from '@angular/common';
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { BackendService } from '../_helpers/services/backend.service';
import { VariablesService } from '../_helpers/services/variables.service';
import { Router } from '@angular/router';
import { Location } from '@angular/common';
@Component({
selector: 'app-wallet-details',
templateUrl: './wallet-details.component.html',
styleUrls: ['./wallet-details.component.scss']
styleUrls: ['./wallet-details.component.scss'],
})
export class WalletDetailsComponent implements OnInit, OnDestroy {
seedPhrase = '';
showSeed = false;
copyAnimation = false;
copyAnimationTimeout;
detailsForm = new FormGroup({
name: new FormControl('', [Validators.required, (g: FormControl) => {
for (let i = 0; i < this.variablesService.wallets.length; i++) {
if (g.value === this.variablesService.wallets[i].name) {
if (this.variablesService.wallets[i].wallet_id === this.variablesService.currentWallet.wallet_id) {
return {'same': true};
} else {
return {'duplicate': true};
name: new FormControl('', [
Validators.required,
(g: FormControl) => {
for (let i = 0; i < this.variablesService.wallets.length; i++) {
if (g.value === this.variablesService.wallets[i].name) {
if (
this.variablesService.wallets[i].wallet_id ===
this.variablesService.currentWallet.wallet_id
) {
return { same: true };
} else {
return { duplicate: true };
}
}
}
}
return null;
}]),
path: new FormControl('')
return null;
},
]),
path: new FormControl(''),
});
seedPhraseForm = new FormGroup(
{
password: new FormControl(
'',
Validators.pattern(this.variablesService.pattern)
),
confirmPassword: new FormControl(
'',
Validators.pattern(this.variablesService.pattern)
),
},
{ validators: this.checkPasswords }
);
checkPasswords(group: FormGroup) {
const pass = group.controls.password.value;
const confirmPass = group.controls.confirmPassword.value;
return pass === confirmPass ? null : { notSame: true };
}
constructor(
private router: Router,
private backend: BackendService,
@ -40,55 +69,91 @@ export class WalletDetailsComponent implements OnInit, OnDestroy {
ngOnInit() {
this.showSeed = false;
this.detailsForm.get('name').setValue(this.variablesService.currentWallet.name);
this.detailsForm.get('path').setValue(this.variablesService.currentWallet.path);
this.backend.getSmartWalletInfo(this.variablesService.currentWallet.wallet_id, (status, data) => {
if (data.hasOwnProperty('restore_key')) {
this.ngZone.run(() => {
this.seedPhrase = data['restore_key'].trim();
});
}
});
this.detailsForm
.get('name')
.setValue(this.variablesService.currentWallet.name);
this.detailsForm
.get('path')
.setValue(this.variablesService.currentWallet.path);
}
showSeedPhrase() {
this.showSeed = true;
}
onSubmitSeed() {
if (this.seedPhraseForm.valid) {
this.showSeedPhrase();
const wallet_id = this.variablesService.currentWallet.wallet_id;
const seed_password = this.seedPhraseForm.controls.password.value;
this.backend.getSmartWalletInfo(
{ wallet_id, seed_password },
(status, data) => {
if (data.hasOwnProperty('seed_phrase')) {
this.ngZone.run(() => {
this.seedPhrase = data['seed_phrase'].trim();
});
}
}
);
}
}
onSubmitEdit() {
if (this.detailsForm.value) {
this.variablesService.currentWallet.name = this.detailsForm.get('name').value;
this.variablesService.currentWallet.name = this.detailsForm.get(
'name'
).value;
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.variablesService.currentWallet.wallet_id]);
this.router.navigate([
'/wallet/' + this.variablesService.currentWallet.wallet_id,
]);
});
}
}
closeWallet() {
this.backend.closeWallet(this.variablesService.currentWallet.wallet_id, () => {
for (let i = this.variablesService.wallets.length - 1; i >= 0; i--) {
if (this.variablesService.wallets[i].wallet_id === this.variablesService.currentWallet.wallet_id) {
this.variablesService.wallets.splice(i, 1);
this.backend.closeWallet(
this.variablesService.currentWallet.wallet_id,
() => {
for (let i = this.variablesService.wallets.length - 1; i >= 0; i--) {
if (
this.variablesService.wallets[i].wallet_id ===
this.variablesService.currentWallet.wallet_id
) {
this.variablesService.wallets.splice(i, 1);
}
}
this.ngZone.run(() => {
if (this.variablesService.wallets.length) {
this.variablesService.currentWallet = this.variablesService.wallets[0];
this.router.navigate([
'/wallet/' + this.variablesService.currentWallet.wallet_id,
]);
} else {
this.router.navigate(['/']);
}
});
if (this.variablesService.appPass) {
this.backend.storeSecureAppData();
}
}
this.ngZone.run(() => {
if (this.variablesService.wallets.length) {
this.variablesService.currentWallet = this.variablesService.wallets[0];
this.router.navigate(['/wallet/' + this.variablesService.currentWallet.wallet_id]);
} else {
this.router.navigate(['/']);
}
});
if (this.variablesService.appPass) {
this.backend.storeSecureAppData();
}
});
);
}
back() {
this.location.back();
}
ngOnDestroy() {}
copySeedPhrase() {
this.backend.setClipboard(this.seedPhrase);
this.copyAnimation = true;
this.copyAnimationTimeout = window.setTimeout(() => {
this.copyAnimation = false;
}, 2000);
}
ngOnDestroy() {
clearTimeout(this.copyAnimationTimeout);
}
}

View file

@ -16,7 +16,7 @@
</div>
</div>
<div>
<button [routerLink]="['/details']" routerLinkActive="active" tooltip="{{ 'WALLET.TOOLTIPS.SETTINGS' | translate }}" placement="left" tooltipClass="table-tooltip account-tooltip" [delay]="500" [timeDelay]="500">
<button [routerLink]="['/details']" routerLinkActive="active" tooltip="{{ 'WALLET.TOOLTIPS.SETTINGS' | translate }}" placement="left" tooltipClass="table-tooltip account-tooltip" [delay]="500" [timeDelay]="500" [disabled]="variablesService.daemon_state !== 2 || !walletLoaded">
<i class="icon details"></i>
</button>
</div>

View file

@ -27,6 +27,7 @@ export class WalletComponent implements OnInit, OnDestroy {
copyAnimation = false;
copyAnimationTimeout;
balanceTooltip;
walletLoaded;
activeTab = 'history';
public mining = false;
public currentPage = 1;
@ -203,6 +204,7 @@ export class WalletComponent implements OnInit, OnDestroy {
this.variablesService.currentWallet.wakeAlias = false;
}
});
this.updateWalletStatus();
}
resetPaginationValues() {
this.ngZone.run(() => {
@ -369,4 +371,13 @@ export class WalletComponent implements OnInit, OnDestroy {
clearTimeout(this.copyAnimationTimeout);
}
updateWalletStatus() {
this.backend.eventSubscribe('update_wallet_status', (data) => {
const wallet_state = data.wallet_state;
this.walletLoaded = false;
if (wallet_state === 2) {
this.walletLoaded = true;
}
});
}
}

View file

@ -120,13 +120,16 @@
"BUTTON_CREATE": "Create wallet",
"NOT_CORRECT_FILE_OR_PASSWORD": "Invalid wallet file or password does not match",
"CHOOSE_PATH": "Please choose a path",
"SEED_PASSWORD": "Seed password",
"OK": "OK",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required",
"NAME_DUPLICATE": "Name is duplicate",
"MAX_LENGTH": "Maximum name length reached",
"CONFIRM_NOT_MATCH": "Confirm password not match",
"KEY_REQUIRED": "Key is required",
"KEY_NOT_VALID": "Key not valid"
"KEY_NOT_VALID": "Key not valid",
"INCORRECT_PASSWORD": "Incorrect password"
}
},
"SEED_PHRASE": {
@ -214,10 +217,21 @@
"SEED_PHRASE_HINT": "Click to reveal the seed phrase",
"BUTTON_SAVE": "Save",
"BUTTON_REMOVE": "Close wallet",
"CREATE_PASSWORD_SECURE": "Create a password to secure your seed",
"INFO": "info",
"SEED_IS_UNSECURED": "Seed is unsecured",
"SEED_IS_SECURED": "Seed is secured",
"REMEMBER_YOU_WILL_REQUIRE": "Remember, you will require the password to restore it.",
"FORM": {
"CONFIRM_PASSWORD": "Confirm password",
"GENERATE_SECURE_SEED": "Generate Secure Seed",
"SECURED_SEED_WILL_REQUIRE": "Secured seed will require this password to restore."
},
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required",
"NAME_DUPLICATE": "Name is duplicate",
"MAX_LENGTH": "Maximum name length reached"
"MAX_LENGTH": "Maximum name length reached",
"PASSWORDS_DONT_MATCH": "Passwords don't match"
}
},
"ASSIGN_ALIAS": {

View file

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{fill:#fff;}</style></defs><title>info</title><path class="cls-1" d="M192,42.05476A149.94522,149.94522,0,0,1,298.02728,298.02728,149.94522,149.94522,0,1,1,85.97271,85.97271,148.96374,148.96374,0,0,1,192,42.05476M192,0C85.96132,0,0,85.96132,0,192S85.96132,384,192,384s192-85.96132,192-192S298.03868,0,192,0Z"/><rect class="cls-1" x="171" y="247.94524" width="42" height="42"/><rect class="cls-1" x="171" y="94.05476" width="42" height="120"/></svg>

After

Width:  |  Height:  |  Size: 561 B

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 320 384" style="enable-background:new 0 0 320 384;" xml:space="preserve">
<style type="text/css">
.st0{clip-path:url(#SVGID_2_);}
.st1{clip-path:url(#SVGID_2_);fill:none;stroke:#000000;stroke-width:42;stroke-miterlimit:10;}
</style>
<g>
<defs>
<rect id="SVGID_1_" width="320" height="384"/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<path class="st0" d="M159.9,44.5L278,85.8V232c0,0.9-0.4,9.2-14.7,26c-11,13-26.9,27.1-47.3,42c-20.6,15.1-41.6,27.8-56.1,36.1
c-14.5-8.3-35.4-21-56-36.1c-20.3-14.9-36.2-29-47.2-42C42.4,241.2,42,232.9,42,232V85.8L159.9,44.5 M159.9,0L0,56v176
c0,70.7,159.9,152,159.9,152S320,302.7,320,232V56L159.9,0z"/>
<polyline class="st1" points="100.1,182.2 142.3,218.5 220.3,145.1 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M26,15v-5c0-5.5-4.5-10-10-10h-0.1c-5.5,0-10,4.5-10,10v5H3v17h12.9H16h13V15H26z M9.9,10c0-3.3,2.7-6,6-6H16
c3.3,0,6,2.7,6,6v5H9.9V10z M14,27v-7h4v7H14z"/>
</svg>

After

Width:  |  Height:  |  Size: 582 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M22,15h-3.4H9.9v-5c0-3.3,2.7-6,6-6H16c3,0,5.4,2.2,5.9,5h4c-0.5-5-4.8-9-9.9-9h-0.1c-5.5,0-10,4.5-10,10v5H3
v17h12.9H16h13V15h-3H22z M18,27h-4v-7h4V27z"/>
</svg>

After

Width:  |  Height:  |  Size: 581 B

View file

@ -205,6 +205,16 @@ button {
color: themed(redTextColor);
}
}
.success-block {
font-size: 1rem;
line-height: 1.4rem;
align-self: flex-end;
text-align: right;
@include themify($themes) {
color: themed(greenTextColor);
}
}
}
.error-text {

View file

@ -18,6 +18,14 @@ app-wallet {
background-color: themed(blueTextColor);
}
}
&:disabled {
background-color: transparent !important;
.icon {
@include themify($themes) {
background-color: themed(accountOptionalTextColor);
}
}
}
}
.alias {

View file

@ -8,16 +8,11 @@
#include "application/mainwindow.h"
#include "qdebug.h"
#include <thread>
#include <math.h>
//#include "qtlogger.h"
#include "include_base_utils.h"
#include "currency_core/currency_config.h"
#include <math.h>
// class MyApplication : public QApplication
// {
//
//
// }
int main(int argc, char *argv[])
{

View file

@ -46,7 +46,6 @@ namespace
const command_line::arg_descriptor<std::string> arg_password = {"password", "Wallet password", "", true};
const command_line::arg_descriptor<bool> arg_dont_refresh = { "no-refresh", "Do not refresh after load", false, true };
const command_line::arg_descriptor<bool> arg_dont_set_date = { "no-set-creation-date", "Do not set wallet creation date", false, false };
const command_line::arg_descriptor<bool> arg_print_brain_wallet = { "print-brain-wallet", "Print to conosole brain wallet", false, false };
const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of default", 0};
const command_line::arg_descriptor<uint32_t> arg_log_level = {"set-log", "", 0, true};
const command_line::arg_descriptor<bool> arg_do_pos_mining = { "do-pos-mining", "Do PoS mining", false, false };
@ -180,7 +179,6 @@ bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<st
simple_wallet::simple_wallet()
: m_daemon_port(0),
m_print_brain_wallet(false),
m_do_refresh_after_load(false),
m_do_not_set_date(false),
m_do_pos_mining(false),
@ -309,10 +307,6 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
m_do_refresh_after_load = false;
}
if (command_line::has_arg(vm, arg_print_brain_wallet))
{
m_print_brain_wallet = true;
}
if (!m_generate_new.empty())
{
@ -338,9 +332,26 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << "failed to read seed phrase";
return false;
}
bool looks_like_tracking_seed = restore_seed_container.password().find(':') != std::string::npos;
bool r = restore_wallet(m_restore_wallet, restore_seed_container.password(), pwd_container.password(), looks_like_tracking_seed);
tools::password_container seed_password_container;
if (!looks_like_tracking_seed)
{
bool is_password_protected = false;
bool sr = account_base::is_seed_password_protected(restore_seed_container.password(), is_password_protected);
if (!sr)
{
fail_msg_writer() << "failed to parse seed phrase";
return false;
}
if (is_password_protected && !seed_password_container.read_password("This seed is secured, to use it please enter the password:\n"))
{
fail_msg_writer() << "failed to read seed phrase";
return false;
}
}
bool r = restore_wallet(m_restore_wallet, restore_seed_container.password(), pwd_container.password(), looks_like_tracking_seed, seed_password_container.password());
CHECK_AND_ASSERT_MES(r, false, "wallet restoring failed");
}
else
@ -402,10 +413,6 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas
if (m_do_not_set_date)
m_wallet->reset_creation_time(0);
if (m_print_brain_wallet)
{
std::cout << "Seed phrase (keep it in secret): " << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush;
}
}
catch (const std::exception& e)
@ -424,7 +431,7 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet)
bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet, const std::string& seed_password)
{
m_wallet_file = wallet_file;
@ -436,13 +443,13 @@ bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::st
if (tracking_wallet)
{
// auditable watch-only aka tracking wallet
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, true);
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, true, "");
message_writer(epee::log_space::console_color_white, true) << "Tracking wallet restored: " << m_wallet->get_account().get_public_address_str();
}
else
{
// normal or auditable wallet
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, false);
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, false, seed_password);
message_writer(epee::log_space::console_color_white, true) << (m_wallet->is_auditable() ? "Auditable wallet" : "Wallet") << " restored: " << m_wallet->get_account().get_public_address_str();
std::cout << "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().view_secret_key) << std::endl << std::flush;
if (m_wallet->is_auditable())
@ -456,7 +463,11 @@ bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::st
fail_msg_writer() << "failed to restore wallet, check your " << (tracking_wallet ? "tracking seed!" : "seed phrase!") << ENDL << e.what();
return false;
}
catch (...)
{
fail_msg_writer() << "failed to restore wallet, check your " << (tracking_wallet ? "tracking seed!" : "seed phrase!") << ENDL;
return false;
}
m_wallet->init(m_daemon_address);
success_msg_writer() <<
@ -484,9 +495,6 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
m_wallet->load(epee::string_encoding::utf8_to_wstring(m_wallet_file), password);
message_writer(epee::log_space::console_color_white, true) << "Opened" << (m_wallet->is_auditable() ? " auditable" : "") << (m_wallet->is_watch_only() ? " watch-only" : "") << " wallet: " << m_wallet->get_account().get_public_address_str();
if (m_print_brain_wallet)
std::cout << "Seed phrase (keep it in secret): " << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush;
break;
}
catch (const tools::error::wallet_load_notice_wallet_restored& /*e*/)
@ -1361,9 +1369,24 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
//----------------------------------------------------------------------------------------------------
bool simple_wallet::show_seed(const std::vector<std::string> &args)
{
success_msg_writer() << "Here's your wallet's seed phrase. Write it down and keep in a safe place.";
success_msg_writer(true) << "Anyone who knows the following 26 words can access your wallet:";
std::cout << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush;
success_msg_writer() << "Please enter a password to secure this seed. Securing your seed is HIGHLY recommended. Leave password blank to stay unsecured.";
success_msg_writer(true) << "Remember, restoring a wallet from Secured Seed can only be done if you know its password.";
tools::password_container seed_password_container1;
if (!seed_password_container1.read_password("Enter seed password: "))
{
return false;
}
tools::password_container seed_password_container2;
if (!seed_password_container2.read_password("Confirm seed password: "))
{
return false;
}
if(seed_password_container1.password() != seed_password_container2.password())
{
std::cout << "Error: password mismatch. Please make sure you entered the correct password and confirmed it" << std::endl << std::flush;
}
std::cout << m_wallet->get_account().get_seed_phrase(seed_password_container2.password()) << std::endl << std::flush;
return true;
}
//----------------------------------------------------------------------------------------------------
@ -1868,7 +1891,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_log_level);
command_line::add_arg(desc_params, arg_dont_refresh);
command_line::add_arg(desc_params, arg_dont_set_date);
command_line::add_arg(desc_params, arg_print_brain_wallet);
command_line::add_arg(desc_params, arg_do_pos_mining);
command_line::add_arg(desc_params, arg_pos_mining_reward_address);
command_line::add_arg(desc_params, arg_restore_wallet);

View file

@ -46,7 +46,7 @@ namespace currency
bool new_wallet(const std::string &wallet_file, const std::string& password, bool create_auditable_wallet);
bool open_wallet(const std::string &wallet_file, const std::string& password);
bool restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet);
bool restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet, const std::string& seed_password);
bool close_wallet();
bool help(const std::vector<std::string> &args = std::vector<std::string>());
@ -163,7 +163,6 @@ namespace currency
int m_daemon_port;
bool m_do_refresh_after_load;
bool m_do_not_set_date;
bool m_print_brain_wallet;
bool m_do_pos_mining;
bool m_offline_mode;
std::string m_restore_wallet;

View file

@ -16,6 +16,7 @@
#include "common/base58.h"
#include "common/config_encrypt_helper.h"
#include "static_helpers.h"
#include "wallet_helpers.h"
#define ANDROID_PACKAGE_NAME "com.zano_mobile"
@ -400,13 +401,13 @@ namespace plain_wallet
return epee::serialization::store_t_to_json(err_result);
}
std::string restore(const std::string& seed, const std::string& path, const std::string& password)
std::string restore(const std::string& seed, const std::string& path, const std::string& password, const std::string& seed_password)
{
GET_INSTANCE_PTR(inst_ptr);
std::string full_path = get_wallets_folder() + path;
epee::json_rpc::response<view::open_wallet_response, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, ok_response.result);
std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, seed_password, ok_response.result);
if (rsp == API_RETURN_CODE_OK || rsp == API_RETURN_CODE_FILE_RESTORED)
{
if (rsp == API_RETURN_CODE_FILE_RESTORED)
@ -484,21 +485,35 @@ namespace plain_wallet
LOG_PRINT_L2("[ASYNC_CALL]: Finished(result put), job id: " << job_id);
}
std::string async_call(const std::string& method_name, uint64_t instance_id, const std::string& params)
{
GET_INSTANCE_PTR(inst_ptr);
std::function<void()> async_callback;
uint64_t job_id = inst_ptr->gjobs_counter++;
async_callback = [job_id, instance_id, method_name, params]()
{
std::string res_str = sync_call(method_name, instance_id, params);
put_result(job_id, res_str);
};
std::thread t([async_callback]() {async_callback(); });
t.detach();
LOG_PRINT_L2("[ASYNC_CALL]: started " << method_name << ", job id: " << job_id);
return std::string("{ \"job_id\": ") + std::to_string(job_id) + "}";
}
std::string sync_call(const std::string& method_name, uint64_t instance_id, const std::string& params)
{
std::string res;
if (method_name == "close")
{
async_callback = [job_id, instance_id]()
{
close_wallet(instance_id);
view::api_responce_return_code rc = AUTO_VAL_INIT(rc);
rc.return_code = API_RETURN_CODE_OK;
put_result(job_id, epee::serialization::store_t_to_json(rc));
};
res = epee::serialization::store_t_to_json(rc);
}
else if (method_name == "open")
{
@ -507,13 +522,11 @@ namespace plain_wallet
{
view::api_response ar = AUTO_VAL_INIT(ar);
ar.error_code = "Wrong parameter";
put_result(job_id, epee::serialization::store_t_to_json(ar));
}
async_callback = [job_id, owr]()
res = epee::serialization::store_t_to_json(ar);
}else
{
std::string res = open(owr.path, owr.pass);
put_result(job_id, res);
};
res = open(owr.path, owr.pass);
}
}
else if (method_name == "restore")
{
@ -522,46 +535,49 @@ namespace plain_wallet
{
view::api_response ar = AUTO_VAL_INIT(ar);
ar.error_code = "Wrong parameter";
put_result(job_id, epee::serialization::store_t_to_json(ar));
res = epee::serialization::store_t_to_json(ar);
}
async_callback = [job_id, rwr]()
else
{
std::string res = restore(rwr.restore_key, rwr.path, rwr.pass);
put_result(job_id, res);
};
res = restore(rwr.seed_phrase, rwr.path, rwr.pass, rwr.seed_pass);
}
}
else if (method_name == "get_seed_phrase_info")
{
view::seed_info_param sip = AUTO_VAL_INIT(sip);
if (!epee::serialization::load_t_from_json(sip, params))
{
view::api_response ar = AUTO_VAL_INIT(ar);
ar.error_code = "Wrong parameter";
res = epee::serialization::store_t_to_json(ar);
}
else
{
view::api_response_t<view::seed_phrase_info> rsp = AUTO_VAL_INIT(rsp);
rsp.error_code = tools::get_seed_phrase_info(sip.seed_phrase, sip.seed_password, rsp.response_data);
res = epee::serialization::store_t_to_json(rsp);
}
}
else if (method_name == "invoke")
{
std::string local_params = params;
async_callback = [job_id, local_params, instance_id]()
{
std::string res = invoke(instance_id, local_params);
put_result(job_id, res);
};
res = invoke(instance_id, params);
}
else if (method_name == "get_wallet_status")
{
std::string local_params = params;
async_callback = [job_id, local_params, instance_id]()
{
std::string res = get_wallet_status(instance_id);
put_result(job_id, res);
};
}else
res = get_wallet_status(instance_id);
}
else
{
view::api_response ar = AUTO_VAL_INIT(ar);
ar.error_code = "UNKNOWN METHOD";
put_result(job_id, epee::serialization::store_t_to_json(ar));
return std::string("{ \"job_id\": ") + std::to_string(job_id) + "}";;
res = epee::serialization::store_t_to_json(ar);
}
std::thread t([async_callback]() {async_callback(); });
t.detach();
LOG_PRINT_L2("[ASYNC_CALL]: started " << method_name << ", job id: " << job_id);
return std::string("{ \"job_id\": ") + std::to_string(job_id) + "}";
return res;
}
std::string try_pull_result(uint64_t job_id)
{
auto inst_ptr = std::atomic_load(&ginstance_ptr);

View file

@ -27,7 +27,7 @@ namespace plain_wallet
std::string get_connectivity_status();
std::string open(const std::string& path, const std::string& password);
std::string restore(const std::string& seed, const std::string& path, const std::string& password);
std::string restore(const std::string& seed, const std::string& path, const std::string& password, const std::string& seed_password);
std::string generate(const std::string& path, const std::string& password);
std::string get_opened_wallets();
@ -38,4 +38,5 @@ namespace plain_wallet
//async api
std::string async_call(const std::string& method_name, uint64_t instance_id, const std::string& params);
std::string try_pull_result(uint64_t);
std::string sync_call(const std::string& method_name, uint64_t instance_id, const std::string& params);
}

View file

@ -272,6 +272,19 @@ public:
END_KV_SERIALIZE_MAP()
};
struct request_get_smart_wallet_info
{
uint64_t wallet_id;
std::string seed_password;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(wallet_id)
KV_SERIALIZE(seed_password)
END_KV_SERIALIZE_MAP()
};
struct response_mining_estimate
{
uint64_t final_amount;
@ -429,16 +442,19 @@ public:
END_KV_SERIALIZE_MAP()
};
struct restore_wallet_request
{
std::string pass;
std::string seed_pass;
std::string path;
std::string restore_key;
std::string seed_phrase;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(pass)
KV_SERIALIZE(path)
KV_SERIALIZE(restore_key)
KV_SERIALIZE(seed_pass)
KV_SERIALIZE(seed_phrase)
END_KV_SERIALIZE_MAP()
};
@ -548,6 +564,8 @@ public:
END_KV_SERIALIZE_MAP()
};
typedef tools::wallet_public::seed_info_param seed_info_param;
typedef tools::wallet_public::seed_phrase_info seed_phrase_info;
struct start_backend_params
{
@ -587,12 +605,10 @@ public:
struct get_restore_info_response
{
std::string restore_key;
std::string error_code;
std::string seed_phrase;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(restore_key)
KV_SERIALIZE(error_code)
KV_SERIALIZE(seed_phrase)
END_KV_SERIALIZE_MAP()
};

View file

@ -2347,7 +2347,7 @@ void wallet2::generate(const std::wstring& path, const std::string& pass, bool a
store();
}
//----------------------------------------------------------------------------------------------------
void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet)
void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password)
{
bool r = false;
clear();
@ -2363,7 +2363,7 @@ void wallet2::restore(const std::wstring& path, const std::string& pass, const s
}
else
{
r = m_account.restore_from_seed_phrase(seed_or_tracking_seed);
r = m_account.restore_from_seed_phrase(seed_or_tracking_seed, seed_password);
init_log_prefix();
THROW_IF_FALSE_WALLET_EX(r, error::wallet_wrong_seed_error, epee::string_encoding::convert_to_ansii(m_wallet_file));
}

View file

@ -470,7 +470,7 @@ namespace tools
void assign_account(const currency::account_base& acc);
void generate(const std::wstring& path, const std::string& password, bool auditable_wallet);
void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet);
void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password);
void load(const std::wstring& path, const std::string& password);
void store();
void store(const std::wstring& path);

View file

@ -23,4 +23,31 @@ namespace tools
wi.is_watch_only = w.is_watch_only();
return true;
}
inline std::string get_seed_phrase_info(const std::string& seed_phrase, const std::string& seed_password, view::seed_phrase_info& result)
{
//cut the last timestamp word from restore_dats
try{
result.syntax_correct = currency::account_base::is_seed_password_protected(seed_phrase, result.require_password);
}
catch (...)
{
result.syntax_correct = false;
return API_RETURN_CODE_OK;
}
if (result.syntax_correct && result.require_password)
{
if (seed_password.size())
{
currency::account_base acc;
result.hash_sum_matched = acc.restore_from_seed_phrase(seed_phrase, seed_password);
}
else
{
result.hash_sum_matched = false;
}
}
return API_RETURN_CODE_OK;
}
}

View file

@ -168,6 +168,29 @@ namespace wallet_public
};
struct seed_info_param
{
std::string seed_phrase;
std::string seed_password;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(seed_phrase)
KV_SERIALIZE(seed_password)
END_KV_SERIALIZE_MAP()
};
struct seed_phrase_info
{
bool syntax_correct;
bool require_password;
bool hash_sum_matched;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(syntax_correct)
KV_SERIALIZE(require_password)
KV_SERIALIZE(hash_sum_matched)
END_KV_SERIALIZE_MAP()
};
struct COMMAND_RPC_GET_BALANCE
{
@ -221,7 +244,6 @@ namespace wallet_public
{
std::string address;
std::string path;
std::string seed;
uint64_t transfers_count;
uint64_t transfer_entries_count;
bool is_whatch_only;
@ -230,7 +252,6 @@ namespace wallet_public
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(path)
KV_SERIALIZE(seed)
KV_SERIALIZE(transfers_count)
KV_SERIALIZE(transfer_entries_count)
KV_SERIALIZE(is_whatch_only)
@ -239,6 +260,32 @@ namespace wallet_public
};
};
struct COMMAND_RPC_GET_WALLET_RESTORE_INFO
{
struct request
{
std::string seed_password;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(seed_password)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string seed_phrase;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(seed_phrase)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_GET_SEED_PHRASE_INFO
{
typedef seed_info_param request;
typedef seed_phrase_info response;
};
struct wallet_provision_info
{

View file

@ -15,6 +15,7 @@ using namespace epee;
#include "misc_language.h"
#include "crypto/hash.h"
#include "wallet_rpc_server_error_codes.h"
#include "wallet_helpers.h"
#define WALLET_RPC_BEGIN_TRY_ENTRY() try {
#define WALLET_RPC_CATCH_TRY_ENTRY() } \
@ -196,7 +197,6 @@ namespace tools
res.path = epee::string_encoding::convert_to_ansii(m_wallet.get_wallet_path());
res.transfers_count = m_wallet.get_recent_transfers_total_count();
res.transfer_entries_count = m_wallet.get_transfer_entries_count();
res.seed = m_wallet.get_account().get_seed_phrase();
std::map<uint64_t, uint64_t> distribution;
m_wallet.get_utxo_distribution(distribution);
for (const auto& ent : distribution)
@ -211,6 +211,25 @@ namespace tools
return false;
}
}
bool wallet_rpc_server::on_getwallet_restore_info(const wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
try
{
res.seed_phrase = m_wallet.get_account().get_seed_phrase(req.seed_password);
return true;
}
catch (std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
}
bool wallet_rpc_server::on_get_seed_phrase_info(const wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::request& req, wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
tools::get_seed_phrase_info(req.seed_phrase, req.seed_password, res);
return true;
}
bool wallet_rpc_server::on_get_recent_txs_and_info(const wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request& req, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
try

View file

@ -51,6 +51,8 @@ namespace tools
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_public::COMMAND_SIGN_TRANSFER)
MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_public::COMMAND_SUBMIT_TRANSFER)
MAP_JON_RPC_WE("search_for_transactions", on_search_for_transactions, wallet_public::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS)
MAP_JON_RPC_WE("get_restore_info", on_getwallet_restore_info, wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO)
MAP_JON_RPC_WE("get_seed_phrase_info", on_get_seed_phrase_info, wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO)
//contracts API
MAP_JON_RPC_WE("contracts_send_proposal", on_contracts_send_proposal, wallet_public::COMMAND_CONTRACTS_SEND_PROPOSAL)
MAP_JON_RPC_WE("contracts_accept_proposal", on_contracts_accept_proposal, wallet_public::COMMAND_CONTRACTS_ACCEPT_PROPOSAL)
@ -70,6 +72,8 @@ namespace tools
bool on_getbalance(const wallet_public::COMMAND_RPC_GET_BALANCE::request& req, wallet_public::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_getaddress(const wallet_public::COMMAND_RPC_GET_ADDRESS::request& req, wallet_public::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_getwallet_info(const wallet_public::COMMAND_RPC_GET_WALLET_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_getwallet_restore_info(const wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_get_seed_phrase_info(const wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::request& req, wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_get_recent_txs_and_info(const wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request& req, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_transfer(const wallet_public::COMMAND_RPC_TRANSFER::request& req, wallet_public::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_store(const wallet_public::COMMAND_RPC_STORE::request& req, wallet_public::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx);

View file

@ -911,7 +911,7 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st
w->get_unconfirmed_transfers(owr.recent_history.history, exclude_mining_txs);
owr.wallet_local_bc_size = w->get_blockchain_current_size();
//workaround for missed fee
owr.seed = w->get_account().get_seed_phrase();
//owr.seed = w->get_account().get_seed_phrase();
break;
}
catch (const tools::error::file_not_found& /**/)
@ -1015,7 +1015,7 @@ std::string wallets_manager::generate_wallet(const std::wstring& path, const std
{
w->generate(path, password, false);
w->set_minimum_height(m_last_daemon_height);
owr.seed = w->get_account().get_seed_phrase();
//owr.seed = w->get_account().get_seed_phrase();
}
catch (const tools::error::file_exists&)
{
@ -1058,10 +1058,10 @@ std::string wallets_manager::is_pos_allowed()
else
return API_RETURN_CODE_FALSE;
}
std::string wallets_manager::is_valid_brain_restore_data(const std::string& seed_phrase)
std::string wallets_manager::is_valid_brain_restore_data(const std::string& seed_phrase, const std::string& seed_password)
{
currency::account_base acc;
if (acc.restore_from_seed_phrase(seed_phrase))
if (acc.restore_from_seed_phrase(seed_phrase, seed_password))
return API_RETURN_CODE_TRUE;
currency::account_public_address addr;
@ -1081,11 +1081,18 @@ void wallets_manager::subscribe_to_core_events(currency::i_core_event_handler* p
}
#endif
std::string wallets_manager::get_seed_phrase_info(const std::string& seed_phrase, const std::string& seed_password, view::seed_phrase_info& result)
{
return tools::get_seed_phrase_info(seed_phrase, seed_password, result);
}
void wallets_manager::get_gui_options(view::gui_options& opt)
{
opt = m_ui_opt;
}
std::string wallets_manager::restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr)
std::string wallets_manager::restore_wallet(const std::wstring& path, const std::string& password, const std::string& seed_phrase, const std::string& seed_password, view::open_wallet_response& owr)
{
std::shared_ptr<tools::wallet2> w(new tools::wallet2());
w->set_use_deffered_global_outputs(m_use_deffered_global_outputs);
@ -1108,9 +1115,9 @@ std::string wallets_manager::restore_wallet(const std::wstring& path, const std:
currency::account_base acc;
try
{
bool auditable_watch_only = restore_key.find(':') != std::string::npos;
w->restore(path, password, restore_key, auditable_watch_only);
owr.seed = w->get_account().get_seed_phrase();
bool auditable_watch_only = seed_phrase.find(':') != std::string::npos;
w->restore(path, password, seed_phrase, auditable_watch_only, seed_password);
//owr.seed = w->get_account().get_seed_phrase();
}
catch (const tools::error::file_exists&)
{
@ -1625,14 +1632,14 @@ std::string wallets_manager::get_mining_history(uint64_t wallet_id, tools::walle
wo.w->get()->get_mining_history(mh);
return API_RETURN_CODE_OK;
}
std::string wallets_manager::get_wallet_restore_info(uint64_t wallet_id, std::string& restore_key)
std::string wallets_manager::get_wallet_restore_info(uint64_t wallet_id, std::string& seed_phrase, const std::string& seed_password)
{
GET_WALLET_OPT_BY_ID(wallet_id, wo);
if (wo.wallet_state != view::wallet_status_info::wallet_state_ready || wo.long_refresh_in_progress)
return API_RETURN_CODE_CORE_BUSY;
restore_key = wo.w->get()->get_account().get_seed_phrase();
seed_phrase = wo.w->get()->get_account().get_seed_phrase(seed_password);
return API_RETURN_CODE_OK;
}

View file

@ -99,7 +99,7 @@ public:
bool get_opened_wallets(std::list<view::open_wallet_response>& result);
std::string open_wallet(const std::wstring& path, const std::string& password, uint64_t txs_to_return, view::open_wallet_response& owr, bool exclude_mining_txs = false);
std::string generate_wallet(const std::wstring& path, const std::string& password, view::open_wallet_response& owr);
std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr);
std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& seed_phrase, const std::string& seed_password, view::open_wallet_response& owr);
std::string invoke(uint64_t wallet_id, std::string params);
std::string get_wallet_status(uint64_t wallet_id);
std::string run_wallet(uint64_t wallet_id);
@ -132,7 +132,7 @@ public:
std::string stop_pos_mining(uint64_t wallet_id);
std::string check_available_sources(uint64_t wallet_id, std::list<uint64_t>& amounts);
std::string get_mining_history(uint64_t wallet_id, tools::wallet_public::mining_history& wrpc);
std::string get_wallet_restore_info(uint64_t wallet_id, std::string& restore_key);
std::string get_wallet_restore_info(uint64_t wallet_id, std::string& seed_phrase, const std::string& seed_password);
std::string backup_wallet(uint64_t wallet_id, const std::wstring& path);
std::string reset_wallet_password(uint64_t wallet_id, const std::string& pass);
std::string is_wallet_password_valid(uint64_t wallet_id, const std::string& pass);
@ -149,7 +149,8 @@ public:
void toggle_pos_mining();
std::string transfer(size_t wallet_id, const view::transfer_params& tp, currency::transaction& res_tx);
std::string get_config_folder();
std::string is_valid_brain_restore_data(const std::string& seed_phrase);
std::string is_valid_brain_restore_data(const std::string& seed_phrase, const std::string& seed_password);
std::string get_seed_phrase_info(const std::string& seed_phrase, const std::string& seed_password, view::seed_phrase_info& result);
#ifndef MOBILE_WALLET_BUILD
void subscribe_to_core_events(currency::i_core_event_handler* pevents_handler);
//void unsubscribe_to_core_events();

View file

@ -612,7 +612,7 @@ gen_no_attchments_in_coinbase::gen_no_attchments_in_coinbase()
// NOTE: This test is made deterministic to be able to correctly set up checkpoint.
random_state_test_restorer::reset_random(); // random generator's state was previously stored, will be restore on dtor (see also m_random_state_test_restorer)
bool r = m_miner_acc.restore_from_seed_phrase("battle harsh arrow gain best doubt nose raw protect salty apart tell distant final yeah stubborn true stop shoulder breathe throne problem everyone stranger only");
bool r = m_miner_acc.restore_from_seed_phrase("battle harsh arrow gain best doubt nose raw protect salty apart tell distant final yeah stubborn true stop shoulder breathe throne problem everyone stranger only", "");
CHECK_AND_ASSERT_THROW_MES(r, "gen_no_attchments_in_coinbase: Can't restore account from seed phrase");
REGISTER_CALLBACK_METHOD(gen_no_attchments_in_coinbase, c1);

View file

@ -1042,7 +1042,7 @@ bool hard_fork_2_awo_wallets_basic_test<before_hf_2>::c1(currency::core& c, size
boost::filesystem::remove(bob_wo_restored_filename, ec);
bob_wlt_awo_restored->restore(bob_wo_restored_filename, "", bob_tracking_seed, true);
bob_wlt_awo_restored->restore(bob_wo_restored_filename, "", bob_tracking_seed, true, "");
bob_wlt_awo_restored->set_core_runtime_config(c.get_blockchain_storage().get_core_runtime_config());
bob_wlt_awo_restored->set_core_proxy(m_core_proxy);
@ -1073,14 +1073,14 @@ bool hard_fork_2_awo_wallets_basic_test<before_hf_2>::c1(currency::core& c, size
// Restore Bob wallet as non-auditable and spend mix_attr!=1 output => make sure other auditable Bob's wallets remain intact
//
std::string bob_seed = bob_wlt->get_account().get_seed_phrase();
std::string bob_seed = bob_wlt->get_account().get_seed_phrase("");
bob_seed.erase(bob_seed.find_last_of(" ")); // remove the last word (with flags and checksum) to make seed old-format 25-words non-auditable with the same keys
std::shared_ptr<tools::wallet2> bob_wlt_non_auditable = std::make_shared<tools::wallet2>();
boost::filesystem::remove(bob_non_auditable_filename, ec);
bob_wlt_non_auditable->restore(bob_non_auditable_filename, "", bob_seed, false);
bob_wlt_non_auditable->restore(bob_non_auditable_filename, "", bob_seed, false, "");
bob_wlt_non_auditable->set_core_runtime_config(c.get_blockchain_storage().get_core_runtime_config());
bob_wlt_non_auditable->set_core_proxy(m_core_proxy);

View file

@ -842,7 +842,7 @@ namespace db_test
ptr = db_array[4];
ASSERT_EQ(ptr->v, "X");
ASSERT_TRUE(db_array.clear());
ASSERT_TRUE((db_array.clear()? true: false));
db_array.commit_transaction();

View file

@ -13,10 +13,10 @@ TEST(wallet_seed, store_restore_test)
{
currency::account_base acc;
acc.generate();
auto seed_phrase = acc.get_seed_phrase();
auto seed_phrase = acc.get_seed_phrase("");
currency::account_base acc2;
bool r = acc2.restore_from_seed_phrase(seed_phrase);
bool r = acc2.restore_from_seed_phrase(seed_phrase, "");
ASSERT_TRUE(r);
if (memcmp(&acc2.get_keys(), &acc.get_keys(), sizeof(currency::account_keys)))
@ -29,10 +29,10 @@ TEST(wallet_seed, store_restore_test)
{
currency::account_base acc;
acc.generate();
auto seed_phrase = acc.get_seed_phrase();
auto seed_phrase = acc.get_seed_phrase("");
currency::account_base acc2;
bool r = acc2.restore_from_seed_phrase(seed_phrase);
bool r = acc2.restore_from_seed_phrase(seed_phrase, "");
ASSERT_TRUE(r);
if (memcmp(&acc2.get_keys(), &acc.get_keys(), sizeof(currency::account_keys)))
@ -57,7 +57,7 @@ wallet_seed_entry wallet_seed_entries[] =
{
{
// legacy 24-word seed phrase -- invalid
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew",
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew god",
"",
"",
0,
@ -66,7 +66,7 @@ wallet_seed_entry wallet_seed_entries[] =
},
{
// old-style 25-word seed phrase -- valid
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew",
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew god",
"5e051454d7226b5734ebd64f754b57db4c655ecda00bd324f1b241d0b6381c0f",
"7dde5590fdf430568c00556ac2accf09da6cde9a29a4bc7d1cb6fd267130f006",
0,
@ -148,7 +148,41 @@ TEST(wallet_seed, basic_test)
bool r = false;
try
{
r = acc.restore_from_seed_phrase(wse.seed_phrase);
r = acc.restore_from_seed_phrase(wse.seed_phrase, "");
if (r)
{
for (size_t j = 0; j != 100; j++)
{
//generate random password
std::string pass = epee::string_tools::pod_to_hex(crypto::cn_fast_hash(&j, sizeof(j)));
if (j!= 0 && j < 64)
{
pass.resize(j);
}
//get secured seed
std::string secured_seed = acc.get_seed_phrase(pass);
//try to restore it without password(should fail)
currency::account_base acc2;
bool r_fail = acc2.restore_from_seed_phrase(secured_seed, "");
ASSERT_EQ(r_fail, false);
//try to restore it with wrong password
bool r_fake_pass = acc2.restore_from_seed_phrase(secured_seed, "fake_password");
if (r_fake_pass)
{
//accidentally checksumm matched(quite possible)
ASSERT_EQ(false, acc2.get_keys() == acc.get_keys());
}
//try to restore it from right password
currency::account_base acc3;
bool r_true_res = acc3.restore_from_seed_phrase(secured_seed, pass);
ASSERT_EQ(true, r_true_res);
ASSERT_EQ(true, acc3.get_keys() == acc.get_keys());
}
}
}
catch (...)
{