From 6ee25c93d6ceaa12ebd3a7f2b7864c89690a21ba Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 28 Jul 2015 12:25:48 +0200 Subject: [PATCH] Allow importing of ethersale wallets in CLI. Fixes #2606. --- alethzero/MainWin.cpp | 40 ++++++--------------------------------- eth/main.cpp | 13 +++++++++++++ ethkey/KeyAux.h | 19 +++++++++++++++++-- libethcore/KeyManager.cpp | 35 ++++++++++++++++++++++++++++++++++ libethcore/KeyManager.h | 3 +++ 5 files changed, 74 insertions(+), 36 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 406d639cc..8bb86f850 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1017,46 +1017,18 @@ void Main::on_claimPresale_triggered() QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); try { - js::mValue val; - json_spirit::read_string(asString(dev::contents(s.toStdString())), val); - auto obj = val.get_obj(); - if (obj["encseed"].type() == js::str_type) - { - auto encseed = fromHex(obj["encseed"].get_str()); - KeyPair k; - for (bool gotit = false; !gotit;) - { - gotit = true; - k = KeyPair::fromEncryptedSeed(&encseed, QInputDialog::getText(this, "Enter Password", "Enter the wallet's passphrase", QLineEdit::Password).toStdString()); - if (obj["ethaddr"].type() == js::str_type) - { - Address a(obj["ethaddr"].get_str()); - Address b = k.address(); - if (a != b) - { - if (QMessageBox::warning(this, "Password Wrong", "Could not import the secret key: the password you gave appears to be wrong.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) - return; - else - gotit = false; - } - } - } - - cnote << k.address(); - if (!m_keyManager.hasAccount(k.address())) - ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice()); - else - QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); - } + KeyPair k = m_keyManager.presaleSecret(dev::contentsString(s.toStdString()), [&](bool){ return QInputDialog::getText(this, "Enter Password", "Enter the wallet's passphrase", QLineEdit::Password).toStdString(); }); + cnote << k.address(); + if (!m_keyManager.hasAccount(k.address())) + ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice()); else - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("encseed type is not js::str_type") ); - + QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); } + catch (dev::eth::PasswordUnknown&) {} catch (...) { cerr << "Unhandled exception!" << endl << boost::current_exception_diagnostic_information(); - QMessageBox::warning(this, "Key File Invalid", "Could not find secret key definition. This is probably not an Ethereum key file."); } } diff --git a/eth/main.cpp b/eth/main.cpp index f85e7096f..ce3007241 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -140,6 +140,7 @@ void help() << " -R,--rebuild Rebuild the blockchain from the existing database." << endl << " --rescue Attempt to rescue a corrupt database." << endl << endl + << " --import-presale Import a presale key; you'll need to type the password to this." << endl << " -s,--import-secret Import a secret key into the key store and use as the default." << endl << " -S,--import-session-secret Import a secret key into the key store and use as the default for this session only." << endl << " --sign-key
Sign all transactions with the key of the given address." << endl @@ -226,6 +227,12 @@ void version() exit(0); } +void importPresale(KeyManager& _km, string const& _file, function _pass) +{ + KeyPair k = _km.presaleSecret(contentsString(_file), [&](bool){ return _pass(); }); + _km.import(k.secret(), "Presale wallet" + _file + " (insecure)"); +} + Address c_config = Address("ccdeac59d35627b7de09332e819d5159e7bb7250"); string pretty(h160 _a, dev::eth::State const& _st) { @@ -1120,6 +1127,7 @@ int main(int argc, char** argv) Address signingKey; Address sessionKey; Address beneficiary = signingKey; + strings presaleImports; /// Structured logging params bool structuredLogging = false; @@ -1434,6 +1442,8 @@ int main(int argc, char** argv) pinning = true; else if (arg == "--hermit") pinning = disableDiscovery = true; + else if (arg == "--import-presale" && i + 1 < argc) + presaleImports.push_back(argv[++i]); else if (arg == "-f" || arg == "--force-mining") forceMining = true; else if (arg == "--old-interactive") @@ -1711,6 +1721,9 @@ int main(int argc, char** argv) keyManager.create(masterPassword); } + for (auto const& presale: presaleImports) + importPresale(keyManager, presale, [&](){ return getPassword("Enter your wallet password for " + presale + ": "); }); + for (auto const& s: toImport) { keyManager.import(s, "Imported key (UNSAFE)"); diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h index af7d8e048..5f97375c8 100644 --- a/ethkey/KeyAux.h +++ b/ethkey/KeyAux.h @@ -104,6 +104,7 @@ public: New, Import, ImportWithAddress, + ImportPresale, Export, Recode, Kill, @@ -143,7 +144,7 @@ public: m_mode = OperationMode::DecodeTx; else if (arg == "--import-bare") m_mode = OperationMode::ImportBare; - else if (arg == "--list-bare") + else if (arg == "-l" || arg == "--list-bare") m_mode = OperationMode::ListBare; else if (arg == "--export-bare") m_mode = OperationMode::ExportBare; @@ -168,7 +169,13 @@ public: m_inputs = strings(1, argv[++i]); m_name = argv[++i]; } - else if ((arg == "-i" || arg == "--import-with-address") && i + 3 < argc) + else if (arg == "--import-presale" && i + 2 < argc) + { + m_mode = OperationMode::ImportPresale; + m_inputs = strings(1, argv[++i]); + m_name = argv[++i]; + } + else if (arg == "--import-with-address" && i + 3 < argc) { m_mode = OperationMode::ImportWithAddress; m_inputs = strings(1, argv[++i]); @@ -477,6 +484,13 @@ public: cout << " UUID: " << toUUID(u) << endl; break; } + case OperationMode::ImportPresale: + { + std::string pw; + KeyPair k = wallet.presaleSecret(contentsString(m_inputs[0]), [&](bool){ return (pw = getPassword("Enter the password for the presale key: ")); }); + wallet.import(k.secret(), m_name, pw, "Same password as used for presale key"); + break; + } case OperationMode::List: { vector bare; @@ -530,6 +544,7 @@ public: << " -l,--list List all keys available in wallet." << endl << " -n,--new Create a new key with given name and add it in the wallet." << endl << " -i,--import [||] Import keys from given source and place in wallet." << endl + << " --import-presale Import a presale wallet into a key with the given name." << endl << " --import-with-address [||]
Import keys from given source with given address and place in wallet." << endl << " -e,--export [
| , ... ] Export given keys." << endl << " -r,--recode [
|| , ... ] Decrypt and re-encrypt given keys." << endl diff --git a/libethcore/KeyManager.cpp b/libethcore/KeyManager.cpp index 26cf451d0..3b1da2562 100644 --- a/libethcore/KeyManager.cpp +++ b/libethcore/KeyManager.cpp @@ -23,12 +23,14 @@ #include #include #include +#include #include #include #include using namespace std; using namespace dev; using namespace eth; +namespace js = json_spirit; namespace fs = boost::filesystem; KeyManager::KeyManager(string const& _keysFile, string const& _secretsPath): @@ -216,6 +218,39 @@ void KeyManager::kill(Address const& _a) write(m_keysFile); } +KeyPair KeyManager::presaleSecret(std::string const& _json, function const& _password) +{ + js::mValue val; + json_spirit::read_string(_json, val); + auto obj = val.get_obj(); + string p = _password(true); + if (obj["encseed"].type() == js::str_type) + { + auto encseed = fromHex(obj["encseed"].get_str()); + KeyPair k; + for (bool gotit = false; !gotit;) + { + gotit = true; + k = KeyPair::fromEncryptedSeed(&encseed, p); + if (obj["ethaddr"].type() == js::str_type) + { + Address a(obj["ethaddr"].get_str()); + Address b = k.address(); + if (a != b) + { + if ((p = _password(false)).empty()) + BOOST_THROW_EXCEPTION(PasswordUnknown()); + else + gotit = false; + } + } + } + return k; + } + else + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("encseed type is not js::str_type")); +} + Addresses KeyManager::accounts() const { Addresses ret; diff --git a/libethcore/KeyManager.h b/libethcore/KeyManager.h index a2b5a4e07..86419c0ef 100644 --- a/libethcore/KeyManager.h +++ b/libethcore/KeyManager.h @@ -123,6 +123,9 @@ public: static std::string defaultPath() { return getDataDir("ethereum") + "/keys.info"; } + /// Extracts the secret key from the presale wallet. + KeyPair presaleSecret(std::string const& _json, std::function const& _password); + private: std::string getPassword(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; std::string getPassword(h256 const& _passHash, std::function const& _pass = DontKnowThrow) const;