1
0
Fork 0
forked from lthn/blockchain

Merge branch 'release'

This commit is contained in:
cryptozoidberg 2020-12-18 17:00:10 +01:00
commit 9711a555a7
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
80 changed files with 4408 additions and 690 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

@ -12,14 +12,14 @@ DISABLE_VS_WARNINGS(4146 4244)
/* Predeclarations */
static void fe_mul(fe, const fe, const fe);
static void fe_sq(fe, const fe);
static void fe_tobytes(unsigned char *, const fe);
void fe_mul(fe, const fe, const fe);
void fe_sq(fe, const fe);
void fe_tobytes(unsigned char *, const fe);
static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_p2_0(ge_p2 *);
static void ge_p3_dbl(ge_p1p1 *, const ge_p3 *);
static void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *);
void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *);
static void fe_divpowm1(fe, const fe, const fe);
/* Common functions */
@ -48,7 +48,7 @@ static uint64_t load_4(const unsigned char *in)
h = 0
*/
static void fe_0(fe h) {
void fe_0(fe h) {
h[0] = 0;
h[1] = 0;
h[2] = 0;
@ -232,7 +232,7 @@ static void fe_copy(fe h, const fe f) {
/* From fe_invert.c */
static void fe_invert(fe out, const fe z) {
void fe_invert(fe out, const fe z) {
fe t0;
fe t1;
fe t2;
@ -351,7 +351,7 @@ Can get away with 11 carries, but then data flow is much deeper.
With tighter constraints on inputs can squeeze carries into int32.
*/
static void fe_mul(fe h, const fe f, const fe g) {
void fe_mul(fe h, const fe f, const fe g) {
int32_t f0 = f[0];
int32_t f1 = f[1];
int32_t f2 = f[2];
@ -631,7 +631,7 @@ Postconditions:
See fe_mul.c for discussion of implementation strategy.
*/
static void fe_sq(fe h, const fe f) {
void fe_sq(fe h, const fe f) {
int32_t f0 = f[0];
int32_t f1 = f[1];
int32_t f2 = f[2];
@ -1005,7 +1005,7 @@ Proof:
so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
*/
static void fe_tobytes(unsigned char *s, const fe h) {
void fe_tobytes(unsigned char *s, const fe h) {
int32_t h0 = h[0];
int32_t h1 = h[1];
int32_t h2 = h[2];
@ -1398,7 +1398,7 @@ void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) {
/* From ge_p3_0.c */
static void ge_p3_0(ge_p3 *h) {
void ge_p3_0(ge_p3 *h) {
fe_0(h->X);
fe_1(h->Y);
fe_1(h->Z);
@ -1565,7 +1565,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) {
r = p - q
*/
static void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
@ -2963,3 +2963,53 @@ int sc_isnonzero(const unsigned char *s) {
s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] |
s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1;
}
// see implmentation of ge_frombytes_vartime above
void fe_frombytes(fe h, const unsigned char *s)
{
/* From fe_frombytes.c */
int64_t h0 = load_4(s);
int64_t h1 = load_3(s + 4) << 6;
int64_t h2 = load_3(s + 7) << 5;
int64_t h3 = load_3(s + 10) << 3;
int64_t h4 = load_3(s + 13) << 2;
int64_t h5 = load_4(s + 16);
int64_t h6 = load_3(s + 20) << 7;
int64_t h7 = load_3(s + 23) << 5;
int64_t h8 = load_3(s + 26) << 4;
int64_t h9 = (load_3(s + 29) & 8388607) << 2;
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
carry9 = (h9 + (int64_t)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
carry1 = (h1 + (int64_t)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry3 = (h3 + (int64_t)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry5 = (h5 + (int64_t)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
carry7 = (h7 + (int64_t)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
carry0 = (h0 + (int64_t)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry2 = (h2 + (int64_t)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry4 = (h4 + (int64_t)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry6 = (h6 + (int64_t)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
carry8 = (h8 + (int64_t)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}

View file

@ -104,13 +104,17 @@ void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *);
void ge_scalarmult_p3(ge_p3 *, const unsigned char *, const ge_p3 *);
void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp);
void ge_mul8(ge_p1p1 *, const ge_p2 *);
void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *);
void ge_p3_0(ge_p3 *h);
void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *);
extern const fe fe_ma2;
extern const fe fe_ma;
extern const fe fe_fffb1;
extern const fe fe_fffb2;
extern const fe fe_fffb3;
extern const fe fe_fffb4;
void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *);
void sc_0(unsigned char *);
void sc_reduce32(unsigned char *);
void sc_add(unsigned char *, const unsigned char *, const unsigned char *);
@ -118,3 +122,9 @@ void sc_sub(unsigned char *, const unsigned char *, const unsigned char *);
void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *);
int sc_check(const unsigned char *);
int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
void fe_sq(fe h, const fe f);
void fe_mul(fe, const fe, const fe);
void fe_frombytes(fe h, const unsigned char *s);
void fe_invert(fe out, const fe z);
void fe_tobytes(unsigned char *s, const fe h);

View file

@ -288,7 +288,7 @@ namespace crypto {
uint8_t* const m_p_data;
size_t m_data_used;
bool m_ready;
};
}; // class stream_cn_hash
} // namespace crypto

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,46 @@ namespace currency
return true;
}
//-----------------------------------------------------------------
bool account_base::is_seed_tracking(const std::string& seed_phrase)
{
return seed_phrase.find(':') != std::string::npos;
}
//-----------------------------------------------------------------
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,8 @@ 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);
static bool is_seed_tracking(const std::string& seed_phrase);
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

@ -33,6 +33,9 @@ $themes: (
accountBackgroundColor: rgba(43, 54, 68, 0.5),
accountHoverBackgroundColor: rgba(58, 69, 85, 0.5),
accountMainTextColor: #e0e0e0,
securedSeed: #e0e0e0,
seedItemText: #e0e0e0,
seedItemBackgroundColor: #18202a,
accountOptionalTextColor: #556576,
accountIndicatorTextColor: #111921,
accountSwitchBackgroundColor: #000000,
@ -90,6 +93,9 @@ $themes: (
accountBackgroundColor: rgba(37, 40, 43, 0.5),
accountHoverBackgroundColor: rgba(58, 62, 66, 0.5),
accountMainTextColor: #e0e0e0,
securedSeed: #e0e0e0,
seedItemText: #e0e0e0,
seedItemBackgroundColor: #292d31,
accountOptionalTextColor: #565c62,
accountIndicatorTextColor: #1a1a1a,
accountSwitchBackgroundColor: #000000,
@ -147,6 +153,9 @@ $themes: (
accountBackgroundColor: rgba(30, 136, 229, 1),
accountHoverBackgroundColor: rgba(240, 240, 240, 0.5),
accountMainTextColor: #ffffff,
securedSeed: #a0a5ab,
seedItemText: #43454b,
seedItemBackgroundColor: #e6e6e6,
accountOptionalTextColor: #91baf1,
accountIndicatorTextColor: #43454b,
accountSwitchBackgroundColor: #ffffff,

View file

@ -0,0 +1,258 @@
.form-details {
margin-top: 1.8rem;
.input-block {
&:first-child {
width: 50%;
}
}
.wallet-buttons {
display: flex;
align-items: center;
justify-content: space-between;
button {
margin: 2.9rem 0;
width: 100%;
max-width: 15rem;
}
}
}
.form-seed {
label {
@include themify($themes) {
color: themed(optionalTextColor);
}
font-size: 1.3rem;
line-height: 2.4rem;
}
.form-content {
@include themify($themes) {
border: 2px solid themed(transparentButtonBorderColor);
}
display: flex;
justify-content: center;
padding: 3rem;
width: 100%;
}
.text-coral {
@include themify($themes) {
color: themed(blueTextColor);
}
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;
@include themify($themes) {
color: themed(orangeTextColor);
}
}
.secured-seed {
@include themify($themes) {
color: themed(securedSeed);
}
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%;
@include themify($themes) {
color: themed(optionalTextColor);
}
.right-part {
display: inline-flex;
align-items: center;
.icon {
cursor: pointer;
width: 1.7rem;
height: 1.7rem;
&.copy {
@include themify($themes) {
background-color: themed(blueTextColor);
}
margin-right: 1.2rem;
mask: url(~src/assets/icons/copy.svg) no-repeat center;
&:hover {
opacity: 0.75;
}
}
&.copied {
@include themify($themes) {
background-color: themed(blueTextColor);
}
margin-right: 1.2rem;
mask: url(~src/assets/icons/complete-testwallet.svg) no-repeat center;
}
&.secured,
&.unsecured {
margin-left: 1.2rem;
}
}
span {
font-size: 1.3rem;
line-height: 2.4rem;
display: inline-flex;
align-items: center;
.icon {
margin-left: 1.2rem;
}
}
}
}
&-content {
@include themify($themes) {
border-top: 2px solid themed(transparentButtonBorderColor);
border-bottom: 2px solid themed(transparentButtonBorderColor);
}
padding: 1rem 0 1rem;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 1rem;
.item {
@include themify($themes) {
color: themed(seedItemText);
}
display: flex;
align-items: center;
padding: 0 2rem;
width: calc(100% / 7);
min-height: 35px;
font-size: 1.3rem;
&.dark {
@include themify($themes) {
background-color: themed(seedItemBackgroundColor);
}
}
}
}
&-footer {
text-align: center;
.title {
@include themify($themes) {
color: themed(seedItemText);
}
font-size: 1.3rem;
line-height: 2.4rem;
}
}
}
.icon {
display: inline-flex;
width: 1.6rem;
height: 1.6rem;
&.secured {
@include themify($themes) {
background-color: themed(greenTextColor);
}
mask: url(~src/assets/icons/secured.svg) no-repeat center;
}
&.info {
@include themify($themes) {
background-color: themed(blueTextColor);
}
mask: url(~src/assets/icons/info.svg) no-repeat center;
}
&.unsecured {
@include themify($themes) {
background-color: themed(orangeTextColor);
}
mask: url(~src/assets/icons/unsecured.svg) no-repeat center;
}
&.safety {
@include themify($themes) {
background-color: themed(alternativeTextColor);
}
mask: url(~src/assets/icons/safety.svg) no-repeat center;
}
}
.wrap-buttons {
display: flex;
.seed-phrase-button {
margin: 2.8rem 0;
width: 25%;
min-width: 1.5rem;
}
.copy-button {
margin: 2.8rem 1rem;
width: 25%;
min-width: 1.5rem;
}
}

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

@ -59,4 +59,4 @@
</div>
<app-progress-container [width]="progressWidth" [labels]="['PROGRESS.ADD_WALLET', 'PROGRESS.SELECT_LOCATION', 'PROGRESS.CREATE_WALLET']"></app-progress-container>
<app-progress-container [width]="progressWidth" [labels]="['PROGRESS.ADD_WALLET', 'PROGRESS.SELECT_LOCATION', 'Seed Phrase', 'PROGRESS.CREATE_WALLET']"></app-progress-container>

View file

@ -84,7 +84,7 @@ export class CreateWalletComponent implements OnInit {
this.variablesService.opening_wallet.currentPage = 1;
this.ngZone.run(() => {
this.walletSaved = true;
this.progressWidth = '50%';
this.progressWidth = '33%';
});
} else {
if (errorCode && errorCode === 'ALREADY_EXISTS') {

View file

@ -3,7 +3,7 @@
<form class="open-form" (ngSubmit)="openWallet()">
<div class="wallet-path">{{ wallet.name }}</div>
<div class="wallet-path">{{ wallet.path }}</div>
<div class="input-block" *ngIf="!wallet.notFound && !wallet.emptyPass">
<div class="input-block" *ngIf="!wallet.notFound">
<label for="password">{{ 'OPEN_WALLET.MODAL.LABEL' | translate }}</label>
<input type="password" id="password" name="password" [(ngModel)]="wallet.pass" (contextmenu)="variablesService.onContextMenuPasteSelect($event)"/>
</div>

View file

@ -35,18 +35,6 @@ export class OpenWalletModalComponent implements OnInit {
if (this.wallets.length) {
this.wallet = this.wallets[0];
this.wallet.pass = '';
this.backend.openWallet(this.wallet.path, '', this.variablesService.count, true, (status, data, error) => {
if (error === 'FILE_NOT_FOUND') {
this.wallet.notFound = true;
}
if (status) {
this.wallet.pass = '';
this.wallet.emptyPass = true;
this.backend.closeWallet(data.wallet_id);
this.openWallet();
}
});
}
}
@ -56,6 +44,9 @@ export class OpenWalletModalComponent implements OnInit {
}
this.backend.openWallet(this.wallet.path, this.wallet.pass, this.variablesService.count, false, (open_status, open_data, open_error) => {
if (open_error && open_error === 'FILE_NOT_FOUND') {
this.ngZone.run(() => {
this.wallet.notFound = true;
});
let error_translate = this.translate.instant('OPEN_WALLET.FILE_NOT_FOUND1');
error_translate += ':<br>' + this.wallet.path;
error_translate += this.translate.instant('OPEN_WALLET.FILE_NOT_FOUND2');

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, pairwise, startWith, 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,38 @@ 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(startWith(null), pairwise(), takeUntil(this.unsubscribeAll))
.subscribe(() => {
this.checkValidSeedPhrasePassword();
});
this.restoreForm.controls.key.valueChanges
.pipe(startWith(null), pairwise(), 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.ngZone.run(() => {
this.seedPhraseInfo = data;
});
});
}
createWallet() {
this.ngZone.run(() => {
@ -57,66 +103,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 +245,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 +260,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

@ -1,5 +1,5 @@
<div class="content">
<!-- breadcrumbs -->
<div class="head">
<div class="breadcrumbs">
<span [routerLink]="['/main']">{{ 'BREADCRUMBS.ADD_WALLET' | translate }}</span>
@ -10,19 +10,103 @@
<span>{{ 'COMMON.BACK' | translate }}</span>
</button>
</div>
<!-- /breadcrumbs -->
<h3 class="seed-phrase-title">{{ 'SEED_PHRASE.TITLE' | translate }}</h3>
<div class="scrolled-content">
<!-- form detail -->
<form class="form-details" [formGroup]="detailsForm">
<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)" readonly>
<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>
<div *ngIf="detailsForm.controls['name'].errors['duplicate']">
{{ 'WALLET_DETAILS.FORM_ERRORS.NAME_DUPLICATE' | translate }}
</div>
</div>
<div class="error-block" *ngIf="detailsForm.get('name').value.length >= variablesService.maxWalletNameLength">
{{ 'WALLET_DETAILS.FORM_ERRORS.MAX_LENGTH' | translate }}
</div>
</div>
<div class="seed-phrase-content" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, seedPhrase)">
<ng-container *ngFor="let word of seedPhrase.split(' '); let index = index">
<div class="word">{{(index + 1) + '. ' + word}}</div>
<div class="input-block">
<label for="wallet-location">{{ 'WALLET_DETAILS.LABEL_FILE_LOCATION' | translate }}</label>
<input type="text" id="wallet-location" formControlName="path" readonly>
</div>
</form>
<!-- /from detail -->
<!-- form seed pasword -->
<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>
</div>
<!-- /form seed pasword -->
<div class="wrap-buttons">
<button type="button" class="blue-button seed-phrase-button" (click)="runWallet()">{{ 'SEED_PHRASE.BUTTON_CREATE_ACCOUNT' | translate }}</button>
<button type="button" class="blue-button copy-button" (click)="copySeedPhrase()">{{ 'SEED_PHRASE.BUTTON_COPY' | translate }}</button>
<!-- seed phrase -->
<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">
<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" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, seedPhrase)">
<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>
<!-- /seed phrase -->
<div class="wrap-buttons" *ngIf="showSeed">
<button type="button" class="blue-button seed-phrase-button"
(click)="runWallet()">{{ 'SEED_PHRASE.BUTTON_CREATE_ACCOUNT' | translate }}</button>
<button type="button" class="blue-button copy-button"
(click)="copySeedPhrase()">{{ 'SEED_PHRASE.BUTTON_COPY' | translate }}</button>
</div>
</div>
</div>
<app-progress-container [width]="'100%'" [labels]="['PROGRESS.ADD_WALLET', 'PROGRESS.SELECT_LOCATION', 'PROGRESS.CREATE_WALLET']"></app-progress-container>
<app-progress-container [width]="progressWidth"
[labels]="['PROGRESS.ADD_WALLET', 'PROGRESS.SELECT_LOCATION', 'Seed Phrase', 'PROGRESS.CREATE_WALLET']">
</app-progress-container>

View file

@ -2,38 +2,15 @@
position: relative;
}
.seed-phrase-title {
line-height: 2.2rem;
padding: 2.2rem 0;
.content {
padding: 3.2rem 3rem 3rem;
}
.scrolled-content, .content {
height: 100%;
overflow-y: auto;
}
.seed-phrase-content {
display: flex;
flex-direction: column;
flex-wrap: wrap;
padding: 1.4rem;
width: 100%;
height: 12rem;
.word {
line-height: 2.2rem;
max-width: 13rem;
}
}
.wrap-buttons {
display: flex;
.seed-phrase-button {
margin: 2.8rem 0;
width: 25%;
min-width: 1.5rem;
}
.copy-button {
margin: 2.8rem 1rem;
width: 25%;
min-width: 1.5rem;
}
.mt-2 {
margin-top: 2rem;
}

View file

@ -4,6 +4,7 @@ import {BackendService} from '../_helpers/services/backend.service';
import {ActivatedRoute, Router} from '@angular/router';
import {VariablesService} from '../_helpers/services/variables.service';
import {ModalService} from '../_helpers/services/modal.service';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-seed-phrase',
@ -14,8 +15,53 @@ export class SeedPhraseComponent implements OnInit, OnDestroy {
queryRouting;
seedPhrase = '';
showSeed = false;
wallet_id: number;
seedPhraseCopied = false;
progressWidth = '66%';
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 };
}
}
}
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 route: ActivatedRoute,
@ -28,16 +74,24 @@ export class SeedPhraseComponent implements OnInit, OnDestroy {
) {}
ngOnInit() {
this.showSeed = false;
this.getWalletId();
this.setWalletInfoNamePath();
}
private setWalletInfoNamePath() {
this.detailsForm
.get('name')
.setValue(this.variablesService.opening_wallet.name);
this.detailsForm
.get('path')
.setValue(this.variablesService.opening_wallet.path);
}
private getWalletId() {
this.queryRouting = this.route.queryParams.subscribe(params => {
if (params.wallet_id) {
this.wallet_id = params.wallet_id;
this.backend.getSmartWalletInfo(params.wallet_id, (status, data) => {
if (data.hasOwnProperty('restore_key')) {
this.ngZone.run(() => {
this.seedPhrase = data['restore_key'].trim();
});
}
});
}
});
}
@ -86,8 +140,30 @@ export class SeedPhraseComponent implements OnInit, OnDestroy {
this.location.back();
}
showSeedPhrase() {
this.showSeed = true;
this.progressWidth = '100%';
}
onSubmitSeed() {
if (this.seedPhraseForm.valid) {
this.showSeedPhrase();
const wallet_id = this.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();
});
}
}
);
}
}
ngOnDestroy() {
this.queryRouting.unsubscribe();
}
}

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,56 @@
<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">
<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" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, seedPhrase)">
<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,8 @@
max-width: 15rem;
}
}
}
.mt-2 {
margin-top: 2rem;
}

View file

@ -1,35 +1,63 @@
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;
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 +68,83 @@ 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() {}
ngOnDestroy() {
}
}

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

@ -1,18 +1,25 @@
import { Component, OnInit, OnDestroy, NgZone, ViewChild, ElementRef } from '@angular/core';
import {
Component,
OnInit,
OnDestroy,
NgZone,
ViewChild,
ElementRef,
} from '@angular/core';
import { ActivatedRoute, Router, RoutesRecognized } from '@angular/router';
import { VariablesService } from '../_helpers/services/variables.service';
import { BackendService } from '../_helpers/services/backend.service';
import { TranslateService } from '@ngx-translate/core';
import { IntToMoneyPipe } from '../_helpers/pipes/int-to-money.pipe';
import {Subscription} from 'rxjs';
import { Subscription } from 'rxjs';
import { LOCKED_BALANCE_HELP_PAGE } from '../_shared/constants';
import icons from '../../assets/icons/icons.json';
import {PaginationService} from '../_helpers/services/pagination.service';
import {PaginationStore} from '../_helpers/services/pagination.store';
import {Store, Sync} from 'store';
import {Wallet} from '../_helpers/models/wallet.model';
import {distinctUntilChanged, filter} from 'rxjs/operators';
import { PaginationService } from '../_helpers/services/pagination.service';
import { PaginationStore } from '../_helpers/services/pagination.store';
import { Store, Sync } from 'store';
import { Wallet } from '../_helpers/models/wallet.model';
import { distinctUntilChanged, filter } from 'rxjs/operators';
@Component({
selector: 'app-wallet',
@ -27,6 +34,7 @@ export class WalletComponent implements OnInit, OnDestroy {
copyAnimation = false;
copyAnimationTimeout;
balanceTooltip;
walletLoaded;
activeTab = 'history';
public mining = false;
public currentPage = 1;
@ -44,7 +52,7 @@ export class WalletComponent implements OnInit, OnDestroy {
indicator: false,
active: true,
animated: icons.history,
itemHovered: false
itemHovered: false,
},
{
title: 'WALLET.TABS.SEND',
@ -53,7 +61,7 @@ export class WalletComponent implements OnInit, OnDestroy {
indicator: false,
active: false,
animated: icons.send,
itemHovered: false
itemHovered: false,
},
{
title: 'WALLET.TABS.RECEIVE',
@ -62,7 +70,7 @@ export class WalletComponent implements OnInit, OnDestroy {
indicator: false,
active: false,
animated: icons.receive,
itemHovered: false
itemHovered: false,
},
{
title: 'WALLET.TABS.CONTRACTS',
@ -71,7 +79,7 @@ export class WalletComponent implements OnInit, OnDestroy {
indicator: 1,
active: false,
animated: icons.contracts,
itemHovered: false
itemHovered: false,
},
/*{
title: 'WALLET.TABS.MESSAGES',
@ -89,8 +97,8 @@ export class WalletComponent implements OnInit, OnDestroy {
indicator: false,
active: false,
animated: icons.staking,
itemHovered: false
}
itemHovered: false,
},
];
aliasSubscription: Subscription;
walletsSubscription: Subscription;
@ -105,54 +113,71 @@ export class WalletComponent implements OnInit, OnDestroy {
private intToMoneyPipe: IntToMoneyPipe,
private pagination: PaginationService,
private paginationStore: PaginationStore,
private store: Store,
) { }
private store: Store
) {}
ngOnInit() {
this.subRouting1 = this.route.params.subscribe(params => {
this.subRouting1 = this.route.params.subscribe((params) => {
// set current wallet only by user click to avoid after sync show synchronized data
this.walletID = +params['id'];
this.walletLoaded = this.variablesService.getWallet(this.walletID).loaded;
this.variablesService.setCurrentWallet(this.walletID);
this.walletsSubscription = this.store.select('sync').pipe(
filter(Boolean),
distinctUntilChanged(),
).subscribe(value => {
const data = value.filter((item: Sync) => item.wallet_id === this.walletID)[0];
if (data && !data.sync) {
let in_progress;
const values = this.store.value.sync;
if (values && values.length) {
in_progress = values.filter(item => item.sync);
this.variablesService.sync_started = !!(in_progress && in_progress.length);
if (!in_progress) {
this.walletsSubscription = this.store
.select('sync')
.pipe(filter(Boolean), distinctUntilChanged())
.subscribe((value) => {
const data = value.filter(
(item: Sync) => item.wallet_id === this.walletID
)[0];
if (data && !data.sync) {
let in_progress;
const values = this.store.value.sync;
if (values && values.length) {
in_progress = values.filter((item) => item.sync);
this.variablesService.sync_started = !!(
in_progress && in_progress.length
);
if (!in_progress) {
this.variablesService.sync_started = false;
}
} else {
this.variablesService.sync_started = false;
}
} else {
this.variablesService.sync_started = false;
}
}
let restore = false;
if (this.variablesService.after_sync_request.hasOwnProperty(this.walletID)) {
restore = this.variablesService.after_sync_request[this.walletID];
}
if (!this.variablesService.sync_started && restore && this.walletID === (data && data.wallet_id)) {
this.wallet = this.variablesService.getNotLoadedWallet();
if (this.wallet) {
this.tick();
let restore = false;
if (
this.variablesService.after_sync_request.hasOwnProperty(
this.walletID
)
) {
restore = this.variablesService.after_sync_request[this.walletID];
}
// if this is was restore wallet and it was selected on moment when sync completed
this.getRecentTransfers();
this.variablesService.after_sync_request[this.walletID] = false;
}
});
let after_sync_request = false;
if (this.variablesService.after_sync_request.hasOwnProperty(this.walletID)) {
after_sync_request = this.variablesService.after_sync_request[this.walletID];
}
if (after_sync_request && !this.variablesService.sync_started) {
// if user click on the wallet at the first time after restore.
if (
!this.variablesService.sync_started &&
restore &&
this.walletID === (data && data.wallet_id)
) {
this.wallet = this.variablesService.getNotLoadedWallet();
if (this.wallet) {
this.tick();
}
// if this is was restore wallet and it was selected on moment when sync completed
this.getRecentTransfers();
this.variablesService.after_sync_request[this.walletID] = false;
}
});
let after_sync_request = false;
if (
this.variablesService.after_sync_request.hasOwnProperty(this.walletID)
) {
after_sync_request = this.variablesService.after_sync_request[
this.walletID
];
}
if (after_sync_request && !this.variablesService.sync_started) {
// if user click on the wallet at the first time after restore.
this.getRecentTransfers();
}
if (this.variablesService.stop_paginate.hasOwnProperty(this.walletID)) {
this.stop_paginate = this.variablesService.stop_paginate[this.walletID];
@ -170,23 +195,27 @@ export class WalletComponent implements OnInit, OnDestroy {
this.copyAnimation = false;
this.mining = this.variablesService.currentWallet.exclude_mining_txs;
if (this.variablesService.wallets.length === 1) {
this.walletID = +params['id'];
this.variablesService.setCurrentWallet(this.walletID);
}
});
this.subRouting2 = this.router.events.subscribe(val => {
this.subRouting2 = this.router.events.subscribe((val) => {
if (val instanceof RoutesRecognized) {
this.activeTab = val.urlAfterRedirects.replace('?sidenav=true', '').split('/').pop();
this.activeTab = val.urlAfterRedirects
.replace('?sidenav=true', '')
.split('/')
.pop();
if (val.state.root.firstChild && val.state.root.firstChild.firstChild) {
for (let i = 0; i < this.tabs.length; i++) {
this.tabs[i].active = (this.tabs[i].link === '/' + val.state.root.firstChild.firstChild.url[0].path);
this.tabs[i].active =
this.tabs[i].link ===
'/' + val.state.root.firstChild.firstChild.url[0].path;
}
}
}
});
this.queryRouting = this.route.queryParams.subscribe(params => {
this.queryRouting = this.route.queryParams.subscribe((params) => {
if (params.send) {
this.tabs.forEach((tab, index) => {
if (tab.link === '/send') {
@ -198,32 +227,54 @@ export class WalletComponent implements OnInit, OnDestroy {
if (this.variablesService.currentWallet.alias.hasOwnProperty('name')) {
this.variablesService.currentWallet.wakeAlias = false;
}
this.aliasSubscription = this.variablesService.getAliasChangedEvent.subscribe(() => {
if (this.variablesService.currentWallet.alias.hasOwnProperty('name')) {
this.variablesService.currentWallet.wakeAlias = false;
this.aliasSubscription = this.variablesService.getAliasChangedEvent.subscribe(
() => {
if (this.variablesService.currentWallet.alias.hasOwnProperty('name')) {
this.variablesService.currentWallet.wakeAlias = false;
}
}
});
);
this.updateWalletStatus();
}
resetPaginationValues() {
this.ngZone.run(() => {
const total_history_item = this.variablesService.currentWallet.total_history_item;
const total_history_item = this.variablesService.currentWallet
.total_history_item;
const count = this.variablesService.count;
this.variablesService.currentWallet.totalPages = Math.ceil( total_history_item / count);
this.variablesService.currentWallet.totalPages = Math.ceil(
total_history_item / count
);
this.variablesService.currentWallet.exclude_mining_txs = this.mining;
this.variablesService.currentWallet.currentPage = 1;
if (!this.variablesService.currentWallet.totalPages) {
this.variablesService.currentWallet.totalPages = 1;
}
this.variablesService.currentWallet.totalPages > this.variablesService.maxPages
? this.variablesService.currentWallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: this.variablesService.currentWallet.pages = new Array(this.variablesService.currentWallet.totalPages).fill(1).map((value, index) => value + index);
})
this.variablesService.currentWallet.totalPages >
this.variablesService.maxPages
? (this.variablesService.currentWallet.pages = new Array(5)
.fill(1)
.map((value, index) => value + index))
: (this.variablesService.currentWallet.pages = new Array(
this.variablesService.currentWallet.totalPages
)
.fill(1)
.map((value, index) => value + index));
});
}
changeTab(index) {
if (((this.tabs[index].link === '/send' || this.tabs[index].link === '/contracts' || this.tabs[index].link === '/staking') && (this.variablesService.daemon_state !== 2 || !this.variablesService.currentWallet.loaded))
|| ((this.tabs[index].link === '/send' || this.tabs[index].link === '/contracts') && this.variablesService.currentWallet.is_watch_only && this.variablesService.currentWallet.is_auditable)) {
if (
((this.tabs[index].link === '/send' ||
this.tabs[index].link === '/contracts' ||
this.tabs[index].link === '/staking') &&
(this.variablesService.daemon_state !== 2 ||
!this.variablesService.currentWallet.loaded)) ||
((this.tabs[index].link === '/send' ||
this.tabs[index].link === '/contracts') &&
this.variablesService.currentWallet.is_watch_only &&
this.variablesService.currentWallet.is_auditable)
) {
return;
}
this.tabs.forEach((tab) => {
@ -252,11 +303,23 @@ export class WalletComponent implements OnInit, OnDestroy {
this.balanceTooltip = document.createElement('div');
const available = document.createElement('span');
available.setAttribute('class', 'available');
available.innerHTML = this.translate.instant('WALLET.AVAILABLE_BALANCE', { available: this.intToMoneyPipe.transform(this.variablesService.currentWallet.unlocked_balance), currency: this.variablesService.defaultCurrency });
available.innerHTML = this.translate.instant('WALLET.AVAILABLE_BALANCE', {
available: this.intToMoneyPipe.transform(
this.variablesService.currentWallet.unlocked_balance
),
currency: this.variablesService.defaultCurrency,
});
this.balanceTooltip.appendChild(available);
const locked = document.createElement('span');
locked.setAttribute('class', 'locked');
locked.innerHTML = this.translate.instant('WALLET.LOCKED_BALANCE', { locked: this.intToMoneyPipe.transform(this.variablesService.currentWallet.balance.minus(this.variablesService.currentWallet.unlocked_balance)), currency: this.variablesService.defaultCurrency });
locked.innerHTML = this.translate.instant('WALLET.LOCKED_BALANCE', {
locked: this.intToMoneyPipe.transform(
this.variablesService.currentWallet.balance.minus(
this.variablesService.currentWallet.unlocked_balance
)
),
currency: this.variablesService.defaultCurrency,
});
this.balanceTooltip.appendChild(locked);
const link = document.createElement('span');
link.setAttribute('class', 'link');
@ -278,7 +341,10 @@ export class WalletComponent implements OnInit, OnDestroy {
public setPage(pageNumber: number) {
// this is will allow pagination for wallets that was open from existed wallets'
if (this.variablesService.currentWallet.open_from_exist && !this.variablesService.currentWallet.updated) {
if (
this.variablesService.currentWallet.open_from_exist &&
!this.variablesService.currentWallet.updated
) {
this.variablesService.get_recent_transfers = false;
this.variablesService.currentWallet.updated = true;
}
@ -300,7 +366,7 @@ export class WalletComponent implements OnInit, OnDestroy {
if (!value) {
this.paginationStore.setPage(1, 0, this.walletID); // add back page for the first page
} else {
const pages = value.filter(item => item.walletID === this.walletID);
const pages = value.filter((item) => item.walletID === this.walletID);
if (!pages.length) {
this.paginationStore.setPage(1, 0, this.walletID); // add back page for the first page
}
@ -320,27 +386,44 @@ export class WalletComponent implements OnInit, OnDestroy {
}, 1000);
}
getRecentTransfers () {
getRecentTransfers() {
const offset = this.pagination.getOffset(this.walletID);
const value = this.paginationStore.value;
const pages = value ? value.filter(item => item.walletID === this.walletID) : [];
const pages = value
? value.filter((item) => item.walletID === this.walletID)
: [];
this.backend.getRecentTransfers(
this.walletID,
offset,
this.variablesService.count, this.variablesService.currentWallet.exclude_mining_txs, (status, data) => {
const isForward = this.paginationStore.isForward(pages, this.variablesService.currentWallet.currentPage);
this.variablesService.count,
this.variablesService.currentWallet.exclude_mining_txs,
(status, data) => {
const isForward = this.paginationStore.isForward(
pages,
this.variablesService.currentWallet.currentPage
);
if (this.mining && isForward && pages && pages.length === 1) {
this.variablesService.currentWallet.currentPage = 1; // set init page after navigation back
}
const history = (data && data.history);
this.variablesService.stop_paginate[this.walletID] = history && history.length < this.variablesService.count || !history;
const history = data && data.history;
this.variablesService.stop_paginate[this.walletID] =
(history && history.length < this.variablesService.count) || !history;
this.stop_paginate = this.variablesService.stop_paginate[this.walletID];
if (!this.variablesService.stop_paginate[this.walletID]) {
const page = this.variablesService.currentWallet.currentPage + 1;
if (isForward && this.mining && history && history.length === this.variablesService.count) {
this.paginationStore.setPage(page, data.last_item_index, this.walletID); // add back page for current page
if (
isForward &&
this.mining &&
history &&
history.length === this.variablesService.count
) {
this.paginationStore.setPage(
page,
data.last_item_index,
this.walletID
); // add back page for current page
}
}
@ -349,13 +432,18 @@ export class WalletComponent implements OnInit, OnDestroy {
this.ngZone.run(() => {
this.variablesService.get_recent_transfers = false;
if (this.variablesService.after_sync_request.hasOwnProperty(this.walletID)) {
if (
this.variablesService.after_sync_request.hasOwnProperty(
this.walletID
)
) {
// this is will complete get_recent_transfers request
// this will switch of
this.variablesService.after_sync_request[this.walletID] = false;
}
});
});
}
);
}
ngOnDestroy() {
@ -369,4 +457,29 @@ export class WalletComponent implements OnInit, OnDestroy {
clearTimeout(this.copyAnimationTimeout);
}
updateWalletStatus() {
this.backend.eventSubscribe('wallet_sync_progress', (data) => {
const wallet_id = data.wallet_id;
if (wallet_id === this.walletID) {
this.ngZone.run(() => {
this.walletLoaded = false;
});
}
});
this.backend.eventSubscribe('update_wallet_status', (data) => {
const wallet_state = data.wallet_state;
const wallet_id = data.wallet_id;
this.ngZone.run(() => {
if (wallet_state === 2 && wallet_id === this.walletID) {
this.walletLoaded =
this.variablesService.getWallet(this.walletID) !== null &&
this.variablesService.getWallet(this.walletID).loaded
? true
: false;
} else {
this.walletLoaded = false;
}
});
});
}
}

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

@ -33,6 +33,9 @@ $themes: (
accountBackgroundColor: rgba(43, 54, 68, 0.5),
accountHoverBackgroundColor: rgba(58, 69, 85, 0.5),
accountMainTextColor: #e0e0e0,
securedSeed: #e0e0e0,
seedItemText: #e0e0e0,
seedItemBackgroundColor: #18202a,
accountOptionalTextColor: #556576,
accountIndicatorTextColor: #111921,
accountSwitchBackgroundColor: #000000,
@ -90,6 +93,9 @@ $themes: (
accountBackgroundColor: rgba(37, 40, 43, 0.5),
accountHoverBackgroundColor: rgba(58, 62, 66, 0.5),
accountMainTextColor: #e0e0e0,
securedSeed: #e0e0e0,
seedItemText: #e0e0e0,
seedItemBackgroundColor: #292d31,
accountOptionalTextColor: #565c62,
accountIndicatorTextColor: #1a1a1a,
accountSwitchBackgroundColor: #000000,
@ -147,6 +153,9 @@ $themes: (
accountBackgroundColor: rgba(30, 136, 229, 1),
accountHoverBackgroundColor: rgba(240, 240, 240, 0.5),
accountMainTextColor: #ffffff,
securedSeed: #a0a5ab,
seedItemText: #43454b,
seedItemBackgroundColor: #e6e6e6,
accountOptionalTextColor: #91baf1,
accountIndicatorTextColor: #43454b,
accountSwitchBackgroundColor: #ffffff,

View file

@ -0,0 +1,258 @@
.form-details {
margin-top: 1.8rem;
.input-block {
&:first-child {
width: 50%;
}
}
.wallet-buttons {
display: flex;
align-items: center;
justify-content: space-between;
button {
margin: 2.9rem 0;
width: 100%;
max-width: 15rem;
}
}
}
.form-seed {
label {
@include themify($themes) {
color: themed(optionalTextColor);
}
font-size: 1.3rem;
line-height: 2.4rem;
}
.form-content {
@include themify($themes) {
border: 2px solid themed(transparentButtonBorderColor);
}
display: flex;
justify-content: center;
padding: 3rem;
width: 100%;
}
.text-coral {
@include themify($themes) {
color: themed(blueTextColor);
}
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;
@include themify($themes) {
color: themed(orangeTextColor);
}
}
.secured-seed {
@include themify($themes) {
color: themed(securedSeed);
}
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%;
@include themify($themes) {
color: themed(optionalTextColor);
}
.right-part {
display: inline-flex;
align-items: center;
.icon {
cursor: pointer;
width: 1.7rem;
height: 1.7rem;
&.copy {
@include themify($themes) {
background-color: themed(blueTextColor);
}
margin-right: 1.2rem;
mask: url(~src/assets/icons/copy.svg) no-repeat center;
&:hover {
opacity: 0.75;
}
}
&.copied {
@include themify($themes) {
background-color: themed(blueTextColor);
}
margin-right: 1.2rem;
mask: url(~src/assets/icons/complete-testwallet.svg) no-repeat center;
}
&.secured,
&.unsecured {
margin-left: 1.2rem;
}
}
span {
font-size: 1.3rem;
line-height: 2.4rem;
display: inline-flex;
align-items: center;
.icon {
margin-left: 1.2rem;
}
}
}
}
&-content {
@include themify($themes) {
border-top: 2px solid themed(transparentButtonBorderColor);
border-bottom: 2px solid themed(transparentButtonBorderColor);
}
padding: 1rem 0 1rem;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 1rem;
.item {
@include themify($themes) {
color: themed(seedItemText);
}
display: flex;
align-items: center;
padding: 0 2rem;
width: calc(100% / 7);
min-height: 35px;
font-size: 1.3rem;
&.dark {
@include themify($themes) {
background-color: themed(seedItemBackgroundColor);
}
}
}
}
&-footer {
text-align: center;
.title {
@include themify($themes) {
color: themed(seedItemText);
}
font-size: 1.3rem;
line-height: 2.4rem;
}
}
}
.icon {
display: inline-flex;
width: 1.6rem;
height: 1.6rem;
&.secured {
@include themify($themes) {
background-color: themed(greenTextColor);
}
mask: url(~src/assets/icons/secured.svg) no-repeat center;
}
&.info {
@include themify($themes) {
background-color: themed(blueTextColor);
}
mask: url(~src/assets/icons/info.svg) no-repeat center;
}
&.unsecured {
@include themify($themes) {
background-color: themed(orangeTextColor);
}
mask: url(~src/assets/icons/unsecured.svg) no-repeat center;
}
&.safety {
@include themify($themes) {
background-color: themed(alternativeTextColor);
}
mask: url(~src/assets/icons/safety.svg) no-repeat center;
}
}
.wrap-buttons {
display: flex;
.seed-phrase-button {
margin: 2.8rem 0;
width: 25%;
min-width: 1.5rem;
}
.copy-button {
margin: 2.8rem 1rem;
width: 25%;
min-width: 1.5rem;
}
}

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

@ -11,6 +11,7 @@
@import 'assets/scss/layout/status';
@import 'assets/scss/layout/wallet';
@import 'assets/scss/layout/contact';
@import 'assets/scss/layout/seed-phrase';
// MODULES
@import 'assets/scss/modules/head';

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

@ -8,6 +8,6 @@
#define PROJECT_REVISION "7"
#define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION
#define PROJECT_VERSION_BUILD_NO 112
#define PROJECT_VERSION_BUILD_NO 114
#define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO)
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]"

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,43 @@ 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
{
//restore_from_tracking_seed
bool is_tracking = currency::account_base::is_seed_tracking(seed_phrase);
if (is_tracking)
{
currency::account_base acc;
result.require_password = false;
result.hash_sum_matched = false;
result.syntax_correct = acc.restore_from_tracking_seed(seed_phrase);
}
else
{
result.syntax_correct = currency::account_base::is_seed_password_protected(seed_phrase, result.require_password);
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;
}
catch (...)
{
result.syntax_correct = 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,18 +1058,24 @@ 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))
return API_RETURN_CODE_TRUE;
currency::account_public_address addr;
crypto::secret_key view_sec_key;
uint64_t ts;
if (currency::parse_tracking_seed(seed_phrase, addr, view_sec_key, ts))
return API_RETURN_CODE_TRUE;
if (!currency::account_base::is_seed_tracking(seed_phrase))
{
if (acc.restore_from_seed_phrase(seed_phrase, seed_password))
return API_RETURN_CODE_TRUE;
}
else
{
currency::account_public_address addr = AUTO_VAL_INIT(addr);
crypto::secret_key view_sec_key = AUTO_VAL_INIT(view_sec_key);
uint64_t ts = 0;
if (currency::parse_tracking_seed(seed_phrase, addr, view_sec_key, ts))
return API_RETURN_CODE_TRUE;
}
return API_RETURN_CODE_FALSE;
}
#ifndef MOBILE_WALLET_BUILD
@ -1081,11 +1087,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 +1121,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 is_tracking = currency::account_base::is_seed_tracking(seed_phrase);
w->restore(path, password, seed_phrase, is_tracking, seed_password);
//owr.seed = w->get_account().get_seed_phrase();
}
catch (const tools::error::file_exists&)
{
@ -1625,14 +1638,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

@ -0,0 +1,803 @@
// Copyright (c) 2020 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on random manupulation for tests
#include "gtest/gtest.h"
#include "crypto/crypto.h"
extern "C" {
#include "crypto/crypto-ops.h"
void fe_reduce(fe u, const fe h)
{
/* From fe_frombytes.c */
// loading changed
int64_t h0 = h[0];
int64_t h1 = h[1];
int64_t h2 = h[2];
int64_t h3 = h[3];
int64_t h4 = h[4];
int64_t h5 = h[5];
int64_t h6 = h[6];
int64_t h7 = h[7];
int64_t h8 = h[8];
int64_t h9 = h[9];
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
carry9 = (h9 + (int64_t)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
carry1 = (h1 + (int64_t)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry3 = (h3 + (int64_t)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry5 = (h5 + (int64_t)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
carry7 = (h7 + (int64_t)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
carry0 = (h0 + (int64_t)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry2 = (h2 + (int64_t)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry4 = (h4 + (int64_t)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry6 = (h6 + (int64_t)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
carry8 = (h8 + (int64_t)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
u[0] = h0;
u[1] = h1;
u[2] = h2;
u[3] = h3;
u[4] = h4;
u[5] = h5;
u[6] = h6;
u[7] = h7;
u[8] = h8;
u[9] = h9;
/* End fe_frombytes.c */
}
void sc_mul(unsigned char *s, const unsigned char *a, const unsigned char *b)
{
unsigned char c[32];
unsigned char neg_a[32];
sc_0(c);
sc_sub(neg_a, c, a);
// s = c - ab
sc_mulsub(s, neg_a, b, c);
}
} // extern "C"
unsigned char Lm2[32] = { 0xeb, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10 };
// out = z ^ s (mod l)
void sc_exp(unsigned char* out, const unsigned char* z, const unsigned char* s)
{
sc_0(out);
out[0] = 1;
if (sc_isnonzero(s) == 0)
{
return;
}
// calc position of the most significant bit of s
size_t msb_s = 0;
for (size_t i = 31; i != SIZE_MAX; --i)
{
if (s[i] != 0)
{
msb_s = 8 * i;
uint8_t t = s[i] >> 1;
while (t)
{
t >>= 1;
++msb_s;
}
break;
}
}
//memcpy(out, z, sizeof(crypto::ec_scalar));
for (size_t i = msb_s; i != SIZE_MAX; --i)
{
sc_mul(out, out, out);
std::cout << "sc_mul(out, out, out);" << std::endl;
uint8_t bit = (s[i / 8] >> (i % 8)) & 1;
if (bit)
{
sc_mul(out, out, z);
std::cout << "sc_mul(out, out, z);" << std::endl;
}
}
}
void sc_invert(unsigned char* out, const unsigned char* z)
{
memcpy(out, z, sizeof(crypto::ec_scalar));
for(size_t i = 0; i < 128; ++i)
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, out);
sc_mul(out, out, z);
sc_mul(out, out, out);
sc_mul(out, out, z);
}
template<class pod_t>
std::string pod_to_hex_big_endian(const pod_t &h)
{
constexpr char hexmap[] = "0123456789abcdef";
const char* data = reinterpret_cast<const char*>(&h);
size_t len = sizeof h;
std::string s(len * 2, ' ');
for (size_t i = 0; i < len; ++i) {
s[2 * i] = hexmap[(data[len - 1 - i] & 0xF0) >> 4];
s[2 * i + 1] = hexmap[(data[len - 1 - i] & 0x0F)];
}
return s;
}
int fe_cmp(const fe a, const fe b)
{
for (size_t i = 9; i != SIZE_MAX; --i)
{
if (reinterpret_cast<const uint32_t&>(a[i]) < reinterpret_cast<const uint32_t&>(b[i])) return -1;
if (reinterpret_cast<const uint32_t&>(a[i]) > reinterpret_cast<const uint32_t&>(b[i])) return 1;
}
return 0;
}
static const fe scalar_L_fe = { 16110573, 10012311, -6632702, 16062397, 5471207, 0, 0, 0, 0, 4194304 };
__declspec(align(32))
struct scalar_t
{
//fe m_fe; // 40 bytes, array 10 * 4, optimized form
union
{
uint64_t m_u64[4];
unsigned char m_s[32];
};
// DONE! consider 1) change to aligned array of unsigned chars
// consider 2) add 32 byte after to speed up sc_reduce by decreasing num of copy operations
scalar_t()
{}
scalar_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3)
{
m_u64[0] = a0;
m_u64[1] = a1;
m_u64[2] = a2;
m_u64[3] = a3;
}
scalar_t(int64_t v)
{
zero();
if (v == 0)
{
return;
}
//unsigned char bytes[32] = {0};
reinterpret_cast<int64_t&>(m_s) = v;
//fe_frombytes(m_fe, bytes);
// do not need to call reduce as 2^64 < L
}
unsigned char* data()
{
return &m_s[0];
}
const unsigned char* data() const
{
return &m_s[0];
}
operator crypto::secret_key() const
{
crypto::secret_key result;
memcpy(result.data, &m_s, sizeof result.data);
//fe_tobytes(reinterpret_cast<unsigned char*>(&result), m_fe);
return result;
}
bool from_secret_key(const crypto::secret_key& sk)
{
//fe_frombytes(m_fe, reinterpret_cast<const unsigned char*>(&sk));
return false;
}
void zero()
{
//fe_0(m_fe);
m_u64[0] = 0;
m_u64[1] = 0;
m_u64[2] = 0;
m_u64[3] = 0;
//memset(&m_s, 0, sizeof m_s);
}
void make_random()
{
unsigned char tmp[64];
crypto::generate_random_bytes(64, tmp);
sc_reduce(tmp);
memcpy(&m_s, tmp, 32);
}
bool is_zero() const
{
return sc_isnonzero(&m_s[0]) == 0;
}
scalar_t operator+(const scalar_t& v) const
{
scalar_t result;
sc_add(reinterpret_cast<unsigned char*>(&result), reinterpret_cast<const unsigned char*>(&m_s), reinterpret_cast<const unsigned char*>(&v));
return result;
}
scalar_t operator-(const scalar_t& v) const
{
scalar_t result;
sc_sub(reinterpret_cast<unsigned char*>(&result), reinterpret_cast<const unsigned char*>(&m_s), reinterpret_cast<const unsigned char*>(&v));
return result;
}
scalar_t operator*(const scalar_t& v) const
{
scalar_t result;
sc_mul(reinterpret_cast<unsigned char*>(&result), reinterpret_cast<const unsigned char*>(&m_s), reinterpret_cast<const unsigned char*>(&v));
return result;
}
scalar_t reciprocal() const
{
/*unsigned char bytes[32] = {2};
fe t;
fe_frombytes(t, (unsigned char*)&bytes);
fe r;
fe_invert(r, t);
fe m;
fe_mul(m, r, t);
fe r2;
my_fe_invert(r2, t);
fe m2;
fe_mul(m2, r2, t);
scalar_t result;
fe v_f;
fe result_f;
fe_frombytes(v_f, reinterpret_cast<const unsigned char*>(&m_s));
my_fe_invert(result_f, v_f);
fe_tobytes(reinterpret_cast<unsigned char*>(&result), result_f);
//sc_reduce(reinterpret_cast<unsigned char*>(&result));
fe result_check;
fe_frombytes(result_check, reinterpret_cast<unsigned char*>(&result));
fe v_check;
fe_invert(v_check, result_check);
//sc_reduce(reinterpret_cast<unsigned char*>(&result));
return result;*/
scalar_t result;
fe v_f;
fe result_f;
fe_frombytes(v_f, reinterpret_cast<const unsigned char*>(&m_s));
fe_invert(result_f, v_f);
/*fe x2;
fe_mul(x2, result_f, v_f);
fe_reduce(result_f, result_f);
if (fe_cmp(result_f, scalar_L_fe) > 0)
{
// result_f > L
fe_sub(result_f, result_f, scalar_L_fe);
if (fe_cmp(result_f, scalar_L_fe) >= 0)
return false; // fail
}*/
unsigned char tmp[64] = { 0 };
fe_tobytes(reinterpret_cast<unsigned char*>(&tmp), result_f);
sc_reduce(tmp);
memcpy(&result, &tmp, sizeof result);
return result;
}
scalar_t operator/(const scalar_t& v) const
{
return operator*(v.reciprocal());
}
bool operator==(const scalar_t& rhs) const
{
return
m_u64[0] == rhs.m_u64[0] &&
m_u64[1] == rhs.m_u64[1] &&
m_u64[2] == rhs.m_u64[2] &&
m_u64[3] == rhs.m_u64[3];
}
bool operator<(const scalar_t& rhs) const
{
if (m_u64[3] < rhs.m_u64[3]) return true;
if (m_u64[3] > rhs.m_u64[3]) return false;
if (m_u64[2] < rhs.m_u64[2]) return true;
if (m_u64[2] > rhs.m_u64[2]) return false;
if (m_u64[1] < rhs.m_u64[1]) return true;
if (m_u64[1] > rhs.m_u64[1]) return false;
if (m_u64[0] < rhs.m_u64[0]) return true;
if (m_u64[0] > rhs.m_u64[0]) return false;
return false;
}
bool operator>(const scalar_t& rhs) const
{
if (m_u64[3] < rhs.m_u64[3]) return false;
if (m_u64[3] > rhs.m_u64[3]) return true;
if (m_u64[2] < rhs.m_u64[2]) return false;
if (m_u64[2] > rhs.m_u64[2]) return true;
if (m_u64[1] < rhs.m_u64[1]) return false;
if (m_u64[1] > rhs.m_u64[1]) return true;
if (m_u64[0] < rhs.m_u64[0]) return false;
if (m_u64[0] > rhs.m_u64[0]) return true;
return false;
}
friend std::ostream& operator<<(std::ostream& ss, const scalar_t &v)
{
return ss << "0x" << pod_to_hex_big_endian(v);
}
}; // struct scalar_t
//__declspec(align(32))
struct point_t
{
// A point(x, y) is represented in extended homogeneous coordinates (X, Y, Z, T)
// with x = X / Z, y = Y / Z, x * y = T / Z.
ge_p3 m_p3;
point_t()
{
}
void zero()
{
ge_p3_0(&m_p3);
}
bool from_public_key(const crypto::public_key& pk)
{
return ge_frombytes_vartime(&m_p3, reinterpret_cast<const unsigned char*>(&pk)) == 0;
}
operator crypto::public_key()
{
crypto::public_key result;
ge_p3_tobytes((unsigned char*)&result, &m_p3);
return result;
}
point_t operator+(const point_t& rhs)
{
point_t result;
ge_cached rhs_c;
ge_p1p1 t;
ge_p3_to_cached(&rhs_c, &rhs.m_p3);
ge_add(&t, &m_p3, &rhs_c);
ge_p1p1_to_p3(&result.m_p3, &t);
return result;
}
point_t operator-(const point_t& rhs)
{
point_t result;
ge_cached rhs_c;
ge_p1p1 t;
ge_p3_to_cached(&rhs_c, &rhs.m_p3);
ge_sub(&t, &m_p3, &rhs_c);
ge_p1p1_to_p3(&result.m_p3, &t);
return result;
}
friend point_t operator*(const scalar_t& lhs, const point_t& rhs)
{
point_t result;
ge_scalarmult_p3(&result.m_p3, reinterpret_cast<const unsigned char*>(&lhs), &rhs.m_p3);
return result;
}
friend bool operator==(const point_t& lhs, const point_t& rhs)
{
// convert to xy form, then compare components (because (z, y, z, t) representation is not unique)
fe lrecip, lx, ly;
fe rrecip, rx, ry;
fe_invert(lrecip, lhs.m_p3.Z);
fe_invert(rrecip, rhs.m_p3.Z);
fe_mul(lx, lhs.m_p3.X, lrecip);
fe_mul(rx, rhs.m_p3.X, rrecip);
if (memcmp(&lx, &rx, sizeof lx) != 0)
return false;
fe_mul(ly, lhs.m_p3.Y, lrecip);
fe_mul(ry, rhs.m_p3.Y, rrecip);
if (memcmp(&ly, &ry, sizeof ly) != 0)
return false;
return true;
};
}; // struct point_t
struct point_g_t : public point_t
{
point_g_t()
{
}
friend point_t operator*(const scalar_t& lhs, const point_g_t&)
{
point_t result;
ge_scalarmult_base(&result.m_p3, reinterpret_cast<const unsigned char*>(&lhs));
return result;
}
/*friend point_t operator*(const int64_t lhs, const point_g_t& rhs)
{
return operator*(scalar_t)
}*/
static_assert(sizeof(crypto::public_key) == 32, "size error");
}; // struct point_g_t
static const point_g_t point_G;
static const scalar_t scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
static const scalar_t scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
static const scalar_t scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
static const scalar_t scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
static const scalar_t scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff };
/* temporary disable in order not to break the compilation
TEST(crypto, scalar_basics)
{
scalar_t zero = 0;
ASSERT_TRUE(zero.is_zero());
scalar_t one = 1;
ASSERT_FALSE(one.is_zero());
ASSERT_TRUE(one > zero);
scalar_t z = 0;
for (size_t j = 0; j < 1000; ++j)
{
z.make_random();
ASSERT_FALSE(z.is_zero());
ASSERT_GT(z, z - 1);
ASSERT_LT(z, z + 1);
}
ASSERT_TRUE(scalar_L > 0 && !(scalar_L < 0));
ASSERT_TRUE(scalar_Lm1 > 0 && !(scalar_Lm1 < 0));
ASSERT_TRUE(scalar_Lm1 < scalar_L);
ASSERT_FALSE(scalar_Lm1 > scalar_L);
ASSERT_TRUE(scalar_P > scalar_Pm1);
ASSERT_FALSE(scalar_P < scalar_Pm1);
std::cout << "0 = " << zero << std::endl;
std::cout << "1 = " << one << std::endl;
std::cout << "L = " << scalar_L << std::endl;
std::cout << "Lm1 = " << scalar_Lm1 << std::endl;
std::cout << "P = " << scalar_P << std::endl;
std::cout << "Pm1 = " << scalar_Pm1 << std::endl;
// check rolling over L for scalars arithmetics
ASSERT_EQ(scalar_Lm1 + 1, 0);
ASSERT_EQ(scalar_t(0) - 1, scalar_Lm1);
ASSERT_EQ(scalar_Lm1 * 2, scalar_Lm1 - 1); // (L - 1) * 2 = L + L - 2 = (L - 1) - 1 (mod L)
ASSERT_EQ(scalar_Lm1 * 100, scalar_Lm1 - 99);
ASSERT_EQ(scalar_Lm1 * scalar_Lm1, 1); // (L - 1) * (L - 1) = L*L - 2L + 1 = 1 (mod L)
std::cout << std::endl;
fe L_fe;
fe_frombytes(L_fe, &scalar_L.m_s[0]);
fe Pm1_fe;
fe_frombytes(Pm1_fe, &scalar_Pm1.m_s[0]);
fe r;
fe f_1 = { 1 };
fe_add(r, Pm1_fe, f_1);
while(true)
{
static int ti = 2;
static int pi = 3;
scalar_t t = ti;
scalar_t p = pi;
scalar_t r = 0;
//sc_exp(r.data(), t.data(), Lm2);
sc_invert(r.data(), t.data());
std::cout << ti << " ^ L-2" << " = " << r << std::endl;
r = r * 6;
std::cout << r << std::endl;
}
scalar_t a = 2;
a = a / 2;
std::cout << "2 / 2 = " << a << std::endl;
a = scalar_Lm1 / 2;
std::cout << "L-1 / 2 = " << a << std::endl;
a = a * 2;
std::cout << "L-1 / 2 * 2 = " << a << std::endl;
}
TEST(crypto, point_basics)
{
scalar_t s = 4;
point_t E = s * point_G;
point_t X = 4 * E;
point_t K = 193847 * point_G;
point_t C = E + K;
ASSERT_TRUE(X == 16 * point_G);
ASSERT_TRUE(C - K == E);
ASSERT_TRUE(C - E == K);
ASSERT_TRUE(C == 193851 * point_G);
}
TEST(crypto, scalar_reciprocal)
{
int64_t test_nums[] = {1, 2, 10};
for (size_t i = 0; i < sizeof test_nums / sizeof test_nums[0]; ++i)
{
scalar_t s = test_nums[i];
scalar_t z = s - s;
ASSERT_TRUE(z.is_zero());
}
scalar_t s = 20;
scalar_t d = 5;
scalar_t e = s / d;
scalar_t m = e * d;
ASSERT_TRUE(m == s);
}
TEST(crypto, scalars)
{
scalar_t s = 20;
scalar_t d = 5;
scalar_t e = s / d;
scalar_t m = e * d;
ASSERT_TRUE(m == s);
}
*/

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 (...)
{