mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-23 12:09:50 +00:00
Merge pull request 'FeatherSeed: allow erasure' (#299) from tobtoht/feather:seed_erasure into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/299
This commit is contained in:
commit
ff2f325c51
10 changed files with 231 additions and 192 deletions
|
@ -533,12 +533,21 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(seed.mnemonicSeed.isEmpty()) {
|
if(seed.mnemonic.isEmpty()) {
|
||||||
emit walletCreatedError("Mnemonic seed error. Failed to write wallet.");
|
emit walletCreatedError("Mnemonic seed error. Failed to write wallet.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->currentWallet = seed.writeWallet(this->walletManager, this->networkType, path, password, this->kdfRounds);
|
Wallet *wallet = nullptr;
|
||||||
|
if (seed.seedType == SeedType::TEVADOR) {
|
||||||
|
wallet = this->walletManager->createDeterministicWalletFromSpendKey(path, password, seed.language, this->networkType, seed.spendKey, seed.restoreHeight, this->kdfRounds);
|
||||||
|
wallet->setCacheAttribute("feather.seed", seed.mnemonic.join(" "));
|
||||||
|
}
|
||||||
|
if (seed.seedType == SeedType::MONERO) {
|
||||||
|
wallet = this->walletManager->recoveryWallet(path, password, seed.mnemonic.join(" "), "", this->networkType, seed.restoreHeight, this->kdfRounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->currentWallet = wallet;
|
||||||
if(this->currentWallet == nullptr) {
|
if(this->currentWallet == nullptr) {
|
||||||
emit walletCreatedError("Failed to write wallet");
|
emit walletCreatedError("Failed to write wallet");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
#include "utils/xmrig.h"
|
#include "utils/xmrig.h"
|
||||||
#include "utils/wsclient.h"
|
#include "utils/wsclient.h"
|
||||||
#include "utils/txfiathistory.h"
|
#include "utils/txfiathistory.h"
|
||||||
|
#include "utils/FeatherSeed.h"
|
||||||
#include "widgets/RedditPost.h"
|
#include "widgets/RedditPost.h"
|
||||||
#include "widgets/CCSEntry.h"
|
#include "widgets/CCSEntry.h"
|
||||||
#include "utils/seeds.h"
|
#include "utils/RestoreHeightLookup.h"
|
||||||
#include "utils/nodes.h"
|
#include "utils/nodes.h"
|
||||||
|
|
||||||
#include "libwalletqt/WalletManager.h"
|
#include "libwalletqt/WalletManager.h"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QAbstractButton>
|
#include <QAbstractButton>
|
||||||
|
|
||||||
#include "utils/seeds.h"
|
#include "utils/RestoreHeightLookup.h"
|
||||||
#include "appcontext.h"
|
#include "appcontext.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
114
src/utils/FeatherSeed.h
Normal file
114
src/utils/FeatherSeed.h
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#ifndef FEATHER_FEATHERSEED_H
|
||||||
|
#define FEATHER_FEATHERSEED_H
|
||||||
|
|
||||||
|
#include "libwalletqt/WalletManager.h"
|
||||||
|
#include "libwalletqt/Wallet.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include "RestoreHeightLookup.h"
|
||||||
|
|
||||||
|
enum SeedType {
|
||||||
|
MONERO = 0, // 25 word seeds
|
||||||
|
TEVADOR // 14 word seeds
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FeatherSeed {
|
||||||
|
QString coin;
|
||||||
|
QString language;
|
||||||
|
SeedType seedType;
|
||||||
|
|
||||||
|
QStringList mnemonic;
|
||||||
|
QString spendKey;
|
||||||
|
QString correction;
|
||||||
|
|
||||||
|
time_t time;
|
||||||
|
int restoreHeight = 0;
|
||||||
|
RestoreHeightLookup *lookup = nullptr;
|
||||||
|
|
||||||
|
QString errorString;
|
||||||
|
|
||||||
|
explicit FeatherSeed(RestoreHeightLookup *lookup,
|
||||||
|
const QString &coin = "monero",
|
||||||
|
const QString &language = "English",
|
||||||
|
const QStringList &mnemonic = {})
|
||||||
|
: lookup(lookup), coin(coin), language(language), mnemonic(mnemonic)
|
||||||
|
{
|
||||||
|
// Generate a new mnemonic if none was given
|
||||||
|
if (mnemonic.length() == 0) {
|
||||||
|
this->time = std::time(nullptr);
|
||||||
|
monero_seed seed(this->time, coin.toStdString());
|
||||||
|
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << seed;
|
||||||
|
this->mnemonic = QString::fromStdString(buffer.str()).split(" ");
|
||||||
|
|
||||||
|
buffer.str(std::string());
|
||||||
|
buffer << seed.key();
|
||||||
|
this->spendKey = QString::fromStdString(buffer.str());
|
||||||
|
|
||||||
|
this->setRestoreHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mnemonic.length() == 25) {
|
||||||
|
this->seedType = SeedType::MONERO;
|
||||||
|
}
|
||||||
|
else if (mnemonic.length() == 14) {
|
||||||
|
this->seedType = SeedType::TEVADOR;
|
||||||
|
} else {
|
||||||
|
this->errorString = "Mnemonic seed does not match known type";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seedType == SeedType::TEVADOR) {
|
||||||
|
try {
|
||||||
|
monero_seed seed(mnemonic.join(" ").toStdString(), coin.toStdString());
|
||||||
|
|
||||||
|
this->time = seed.date();
|
||||||
|
this->setRestoreHeight();
|
||||||
|
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << seed.key();
|
||||||
|
this->spendKey = QString::fromStdString(buffer.str());
|
||||||
|
|
||||||
|
this->correction = QString::fromStdString(seed.correction());
|
||||||
|
if (!this->correction.isEmpty()) {
|
||||||
|
buffer.str(std::string());
|
||||||
|
buffer << seed;
|
||||||
|
int index = this->mnemonic.indexOf("xxxx");
|
||||||
|
this->mnemonic.replace(index, this->correction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e) {
|
||||||
|
this->errorString = e.what();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int setRestoreHeight() {
|
||||||
|
if (this->lookup == nullptr)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (this->time == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
this->restoreHeight = this->lookup->dateToRestoreHeight(this->time);
|
||||||
|
return this->restoreHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int setRestoreHeight(int height) {
|
||||||
|
auto now = std::time(nullptr);
|
||||||
|
auto nowClearance = 3600 * 24;
|
||||||
|
auto currentBlockHeight = this->lookup->dateToRestoreHeight(now - nowClearance);
|
||||||
|
if (height >= currentBlockHeight + nowClearance) {
|
||||||
|
qCritical() << "unrealistic restore height detected, setting to current blockheight instead: " << currentBlockHeight;
|
||||||
|
this->restoreHeight = currentBlockHeight;
|
||||||
|
} else {
|
||||||
|
this->restoreHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->restoreHeight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_FEATHERSEED_H
|
76
src/utils/RestoreHeightLookup.h
Normal file
76
src/utils/RestoreHeightLookup.h
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020-2021, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef FEATHER_RESTOREHEIGHTLOOKUP_H
|
||||||
|
#define FEATHER_RESTOREHEIGHTLOOKUP_H
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <monero_seed/monero_seed.hpp>
|
||||||
|
|
||||||
|
#include "networktype.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
struct RestoreHeightLookup {
|
||||||
|
NetworkType::Type type;
|
||||||
|
QMap<int, int> data;
|
||||||
|
explicit RestoreHeightLookup(NetworkType::Type type) : type(type) {}
|
||||||
|
|
||||||
|
int dateToRestoreHeight(int date) {
|
||||||
|
// restore height based on a given timestamp using a lookup
|
||||||
|
// table. If it cannot find the date in the lookup table, it
|
||||||
|
// will calculate the blockheight based off the last known
|
||||||
|
// date: ((now - lastKnownDate) / blockTime) - clearance
|
||||||
|
|
||||||
|
if(this->type == NetworkType::TESTNET) return 1;
|
||||||
|
int blockTime = 120;
|
||||||
|
int blocksPerDay = 86400 / blockTime;
|
||||||
|
int blockCalcClearance = blocksPerDay * 5;
|
||||||
|
QList<int> values = this->data.keys();
|
||||||
|
if(date <= values.at(0))
|
||||||
|
return this->data[values.at(0)];
|
||||||
|
for(int i = 0; i != values.count(); i++) {
|
||||||
|
if(values[i] > date) {
|
||||||
|
return i - 1 < 0 ? this->data[values[i]] : this->data[values[i-1]] - blockCalcClearance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup failed, calculate blockheight from last known checkpoint
|
||||||
|
int lastBlockHeightTime = values.at(values.count() - 1);
|
||||||
|
int lastBlockHeight = this->data[lastBlockHeightTime];
|
||||||
|
int deltaTime = date - lastBlockHeightTime;
|
||||||
|
int deltaBlocks = deltaTime / blockTime;
|
||||||
|
int blockHeight = (lastBlockHeight + deltaBlocks) - blockCalcClearance;
|
||||||
|
qDebug() << "Calculated blockheight: " << blockHeight << " from epoch " << date;
|
||||||
|
return blockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int restoreHeightToDate(int height) {
|
||||||
|
// @TODO: most likely inefficient, refactor
|
||||||
|
QMap<int, int>::iterator i;
|
||||||
|
int timestamp = 0;
|
||||||
|
for (i = this->data.begin(); i != this->data.end(); ++i) {
|
||||||
|
int ts = i.key();
|
||||||
|
if (i.value() > height)
|
||||||
|
return timestamp;
|
||||||
|
timestamp = ts;
|
||||||
|
}
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RestoreHeightLookup *fromFile(const QString &fn, NetworkType::Type type) {
|
||||||
|
// initialize this class using a lookup table, e.g `:/assets/restore_heights_monero_mainnet.txt`/
|
||||||
|
auto rtn = new RestoreHeightLookup(type);
|
||||||
|
auto data = Utils::barrayToString(Utils::fileOpen(fn));
|
||||||
|
QMap<int, int> _data;
|
||||||
|
for(const auto &line: data.split('\n')) {
|
||||||
|
if(line.trimmed().isEmpty()) continue;
|
||||||
|
auto spl = line.trimmed().split(':');
|
||||||
|
rtn->data[spl.at(0).toUInt()] = spl.at(1).toUInt();
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_RESTOREHEIGHTLOOKUP_H
|
|
@ -1,168 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef FEATHER_SEEDS_H
|
|
||||||
#define FEATHER_SEEDS_H
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include <monero_seed/monero_seed.hpp>
|
|
||||||
|
|
||||||
#include "networktype.h"
|
|
||||||
#include "libwalletqt/WalletManager.h"
|
|
||||||
#include "libwalletqt/Wallet.h"
|
|
||||||
#include "utils/utils.h"
|
|
||||||
|
|
||||||
struct RestoreHeightLookup {
|
|
||||||
NetworkType::Type type;
|
|
||||||
QMap<int, int> data;
|
|
||||||
explicit RestoreHeightLookup(NetworkType::Type type) : type(type) {}
|
|
||||||
|
|
||||||
int dateToRestoreHeight(int date) {
|
|
||||||
// restore height based on a given timestamp using a lookup
|
|
||||||
// table. If it cannot find the date in the lookup table, it
|
|
||||||
// will calculate the blockheight based off the last known
|
|
||||||
// date: ((now - lastKnownDate) / blockTime) - clearance
|
|
||||||
|
|
||||||
if(this->type == NetworkType::TESTNET) return 1;
|
|
||||||
int blockTime = 120;
|
|
||||||
int blocksPerDay = 86400 / blockTime;
|
|
||||||
int blockCalcClearance = blocksPerDay * 5;
|
|
||||||
QList<int> values = this->data.keys();
|
|
||||||
if(date <= values.at(0))
|
|
||||||
return this->data[values.at(0)];
|
|
||||||
for(int i = 0; i != values.count(); i++) {
|
|
||||||
if(values[i] > date) {
|
|
||||||
return i - 1 < 0 ? this->data[values[i]] : this->data[values[i-1]] - blockCalcClearance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup failed, calculate blockheight from last known checkpoint
|
|
||||||
int lastBlockHeightTime = values.at(values.count() - 1);
|
|
||||||
int lastBlockHeight = this->data[lastBlockHeightTime];
|
|
||||||
int deltaTime = date - lastBlockHeightTime;
|
|
||||||
int deltaBlocks = deltaTime / blockTime;
|
|
||||||
int blockHeight = (lastBlockHeight + deltaBlocks) - blockCalcClearance;
|
|
||||||
qDebug() << "Calculated blockheight: " << blockHeight << " from epoch " << date;
|
|
||||||
return blockHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
int restoreHeightToDate(int height) {
|
|
||||||
// @TODO: most likely inefficient, refactor
|
|
||||||
QMap<int, int>::iterator i;
|
|
||||||
int timestamp = 0;
|
|
||||||
for (i = this->data.begin(); i != this->data.end(); ++i) {
|
|
||||||
int ts = i.key();
|
|
||||||
if (i.value() > height)
|
|
||||||
return timestamp;
|
|
||||||
timestamp = ts;
|
|
||||||
}
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static RestoreHeightLookup *fromFile(const QString &fn, NetworkType::Type type) {
|
|
||||||
// initialize this class using a lookup table, e.g `:/assets/restore_heights_monero_mainnet.txt`/
|
|
||||||
auto rtn = new RestoreHeightLookup(type);
|
|
||||||
auto data = Utils::barrayToString(Utils::fileOpen(fn));
|
|
||||||
QMap<int, int> _data;
|
|
||||||
for(const auto &line: data.split('\n')) {
|
|
||||||
if(line.trimmed().isEmpty()) continue;
|
|
||||||
auto spl = line.trimmed().split(':');
|
|
||||||
rtn->data[spl.at(0).toUInt()] = spl.at(1).toUInt();
|
|
||||||
}
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FeatherSeed {
|
|
||||||
QString mnemonicSeed;
|
|
||||||
QString spendKey;
|
|
||||||
time_t time = 0;
|
|
||||||
int restoreHeight = 0;
|
|
||||||
RestoreHeightLookup *lookup = nullptr;
|
|
||||||
QString language;
|
|
||||||
std::string coinName;
|
|
||||||
explicit FeatherSeed(RestoreHeightLookup *lookup, const std::string &coinName = "monero", const QString &language = "English") : lookup(lookup), coinName(coinName), language(language) {}
|
|
||||||
|
|
||||||
static FeatherSeed fromSeed(RestoreHeightLookup *lookup,
|
|
||||||
const std::string &coinName,
|
|
||||||
const QString &seedLanguage,
|
|
||||||
const std::string &mnemonicSeed) {
|
|
||||||
|
|
||||||
auto rtn = FeatherSeed(lookup, coinName, seedLanguage);
|
|
||||||
rtn.lookup = lookup;
|
|
||||||
rtn.mnemonicSeed = QString::fromStdString(mnemonicSeed);
|
|
||||||
|
|
||||||
if(QString::fromStdString(mnemonicSeed).split(" ").count() == 14) {
|
|
||||||
monero_seed seed(mnemonicSeed, coinName);
|
|
||||||
std::stringstream buffer;
|
|
||||||
buffer << seed.key();
|
|
||||||
rtn.time = seed.date();
|
|
||||||
rtn.setRestoreHeight();
|
|
||||||
rtn.spendKey = QString::fromStdString(buffer.str());
|
|
||||||
}
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FeatherSeed generate(RestoreHeightLookup *lookup, const std::string &coinName, const QString &language) {
|
|
||||||
auto rtn = FeatherSeed(lookup, coinName, language);
|
|
||||||
time_t _time = std::time(nullptr);
|
|
||||||
monero_seed seed(_time, coinName);
|
|
||||||
|
|
||||||
std::stringstream buffer;
|
|
||||||
buffer << seed;
|
|
||||||
rtn.mnemonicSeed = QString::fromStdString(buffer.str());
|
|
||||||
buffer.str(std::string());
|
|
||||||
buffer << seed.key();
|
|
||||||
rtn.spendKey = QString::fromStdString(buffer.str());
|
|
||||||
rtn.time = _time;
|
|
||||||
rtn.setRestoreHeight();
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wallet *writeWallet(WalletManager *manager, NetworkType::Type type, const QString &path, const QString &password, quint64 kdfRounds) {
|
|
||||||
// writes both 14/25 word mnemonic seeds.
|
|
||||||
Wallet *wallet = nullptr;
|
|
||||||
if(this->lookup == nullptr) return wallet;
|
|
||||||
if(this->mnemonicSeed.split(" ").count() == 14) {
|
|
||||||
if(this->spendKey.isEmpty()) {
|
|
||||||
auto _seed = FeatherSeed::fromSeed(this->lookup, this->coinName, this->language, this->mnemonicSeed.toStdString());
|
|
||||||
_seed.setRestoreHeight();
|
|
||||||
this->time = _seed.time;
|
|
||||||
this->restoreHeight = _seed.restoreHeight;
|
|
||||||
this->spendKey = _seed.spendKey;
|
|
||||||
}
|
|
||||||
wallet = manager->createDeterministicWalletFromSpendKey(path, password, this->language, type, this->spendKey, this->restoreHeight, kdfRounds);
|
|
||||||
wallet->setCacheAttribute("feather.seed", this->mnemonicSeed);
|
|
||||||
} else {
|
|
||||||
wallet = manager->recoveryWallet(path, password, this->mnemonicSeed, "", type, this->restoreHeight, kdfRounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
wallet->setPassword(password);
|
|
||||||
return wallet;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setRestoreHeight() {
|
|
||||||
if(this->lookup == nullptr) return 1;
|
|
||||||
if(this->time == 0) return 1;
|
|
||||||
this->restoreHeight = this->lookup->dateToRestoreHeight(this->time);
|
|
||||||
return this->restoreHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setRestoreHeight(int height) {
|
|
||||||
auto now = std::time(nullptr);
|
|
||||||
auto nowClearance = 3600 * 24;
|
|
||||||
auto currentBlockHeight = this->lookup->dateToRestoreHeight(now - nowClearance);
|
|
||||||
if(height >= currentBlockHeight + nowClearance) {
|
|
||||||
qCritical() << "unrealistic restore height detected, setting to current blockheight instead: " << currentBlockHeight;
|
|
||||||
this->restoreHeight = currentBlockHeight;
|
|
||||||
} else
|
|
||||||
this->restoreHeight = height;
|
|
||||||
|
|
||||||
return this->restoreHeight;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //FEATHER_SEEDS_H
|
|
|
@ -34,8 +34,8 @@ CreateWalletSeedPage::CreateWalletSeedPage(AppContext *ctx, QWidget *parent) :
|
||||||
void CreateWalletSeedPage::seedRoulette(int count) {
|
void CreateWalletSeedPage::seedRoulette(int count) {
|
||||||
count += 1;
|
count += 1;
|
||||||
if(count > m_rouletteSpin) return;
|
if(count > m_rouletteSpin) return;
|
||||||
auto seed = FeatherSeed::generate(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), m_ctx->seedLanguage);
|
FeatherSeed seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName, m_ctx->seedLanguage);
|
||||||
m_mnemonic = seed.mnemonicSeed;
|
m_mnemonic = seed.mnemonic.join(" ");
|
||||||
m_restoreHeight = seed.restoreHeight;
|
m_restoreHeight = seed.restoreHeight;
|
||||||
|
|
||||||
this->displaySeed(m_mnemonic);
|
this->displaySeed(m_mnemonic);
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
|
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include <monero_seed/wordlist.hpp> // tevador 14 word
|
#include <monero_seed/wordlist.hpp> // tevador 14 word
|
||||||
|
#include "utils/FeatherSeed.h"
|
||||||
|
|
||||||
RestorePage::RestorePage(AppContext *ctx, QWidget *parent) :
|
RestorePage::RestorePage(AppContext *ctx, QWidget *parent) :
|
||||||
QWizardPage(parent),
|
QWizardPage(parent),
|
||||||
|
@ -29,6 +31,10 @@ RestorePage::RestorePage(AppContext *ctx, QWidget *parent) :
|
||||||
for(int i = 0; i != 2048; i++)
|
for(int i = 0; i != 2048; i++)
|
||||||
m_words14 << QString::fromStdString(wordlist::english.get_word(i));
|
m_words14 << QString::fromStdString(wordlist::english.get_word(i));
|
||||||
|
|
||||||
|
// Restore has limited error correction capability, namely it can correct a single erasure
|
||||||
|
// (illegible word with a known location). This can be tested by replacing a word with xxxx
|
||||||
|
m_words14 << "xxxx";
|
||||||
|
|
||||||
//
|
//
|
||||||
m_completer14Model = new QStringListModel(m_words14, m_completer14);
|
m_completer14Model = new QStringListModel(m_words14, m_completer14);
|
||||||
m_completer14 = new QCompleter(this);
|
m_completer14 = new QCompleter(this);
|
||||||
|
@ -127,13 +133,6 @@ bool RestorePage::validatePage() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto _seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), m_ctx->seedLanguage, seed.toStdString());
|
|
||||||
restoreHeight = _seed.restoreHeight;
|
|
||||||
|
|
||||||
this->setField("restoreHeight", restoreHeight);
|
|
||||||
this->setField("mnemonicRestoredSeed", seed);
|
|
||||||
return true;
|
|
||||||
} else if(m_mode == 25) {
|
} else if(m_mode == 25) {
|
||||||
if(seedSplit.length() != 25) {
|
if(seedSplit.length() != 25) {
|
||||||
ui->label_errorString->show();
|
ui->label_errorString->show();
|
||||||
|
@ -150,15 +149,22 @@ bool RestorePage::validatePage() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto _seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName, m_ctx->seedLanguage, seedSplit);
|
||||||
|
if (!_seed.errorString.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, "Invalid seed", QString("Invalid seed:\n\n%1").arg(_seed.errorString));
|
||||||
|
ui->seedEdit->setStyleSheet(errStyle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_seed.correction.isEmpty()) {
|
||||||
|
QMessageBox::information(this, "Corrected erasure", QString("xxxx -> %1").arg(_seed.correction));
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreHeight = _seed.restoreHeight;
|
||||||
|
|
||||||
auto _seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), m_ctx->seedLanguage, seed.toStdString());
|
|
||||||
_seed.setRestoreHeight(restoreHeight);
|
|
||||||
this->setField("restoreHeight", restoreHeight);
|
this->setField("restoreHeight", restoreHeight);
|
||||||
this->setField("mnemonicSeed", seed);
|
this->setField("mnemonicSeed", seed);
|
||||||
this->setField("mnemonicRestoredSeed", seed);
|
this->setField("mnemonicRestoredSeed", seed);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->seedEdit->setStyleSheet(errStyle);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
|
@ -80,7 +80,8 @@ void WalletWizard::createWallet() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), m_ctx->seedLanguage, mnemonicSeed.toStdString());
|
auto seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName, m_ctx->seedLanguage, mnemonicSeed.split(" "));
|
||||||
|
|
||||||
if(restoreHeight > 0)
|
if(restoreHeight > 0)
|
||||||
seed.setRestoreHeight(restoreHeight);
|
seed.setRestoreHeight(restoreHeight);
|
||||||
m_ctx->createWallet(seed, walletPath, walletPasswd);
|
m_ctx->createWallet(seed, walletPath, walletPasswd);
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
|
|
||||||
#include "appcontext.h"
|
#include "appcontext.h"
|
||||||
#include "utils/seeds.h"
|
#include "utils/RestoreHeightLookup.h"
|
||||||
#include "utils/config.h"
|
#include "utils/config.h"
|
||||||
|
|
||||||
class WalletWizard : public QWizard
|
class WalletWizard : public QWizard
|
||||||
|
|
Loading…
Reference in a new issue