diff --git a/CMakeLists.txt b/CMakeLists.txt index 7eeab79d2..45a1fafef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -377,7 +377,8 @@ if (TOOLS) endif() if (NCURSES) - add_subdirectory(neth) + # Commented out until caktux refactors with the new wallet API. +# add_subdirectory(neth) endif () if (GUI) diff --git a/alethzero/Context.h b/alethzero/Context.h index 20c9696f9..4a52366db 100644 --- a/alethzero/Context.h +++ b/alethzero/Context.h @@ -29,7 +29,7 @@ class QComboBox; -namespace dev { namespace eth { struct StateDiff; } } +namespace dev { namespace eth { struct StateDiff; class KeyManager; } } #define Small "font-size: small; " #define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; " @@ -64,5 +64,8 @@ public: virtual std::pair fromString(std::string const& _a) const = 0; virtual std::string renderDiff(dev::eth::StateDiff const& _d) const = 0; virtual std::string render(dev::Address const& _a) const = 0; + virtual dev::Secret retrieveSecret(dev::Address const& _a) const = 0; + virtual dev::eth::KeyManager& keyManager() = 0; + }; diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 736af8684..5b0ad7582 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -548,12 +548,15 @@ QFrame::NoFrame - - QAbstractItemView::InternalMove + + false true + + true + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 437e75576..87c9c2dc9 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +144,38 @@ Main::Main(QWidget *parent) : // ui->log->addItem(QString::fromStdString(s)); }; + // Open Key Store + bool opened = false; + if (m_keyManager.exists()) + while (!opened) + { + QString s = QInputDialog::getText(nullptr, "Master password", "Enter your MASTER account password.", QLineEdit::Password, QString()); + if (m_keyManager.load(s.toStdString())) + opened = true; + else if (QMessageBox::question( + nullptr, + "Invalid password entered", + "The password you entered is incorrect. If you have forgotten your password, and you wish to start afresh, manually remove the file: " + QString::fromStdString(getDataDir("ethereum")) + "/keys.info", + QMessageBox::Retry, + QMessageBox::Abort) + == QMessageBox::Abort) + exit(0); + } + if (!opened) + { + QString password; + while (true) + { + password = QInputDialog::getText(nullptr, "Master password", "Enter a MASTER password for your key store. Make it strong. You probably want to write it down somewhere and keep it safe and secure; your identity will rely on this - you never want to lose it.", QLineEdit::Password, QString()); + QString confirm = QInputDialog::getText(nullptr, "Master password", "Confirm this password by typing it again", QLineEdit::Password, QString()); + if (password == confirm) + break; + QMessageBox::warning(nullptr, "Try again", "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok); + } + m_keyManager.create(password.toStdString()); + m_keyManager.import(Secret::random(), "Default identity"); + } + #if ETH_DEBUG m_servers.append("127.0.0.1:30300"); #endif @@ -168,15 +201,13 @@ Main::Main(QWidget *parent) : ui->blockCount->setText(QString("PV%1.%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(eth::c_minorProtocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version)); - connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); - QSettings s("ethereum", "alethzero"); m_networkConfig = s.value("peers").toByteArray(); bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size()); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth", "shh"}, p2p::NetworkPreferences(), network)); m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); - m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this)); + m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this)); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); @@ -198,6 +229,7 @@ Main::Main(QWidget *parent) : // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // QWebEngineInspector* inspector = new QWebEngineInspector(); // inspector->setPage(page); + setBeneficiary(*m_keyManager.accounts().begin()); readSettings(); #if !ETH_FATDB removeDockWidget(ui->dockWidget_accounts); @@ -358,9 +390,9 @@ void Main::installBalancesWatch() // TODO: Update for new currencies reg. for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i) altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); - for (auto i: m_myKeys) + for (auto const& i: m_keyManager.accounts()) for (auto c: altCoins) - tf.address(c).topic(0, h256(i.address(), h256::AlignRight)); + tf.address(c).topic(0, h256(i, h256::AlignRight)); uninstallWatch(m_balancesFilter); m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); }); @@ -429,7 +461,7 @@ void Main::load(QString _s) void Main::on_newTransaction_triggered() { - m_transact.setEnvironment(m_myKeys, ethereum(), &m_natSpecDB); + m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); m_transact.exec(); } @@ -616,17 +648,7 @@ void Main::on_paranoia_triggered() void Main::writeSettings() { QSettings s("ethereum", "alethzero"); - { - QByteArray b; - b.resize(sizeof(Secret) * m_myKeys.size()); - auto p = b.data(); - for (auto i: m_myKeys) - { - memcpy(p, &(i.secret()), sizeof(Secret)); - p += sizeof(Secret); - } - s.setValue("address", b); - } + s.remove("address"); { QByteArray b; b.resize(sizeof(Secret) * m_myIdentities.size()); @@ -666,6 +688,20 @@ void Main::writeSettings() s.setValue("windowState", saveState()); } +Secret Main::retrieveSecret(Address const& _a) const +{ + auto info = m_keyManager.accountDetails()[_a]; + while (true) + { + if (Secret s = m_keyManager.secret(_a, [&](){ + return QInputDialog::getText(const_cast(this), "Import Account Key", QString("Enter the password for the account %2 (%1). The hint is:\n%3").arg(QString::fromStdString(_a.abridged())).arg(QString::fromStdString(info.first)).arg(QString::fromStdString(info.second)), QLineEdit::Password).toStdString(); + })) + return s; + else if (QMessageBox::warning(const_cast(this), "Incorrect Password", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) + return Secret(); + } +} + void Main::readSettings(bool _skipGeometry) { QSettings s("ethereum", "alethzero"); @@ -675,22 +711,17 @@ void Main::readSettings(bool _skipGeometry) restoreState(s.value("windowState").toByteArray()); { - m_myKeys.clear(); QByteArray b = s.value("address").toByteArray(); - if (b.isEmpty()) - m_myKeys.append(KeyPair::create()); - else + if (!b.isEmpty()) { h256 k; for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) { memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); - if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k))) - m_myKeys.append(KeyPair(k)); + if (!m_keyManager.accounts().count(KeyPair(k).address())) + m_keyManager.import(k, "Imported (UNSAFE) key."); } } - ethereum()->setAddress(m_myKeys.back().address()); - m_server->setAccounts(keysAsVector(m_myKeys)); } { @@ -735,16 +766,38 @@ void Main::readSettings(bool _skipGeometry) on_urlEdit_returnPressed(); } +std::string Main::getPassword(std::string const& _title, std::string const& _for) +{ + QString password; + while (true) + { + password = QInputDialog::getText(nullptr, QString::fromStdString(_title), QString::fromStdString(_for), QLineEdit::Password, QString()); + QString confirm = QInputDialog::getText(nullptr, QString::fromStdString(_title), "Confirm this password by typing it again", QLineEdit::Password, QString()); + if (password == confirm) + break; + QMessageBox::warning(nullptr, QString::fromStdString(_title), "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok); + } + return password.toStdString(); +} + void Main::on_importKey_triggered() { - QString s = QInputDialog::getText(this, "Import Account Key", "Enter account's secret key"); + QString s = QInputDialog::getText(this, "Import Account Key", "Enter account's secret key", QLineEdit::Password); bytes b = fromHex(s.toStdString()); if (b.size() == 32) { auto k = KeyPair(h256(b)); - if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end()) + if (!m_keyManager.accounts().count(k.address())) { - m_myKeys.append(k); + QString s = QInputDialog::getText(this, "Import Account Key", "Enter this account's name"); + if (QMessageBox::question(this, "Additional Security?", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + std::string password = getPassword("Import Account Key", "Enter the password you would like to use for this key. Don't forget it!"); + std::string hint = QInputDialog::getText(this, "Import Account Key", "Enter a hint to help you remember this password.").toStdString(); + m_keyManager.import(k.secret(), s.toStdString(), password, hint); + } + else + m_keyManager.import(k.secret(), s.toStdString()); keysChanged(); } else @@ -785,15 +838,8 @@ void Main::on_importKeyFile_triggered() } cnote << k.address(); - if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end()) - { - if (m_myKeys.empty()) - { - m_myKeys.push_back(KeyPair::create()); - keysChanged(); - } - ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_myKeys.back().address(), {}, c_txGas, gasPrice()); - } + if (!m_keyManager.accounts().count(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."); } @@ -812,10 +858,12 @@ void Main::on_importKeyFile_triggered() void Main::on_exportKey_triggered() { - if (ui->ourAccounts->currentRow() >= 0 && ui->ourAccounts->currentRow() < m_myKeys.size()) + if (ui->ourAccounts->currentRow() >= 0) { - auto k = m_myKeys[ui->ourAccounts->currentRow()]; - QMessageBox::information(this, "Export Account Key", "Secret key to account " + QString::fromStdString(render(k.address()) + " is:\n" + toHex(k.sec().ref()))); + auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); + Address h((byte const*)hba.data(), Address::ConstructFromPointer); + Secret s = retrieveSecret(h); + QMessageBox::information(this, "Export Account Key", "Secret key to account " + QString::fromStdString(render(h) + " is:\n" + s.hex())); } } @@ -913,6 +961,24 @@ void Main::refreshMining() */ } +void Main::setBeneficiary(Address const& _b) +{ + for (int i = 0; i < ui->ourAccounts->count(); ++i) + { + auto hba = ui->ourAccounts->item(i)->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + ui->ourAccounts->item(i)->setCheckState(h == _b ? Qt::Checked : Qt::Unchecked); + } + m_beneficiary = _b; + ethereum()->setAddress(_b); +} + +void Main::on_ourAccounts_itemClicked(QListWidgetItem* _i) +{ + auto hba = _i->data(Qt::UserRole).toByteArray(); + setBeneficiary(Address((byte const*)hba.data(), Address::ConstructFromPointer)); +} + void Main::refreshBalances() { cwatch << "refreshBalances()"; @@ -931,11 +997,13 @@ void Main::refreshBalances() // cdebug << n << addr << denom << sha3(h256(n).asBytes()); altCoins[addr] = make_tuple(fromRaw(n), 0, denom); }*/ - for (auto i: m_myKeys) + for (pair> const& i: m_keyManager.accountDetails()) { - u256 b = ethereum()->balanceAt(i.address()); - (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.address()))).arg((unsigned)ethereum()->countAt(i.address())), ui->ourAccounts)) - ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size)); + u256 b = ethereum()->balanceAt(i.first); + QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.first))).arg((unsigned)ethereum()->countAt(i.first)).arg(QString::fromStdString(i.second.first)), ui->ourAccounts); + li->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); + li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + li->setCheckState(m_beneficiary == i.first ? Qt::Checked : Qt::Unchecked); totalBalance += b; // for (auto& c: altCoins) @@ -1385,23 +1453,6 @@ void Main::on_transactionQueue_currentItemChanged() ui->pendingInfo->moveCursor(QTextCursor::Start); } -void Main::ourAccountsRowsMoved() -{ - QList myKeys; - for (int i = 0; i < ui->ourAccounts->count(); ++i) - { - auto hba = ui->ourAccounts->item(i)->data(Qt::UserRole).toByteArray(); - auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); - for (auto i: m_myKeys) - if (i.address() == h) - myKeys.push_back(i); - } - m_myKeys = myKeys; - - if (m_server.get()) - m_server->setAccounts(keysAsVector(m_myKeys)); -} - void Main::on_inject_triggered() { QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex"); @@ -1827,7 +1878,7 @@ void Main::on_mine_triggered() { if (ui->mine->isChecked()) { - ethereum()->setAddress(m_myKeys.last().address()); + ethereum()->setAddress(m_beneficiary); ethereum()->startMining(); } else @@ -1837,7 +1888,6 @@ void Main::on_mine_triggered() void Main::keysChanged() { onBalancesChange(); - m_server->setAccounts(keysAsVector(m_myKeys)); } bool beginsWith(Address _a, bytes const& _b) @@ -1901,24 +1951,39 @@ void Main::on_newAccount_triggered() t->join(); delete t; } - m_myKeys.append(p); + + QString s = QInputDialog::getText(this, "Create Account", "Enter this account's name"); + if (QMessageBox::question(this, "Create Account", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!"); + std::string hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString(); + m_keyManager.import(p.secret(), s.toStdString(), password, hint); + } + else + m_keyManager.import(p.secret(), s.toStdString()); keysChanged(); } void Main::on_killAccount_triggered() { - if (ui->ourAccounts->currentRow() >= 0 && ui->ourAccounts->currentRow() < m_myKeys.size()) + if (ui->ourAccounts->currentRow() >= 0) { - auto k = m_myKeys[ui->ourAccounts->currentRow()]; + auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); + Address h((byte const*)hba.data(), Address::ConstructFromPointer); + auto k = m_keyManager.accountDetails()[h]; if ( - ethereum()->balanceAt(k.address()) != 0 && - QMessageBox::critical(this, "Kill Account?!", - QString::fromStdString("Account " + render(k.address()) + " has " + formatBalance(ethereum()->balanceAt(k.address())) + " in it. It, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" + ethereum()->balanceAt(h) != 0 && + QMessageBox::critical(this, QString::fromStdString("Kill Account " + k.first + "?!"), + QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it. It, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" "Are you sure you want to continue?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) return; - m_myKeys.erase(m_myKeys.begin() + ui->ourAccounts->currentRow()); + m_keyManager.kill(h); + if (m_keyManager.accounts().empty()) + m_keyManager.import(Secret::random(), "Default account"); keysChanged(); + if (m_beneficiary == h) + setBeneficiary(*m_keyManager.accounts().begin()); } } @@ -1929,7 +1994,8 @@ void Main::on_go_triggered() ui->net->setChecked(true); on_net_triggered(); } - web3()->addNode(p2p::NodeId(), Host::pocHost()); + for (auto const& i: Host::pocHosts()) + web3()->requirePeer(i.first, i.second); } std::string Main::prettyU256(dev::u256 const& _n) const diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 127b174c6..51d4cc8f4 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include "Context.h" @@ -42,6 +43,8 @@ #include "NatspecHandler.h" #include "Connect.h" +class QListWidgetItem; + namespace Ui { class Main; } @@ -86,10 +89,14 @@ public: std::pair fromString(std::string const& _a) const override; std::string renderDiff(dev::eth::StateDiff const& _d) const override; - QList owned() const { return m_myIdentities + m_myKeys; } + QList owned() const { return m_myIdentities; } dev::u256 gasPrice() const { return 10 * dev::eth::szabo; } + dev::eth::KeyManager& keyManager() override { return m_keyManager; } + + dev::Secret retrieveSecret(dev::Address const& _a) const override; + public slots: void load(QString _file); void note(QString _entry); @@ -144,7 +151,7 @@ private slots: void on_exportState_triggered(); // Stuff concerning the blocks/transactions/accounts panels - void ourAccountsRowsMoved(); + void on_ourAccounts_itemClicked(QListWidgetItem* _i); void on_ourAccounts_doubleClicked(); void on_accounts_doubleClicked(); void on_accounts_currentItemChanged(); @@ -236,6 +243,9 @@ private: void refreshBlockCount(); void refreshBalances(); + void setBeneficiary(dev::Address const& _b); + std::string getPassword(std::string const& _title, std::string const& _for); + std::unique_ptr ui; std::unique_ptr m_webThree; @@ -247,10 +257,11 @@ private: QByteArray m_networkConfig; QStringList m_servers; - QList m_myKeys; QList m_myIdentities; + dev::eth::KeyManager m_keyManager; QString m_privateChain; dev::Address m_nameReg; + dev::Address m_beneficiary; QList> m_consoleHistory; QMutex m_logLock; diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 161bb4926..d3ee4f41b 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -20,23 +20,23 @@ */ #include "OurWebThreeStubServer.h" - #include #include #include #include - #include "MainWin.h" - using namespace std; using namespace dev; using namespace dev::eth; -OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, - vector const& _accounts, Main* _main): - WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(_main) +OurWebThreeStubServer::OurWebThreeStubServer( + jsonrpc::AbstractServerConnector& _conn, + WebThreeDirect& _web3, + Main* _main +): + WebThreeStubServer(_conn, _web3, make_shared(_web3, _main), _main->owned().toVector().toStdVector()), + m_main(_main) { - connect(_main, SIGNAL(poll()), this, SLOT(doValidations())); } string OurWebThreeStubServer::shh_newIdentity() @@ -46,7 +46,18 @@ string OurWebThreeStubServer::shh_newIdentity() return toJS(kp.pub()); } -bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text) +OurAccountHolder::OurAccountHolder( + WebThreeDirect& _web3, + Main* _main +): + AccountHolder([=](){ return m_web3->ethereum(); }), + m_web3(&_web3), + m_main(_main) +{ + connect(_main, SIGNAL(poll()), this, SLOT(doValidations())); +} + +bool OurAccountHolder::showAuthenticationPopup(string const& _title, string const& _text) { if (!m_main->confirm()) { @@ -66,18 +77,18 @@ bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string //return button == QMessageBox::Ok; } -bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy) +bool OurAccountHolder::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy) { return showAuthenticationPopup("Contract Creation Transaction", string("ÐApp is attemping to create a contract; ") + (_toProxy ? "(this transaction is not executed directly, but forwarded to another ÐApp) " : "") + "to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + "."); } -bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t, bool _toProxy) +bool OurAccountHolder::showSendNotice(TransactionSkeleton const& _t, bool _toProxy) { return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to) + (_toProxy ? " (this transaction is not executed directly, but forwarded to another ÐApp)" : "") + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + "."); } -bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy) +bool OurAccountHolder::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy) { return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!", "ÐApp is attempting to call into an unknown contract at address " + @@ -93,25 +104,47 @@ bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t, "REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!"); } -void OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t, bool _toProxy) +void OurAccountHolder::authenticate(TransactionSkeleton const& _t) { Guard l(x_queued); - m_queued.push(make_pair(_t, _toProxy)); + m_queued.push(_t); } -void OurWebThreeStubServer::doValidations() +void OurAccountHolder::doValidations() { Guard l(x_queued); while (!m_queued.empty()) { - auto q = m_queued.front(); + auto t = m_queued.front(); m_queued.pop(); - if (validateTransaction(q.first, q.second)) - WebThreeStubServerBase::authenticate(q.first, q.second); + + bool proxy = isProxyAccount(t.from); + if (!proxy && !isRealAccount(t.from)) + { + cwarn << "Trying to send from non-existant account" << t.from; + return; + } + + // TODO: determine gas price. + + if (!validateTransaction(t, proxy)) + return; + + if (proxy) + queueTransaction(t); + else + // sign and submit. + if (Secret s = m_main->retrieveSecret(t.from)) + m_web3->ethereum()->submitTransaction(s, t); } } -bool OurWebThreeStubServer::validateTransaction(TransactionSkeleton const& _t, bool _toProxy) +AddressHash OurAccountHolder::realAccounts() const +{ + return m_main->keyManager().accounts(); +} + +bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy) { if (_t.creation) { diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h index 95cf70438..a07188b2d 100644 --- a/alethzero/OurWebThreeStubServer.h +++ b/alethzero/OurWebThreeStubServer.h @@ -25,26 +25,29 @@ #include #include #include +#include class Main; -class OurWebThreeStubServer: public QObject, public WebThreeStubServer +class OurAccountHolder: public QObject, public dev::eth::AccountHolder { Q_OBJECT public: - OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, - std::vector const& _accounts, Main* main); - - virtual std::string shh_newIdentity() override; - virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy); - -signals: - void onNewId(QString _s); + OurAccountHolder( + dev::WebThreeDirect& _web3, + Main* _main + ); public slots: void doValidations(); +protected: + // easiest to return keyManager.addresses(); + virtual dev::AddressHash realAccounts() const override; + // use web3 to submit a signed transaction to accept + virtual void authenticate(dev::eth::TransactionSkeleton const& _t) override; + private: bool showAuthenticationPopup(std::string const& _title, std::string const& _text); bool showCreationNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy); @@ -53,9 +56,29 @@ private: bool validateTransaction(dev::eth::TransactionSkeleton const& _t, bool _toProxy); - std::queue> m_queued; + std::queue m_queued; dev::Mutex x_queued; dev::WebThreeDirect* m_web3; Main* m_main; }; + +class OurWebThreeStubServer: public QObject, public WebThreeStubServer +{ + Q_OBJECT + +public: + OurWebThreeStubServer( + jsonrpc::AbstractServerConnector& _conn, + dev::WebThreeDirect& _web3, + Main* main + ); + + virtual std::string shh_newIdentity() override; + +signals: + void onNewId(QString _s); + +private: + Main* m_main; +}; diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 2041bf39d..bc37db8ef 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #if ETH_SERPENT #include #include @@ -69,11 +70,20 @@ Transact::~Transact() delete ui; } -void Transact::setEnvironment(QList _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB) +void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB) { - m_myKeys = _myKeys; + m_accounts = _accounts; m_ethereum = _eth; m_natSpecDB = _natSpecDB; + + ui->from->clear(); + for (auto const& i: m_accounts) + { + auto d = m_context->keyManager().accountDetails()[i]; + u256 b = ethereum()->balanceAt(i, PendingBlock); + QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(i))).arg(QString::fromStdString(d.first)); + ui->from->addItem(s); + } } bool Transact::isCreation() const @@ -126,8 +136,8 @@ void Transact::updateFee() ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); bool ok = false; - for (auto i: m_myKeys) - if (ethereum()->balanceAt(i.address()) >= totalReq) + for (auto const& i: m_accounts) + if (ethereum()->balanceAt(i) >= totalReq) { ok = true; break; @@ -388,22 +398,33 @@ Secret Transact::findSecret(u256 _totalReq) const if (!ethereum()) return Secret(); - Secret best; + Address best; u256 bestBalance = 0; - for (auto const& i: m_myKeys) + for (auto const& i: m_accounts) { - auto b = ethereum()->balanceAt(i.address(), PendingBlock); + auto b = ethereum()->balanceAt(i, PendingBlock); if (b >= _totalReq) - return i.secret(); + { + best = i; + break; + } if (b > bestBalance) - bestBalance = b, best = i.secret(); + bestBalance = b, best = i; } - return best; + return m_context->retrieveSecret(best); +} + +Address Transact::fromAccount() +{ + auto it = m_accounts.begin(); + std::advance(it, ui->from->currentIndex()); + return *it; } void Transact::on_send_clicked() { - Secret s = findSecret(value() + fee()); +// Secret s = findSecret(value() + fee()); + Secret s = m_context->retrieveSecret(fromAccount()); auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock); if (!s || b < value() + fee()) { @@ -440,9 +461,10 @@ void Transact::on_send_clicked() void Transact::on_debug_clicked() { - Secret s = findSecret(value() + fee()); - auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock); - if (!s || b < value() + fee()) +// Secret s = findSecret(value() + fee()); + Address from = fromAccount(); + auto b = ethereum()->balanceAt(from, PendingBlock); + if (!from || b < value() + fee()) { QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); return; @@ -452,8 +474,9 @@ void Transact::on_debug_clicked() { State st(ethereum()->postState()); Transaction t = isCreation() ? - Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(dev::toAddress(s)), s) : - Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, st.transactionsFrom(dev::toAddress(s)), s); + Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(from)) : + Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, st.transactionsFrom(from)); + t.forceSender(from); Debugger dw(m_context, this); Executive e(st, ethereum()->blockChain(), 0); dw.populate(e, t); diff --git a/alethzero/Transact.h b/alethzero/Transact.h index 71bc393b2..cd62c0e20 100644 --- a/alethzero/Transact.h +++ b/alethzero/Transact.h @@ -41,7 +41,7 @@ public: explicit Transact(Context* _context, QWidget* _parent = 0); ~Transact(); - void setEnvironment(QList _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); + void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); private slots: void on_destination_currentTextChanged(QString); @@ -60,6 +60,7 @@ private: dev::eth::Client* ethereum() const { return m_ethereum; } void rejigData(); + dev::Address fromAccount(); void updateDestination(); void updateFee(); bool isCreation() const; @@ -76,7 +77,7 @@ private: unsigned m_backupGas = 0; dev::bytes m_data; - QList m_myKeys; + dev::AddressHash m_accounts; dev::eth::Client* m_ethereum = nullptr; Context* m_context = nullptr; NatSpecFace* m_natSpecDB = nullptr; diff --git a/alethzero/Transact.ui b/alethzero/Transact.ui index 75af9ba98..f7a5e7c0e 100644 --- a/alethzero/Transact.ui +++ b/alethzero/Transact.ui @@ -14,91 +14,69 @@ Transact - - - - + + + + + 0 + 0 + - - 430000000 + + D&ata - - 0 + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + data - - + + - &Amount + &Optimise - - value + + true - - - - false + + + + gas - - true + + 1 - - + + 430000000 + + + 10000 - - - - Qt::Vertical + + + + &Cancel + + + Esc - - - QFrame::NoFrame - - - 0 - - - - - Qt::ClickFocus - - - QFrame::NoFrame - - - 0 - - - true - - - - - - - - - - 0 - 0 - - + + - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + &Debug - + @@ -114,53 +92,68 @@ - - - - - + + + + + 0 + 0 + + - &Debug + - - - - &Execute + + + + - - false + + 430000000 + + + 0 + + + - + - &Gas + &Amount - gas + value - - - - gas - - - 1 + + + + &Execute - - 430000000 + + false - - 10000 + + + + + + true + + + (Create Contract) + + - + @ @@ -173,49 +166,63 @@ - - - - &Optimise - - - true + + + + Qt::Vertical + + + QFrame::NoFrame + + + 0 + + + + + Qt::ClickFocus + + + QFrame::NoFrame + + + 0 + + + true + + - - - - 0 - 0 - - + - D&ata - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + &Gas - data + gas - - - + + + + false + + true - - - (Create Contract) - - + + + - - + + + + + 0 @@ -225,18 +232,24 @@ + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - - + + - &Cancel + &From - - Esc + + from + + + diff --git a/eth/main.cpp b/eth/main.cpp index a5db1eecb..60d454287 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #if ETH_JSCONSOLE || !ETH_TRUE #include @@ -46,6 +47,7 @@ #include #endif #if ETH_JSONRPC || !ETH_TRUE +#include #include #include #include @@ -89,10 +91,8 @@ void interactiveHelp() << " minestart Starts mining." << endl << " minestop Stops mining." << endl << " mineforce Forces mining, even when there are no transactions." << endl - << " address Gives the current address." << endl - << " secret Gives the current secret" << endl << " block Gives the current block height." << endl - << " balance Gives the current balance." << endl + << " accounts Gives information on all owned accounts (balances, mining beneficiary and default signer)." << endl << " transact Execute a given transaction." << endl << " send Execute a given transaction with current secret." << endl << " contract Create a new contract with current secret." << endl @@ -101,7 +101,7 @@ void interactiveHelp() << " listaccounts List the accounts on the network." << endl << " listcontracts List the contracts on the network." << endl #endif - << " setsecret Set the secret to the hex secret key." << endl + << " setsigningkey Set the address with which to sign transactions." << endl << " setaddress Set the coinbase (mining payout) address." << endl << " exportconfig Export the config (.RLP) to the path provided." << endl << " importconfig Import the config (.RLP) from the path provided." << endl @@ -125,13 +125,18 @@ void help() #endif << " -K,--kill First kill the blockchain." << endl << " -R,--rebuild Rebuild the blockchain from the existing database." << endl - << " -s,--secret Set the secret key for use with send command (default: auto)." << endl - << " -S,--session-secret Set the secret key for use with send command, for this session only." << 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 + << " --session-sign-key
Sign all transactions with the key of the given address for this session only." << endl << " --master Give the master password for the key store." << endl + << " --password Give a password for a private key." << endl + << endl << "Client transacting:" << endl << " -B,--block-fees Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl << " -e,--ether-price Set the ether price in the reference unit e.g. ¢ (default: 30.679)." << endl << " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl + << endl << "Client mining:" << endl << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl << " -m,--mining Enable mining, optionally for a specified number of blocks (default: off)" << endl @@ -141,6 +146,7 @@ void help() << " --opencl-platform When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl << " --opencl-device When mining using -G/--opencl use OpenCL device n (default: 0)." << endl << " -t, --mining-threads Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl + << endl << "Client networking:" << endl << " --client-name Add a name to your client's version string (default: blank)." << endl << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl @@ -153,12 +159,15 @@ void help() << " --network-id Only connect to other hosts with this network id (default:0)." << endl << " --upnp Use UPnP for NAT (default: on)." << endl #if ETH_JSONRPC || !ETH_TRUE + << endl << "Work farming mode:" << endl << " -F,--farm Put into mining farm mode with the work server at URL. Use with -G/--opencl." << endl << " --farm-recheck Leave n ms between checks for changed work (default: 500)." << endl #endif + << endl << "Ethash verify mode:" << endl << " -w,--check-pow Check PoW credentials for validity." << endl + << endl << "Benchmarking mode:" << endl << " -M,--benchmark Benchmark for mining and exit; use with --cpu and --opencl." << endl << " --benchmark-warmup Set the duration of warmup for the benchmark tests (default: 3)." << endl @@ -167,14 +176,17 @@ void help() #if ETH_JSONRPC || !ETH_TRUE << " --phone-home When benchmarking, publish results (default: on)" << endl #endif + << endl << "DAG creation mode:" << endl << " -D,--create-dag Create the DAG in preparation for mining on given block and exit." << endl + << endl << "Import/export modes:" << endl << " -I,--import Import file as a concatenated series of blocks and exit." << endl << " -E,--export Export file as a concatenated series of blocks and exit." << endl << " --from Export only from block n; n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl << " --to Export only to block n (inclusive); n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl << " --only Equivalent to --export-from n --export-to n." << endl + << endl << "General Options:" << endl << " -d,--db-path Load database from path (default: " << getDataDir() << ")" << endl #if ETH_EVMJIT || !ETH_TRUE @@ -537,13 +549,14 @@ int main(int argc, char** argv) /// Mining params unsigned mining = 0; bool forceMining = false; - KeyPair sigKey = KeyPair::create(); - Secret sessionSecret; - Address coinbase = sigKey.address(); + Address signingKey; + Address sessionKey; + Address beneficiary = signingKey; /// Structured logging params bool structuredLogging = false; string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S"; + string structuredLoggingURL; /// Transaction params TransactionPriority priority = TransactionPriority::Medium; @@ -568,11 +581,20 @@ int main(int argc, char** argv) string configFile = getDataDir() + "/config.rlp"; bytes b = contents(configFile); + + strings passwordsToNote; + Secrets toImport; if (b.size()) { RLP config(b); - sigKey = KeyPair(config[0].toHash()); - coinbase = config[1].toHash
(); + if (config[0].size() == 32) // secret key - import and forget. + { + Secret s = config[0].toHash(); + toImport.push_back(s); + } + else // new format - just use it as an address. + signingKey = config[0].toHash
(); + beneficiary = config[1].toHash
(); } for (int i = 1; i < argc; ++i) @@ -600,6 +622,8 @@ int main(int argc, char** argv) cerr << "-p is DEPRECATED. It will be removed for the Frontier. Use --port instead (or place directly as host:port)." << endl; remotePort = (short)atoi(argv[++i]); } + else if (arg == "--password" && i + 1 < argc) + passwordsToNote.push_back(argv[++i]); else if (arg == "--master" && i + 1 < argc) masterPassword = argv[++i]; else if ((arg == "-I" || arg == "--import") && i + 1 < argc) @@ -742,7 +766,7 @@ int main(int argc, char** argv) } else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) try { - coinbase = h160(fromHex(argv[++i], WhenError::Throw)); + beneficiary = h160(fromHex(argv[++i], WhenError::Throw)); } catch (BadHexCharacter&) { @@ -758,14 +782,32 @@ int main(int argc, char** argv) minerType = MinerType::CPU; else if (arg == "-G" || arg == "--opencl") minerType = MinerType::GPU; - else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) - sigKey = KeyPair(h256(fromHex(argv[++i]))); - else if ((arg == "-S" || arg == "--session-secret") && i + 1 < argc) - sessionSecret = h256(fromHex(argv[++i])); + /*<< " -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 + << " --session-sign-key
Sign all transactions with the key of the given address for this session only." << endl*/ + else if ((arg == "-s" || arg == "--import-secret") && i + 1 < argc) + { + Secret s(fromHex(argv[++i])); + toImport.push_back(s); + signingKey = toAddress(s); + } + else if ((arg == "-S" || arg == "--import-session-secret") && i + 1 < argc) + { + Secret s(fromHex(argv[++i])); + toImport.push_back(s); + sessionKey = toAddress(s); + } + else if ((arg == "--sign-key") && i + 1 < argc) + sessionKey = Address(fromHex(argv[++i])); + else if ((arg == "--session-sign-key") && i + 1 < argc) + sessionKey = Address(fromHex(argv[++i])); else if (arg == "--structured-logging-format" && i + 1 < argc) structuredLoggingFormat = string(argv[++i]); else if (arg == "--structured-logging") structuredLogging = true; + else if (arg == "--structured-logging-destination" && i + 1 < argc) + structuredLoggingURL = argv[++i]; else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) dbPath = argv[++i]; else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc) @@ -949,16 +991,24 @@ int main(int argc, char** argv) } } + KeyManager keyManager; + for (auto const& s: passwordsToNote) + keyManager.notePassword(s); + for (auto const& s: toImport) + { + keyManager.import(s, "Imported key"); + if (!signingKey) + signingKey = toAddress(s); + } + { RLPStream config(2); - config << sigKey.secret() << coinbase; + config << signingKey << beneficiary; writeFile(configFile, config.out()); } - if (sessionSecret) - sigKey = KeyPair(sessionSecret); - - + if (sessionKey) + signingKey = sessionKey; if (minerType == MinerType::CPU) ProofOfWork::CPUMiner::setNumInstances(miningThreads); @@ -983,7 +1033,7 @@ int main(int argc, char** argv) if (!clientName.empty()) clientName += "/"; - StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); + StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); @@ -1072,27 +1122,68 @@ int main(int argc, char** argv) c->setGasPricer(gasPricer); c->setForceMining(forceMining); c->setTurboMining(minerType == MinerType::GPU); - c->setAddress(coinbase); + c->setAddress(beneficiary); c->setNetworkId(networkId); } - cout << "Transaction Signer: " << sigKey.address() << endl; - cout << "Mining Benefactor: " << coinbase << endl; + cout << "Transaction Signer: " << signingKey << endl; + cout << "Mining Benefactor: " << beneficiary << endl; web3.startNetwork(); cout << "Node ID: " << web3.enode() << endl; if (bootstrap) - web3.addNode(p2p::NodeId(), Host::pocHost()); + for (auto const& i: Host::pocHosts()) + web3.requirePeer(i.first, i.second); if (remoteHost.size()) web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); -#if ETH_JSONRPC + if (keyManager.exists()) + while (masterPassword.empty()) + { + masterPassword = getPassword("Please enter your MASTER password: "); + if (!keyManager.load(masterPassword)) + { + cout << "Password invalid. Try again." << endl; + masterPassword.clear(); + } + } + else + { + while (masterPassword.empty()) + { + masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): "); + string confirm = getPassword("Please confirm the password by entering it again: "); + if (masterPassword != confirm) + { + cout << "Passwords were different. Try again." << endl; + masterPassword.clear(); + } + } + keyManager.create(masterPassword); + } + + string logbuf; + bool silence = false; + std::string additional; + g_logPost = [&](std::string const& a, char const*) { if (silence) logbuf += a + "\n"; else cout << "\r \r" << a << endl << additional << flush; }; + + // TODO: give hints &c. + auto getPassword = [&](Address const& a){ + auto s = silence; + silence = true; + cout << endl; + string ret = dev::getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); + silence = s; + return ret; + }; + +#if ETH_JSONRPC || !ETH_TRUE shared_ptr jsonrpcServer; unique_ptr jsonrpcConnector; if (jsonrpc > -1) { jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({sigKey}))); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getPassword, keyManager), vector())); jsonrpcServer->StartListening(); } #endif @@ -1103,15 +1194,15 @@ int main(int argc, char** argv) if (interactive) { - string logbuf; + additional = "Press Enter"; string l; while (!g_exit) { - g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << "Press Enter" << flush; }; + silence = false; cout << logbuf << "Press Enter" << flush; std::getline(cin, l); logbuf.clear(); - g_logPost = [&](std::string const& a, char const*) { logbuf += a + "\n"; }; + silence = true; #if ETH_READLINE if (l.size()) @@ -1224,7 +1315,7 @@ int main(int argc, char** argv) iss >> g_logVerbosity; cout << "Verbosity: " << g_logVerbosity << endl; } -#if ETH_JSONRPC +#if ETH_JSONRPC || !ETH_TRUE else if (cmd == "jsonport") { if (iss.peek() != -1) @@ -1236,7 +1327,7 @@ int main(int argc, char** argv) if (jsonrpc < 0) jsonrpc = SensibleHttpPort; jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({sigKey}))); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getPassword, keyManager), vector())); jsonrpcServer->StartListening(); } else if (cmd == "jsonstop") @@ -1248,11 +1339,8 @@ int main(int argc, char** argv) #endif else if (cmd == "address") { - cout << "Current address:" << endl << sigKey.address() << endl; - } - else if (cmd == "secret") - { - cout << "Secret Key: " << sigKey.secret() << endl; + cout << "Current mining beneficiary:" << endl << beneficiary << endl; + cout << "Current signing account:" << endl << signingKey << endl; } else if (c && cmd == "block") { @@ -1267,7 +1355,15 @@ int main(int argc, char** argv) } else if (c && cmd == "balance") { - cout << "Current balance: " << formatBalance( c->balanceAt(sigKey.address())) << " = " <balanceAt(sigKey.address()) << " wei" << endl; + cout << "Current balance:" << endl; + u256 total = 0; + for (auto const& i: keyManager.accountDetails()) + { + auto b = c->balanceAt(i.first); + cout << ((i.first == signingKey) ? "SIGNING " : " ") << ((i.first == beneficiary) ? "COINBASE " : " ") << i.second.first << " (" << i.first << "): " << formatBalance(b) << " = " << b << " wei" << endl; + total += b; + } + cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl; } else if (c && cmd == "transact") { @@ -1383,7 +1479,7 @@ int main(int argc, char** argv) try { Address dest = h160(fromHex(hexAddr, WhenError::Throw)); - c->submitTransaction(sigKey.secret(), amount, dest, bytes(), minGas); + c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), amount, dest, bytes(), minGas); } catch (BadHexCharacter& _e) { @@ -1452,7 +1548,7 @@ int main(int argc, char** argv) else if (gas < minGas) cwarn << "Minimum gas amount is" << minGas; else - c->submitTransaction(sigKey.secret(), endowment, init, gas, gasPrice); + c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), endowment, init, gas, gasPrice); } else cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX"; @@ -1563,13 +1659,13 @@ int main(int argc, char** argv) } } } - else if (cmd == "setsecret") + else if (cmd == "setsigningkey") { if (iss.peek() != -1) { string hexSec; iss >> hexSec; - sigKey = KeyPair(h256(fromHex(hexSec))); + signingKey = Address(fromHex(hexSec)); } else cwarn << "Require parameter: setSecret HEXSECRETKEY"; @@ -1586,7 +1682,7 @@ int main(int argc, char** argv) { try { - coinbase = h160(fromHex(hexAddr, WhenError::Throw)); + beneficiary = h160(fromHex(hexAddr, WhenError::Throw)); } catch (BadHexCharacter& _e) { @@ -1609,7 +1705,7 @@ int main(int argc, char** argv) string path; iss >> path; RLPStream config(2); - config << sigKey.secret() << coinbase; + config << signingKey << beneficiary; writeFile(path, config.out()); } else @@ -1625,8 +1721,8 @@ int main(int argc, char** argv) if (b.size()) { RLP config(b); - sigKey = KeyPair(config[0].toHash()); - coinbase = config[1].toHash
(); + signingKey = config[0].toHash
(); + beneficiary = config[1].toHash
(); } else cwarn << path << "has no content!"; diff --git a/exp/main.cpp b/exp/main.cpp index 5fee5cbee..2bd0a741e 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -81,7 +81,7 @@ int main() // cdebug << toString(a2); Address a2("19c486071651b2650449ba3c6a807f316a73e8fe"); - cdebug << keyman.keys(); + cdebug << keyman.accountDetails(); cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; }); cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2); diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 5f3658030..06950cbee 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -28,7 +28,9 @@ using namespace dev; namespace dev { -char const* Version = "0.9.17"; +char const* Version = "0.9.18"; + +const u256 UndefinedU256 = ~(u256)0; void HasInvariants::checkInvariants() const { diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 36e57c44f..41f1b1d49 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -82,6 +82,8 @@ using u160s = std::vector; using u256Set = std::set; using u160Set = std::set; +extern const u256 UndefinedU256; + // Map types. using StringMap = std::map; using u256Map = std::map; diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index a1a1056cb..80ad7ecf9 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -20,7 +20,8 @@ */ #include "CommonIO.h" - +#include +#include #include #include "Exceptions.h" using namespace std; @@ -117,3 +118,14 @@ void dev::writeFile(std::string const& _file, bytesConstRef _data) ofstream(_file, ios::trunc|ios::binary).write((char const*)_data.data(), _data.size()); } +std::string dev::getPassword(std::string const& _prompt) +{ +#if WIN32 + cout << _prompt << flush; + std::string ret; + std::getline(cin, ret); + return ret; +#else + return getpass(_prompt.c_str()); +#endif +} diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 80334fa31..46a8b80bc 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -42,6 +42,8 @@ namespace dev { +std::string getPassword(std::string const& _prompt); + /// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes. bytes contents(std::string const& _file); std::string contentsString(std::string const& _file); diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 14bc500e6..9b25837af 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -282,6 +282,7 @@ namespace std { /// Forward std::hash to dev::FixedHash::hash. template<> struct hash: dev::h64::hash {}; + template<> struct hash: dev::h128::hash {}; template<> struct hash: dev::h160::hash {}; template<> struct hash: dev::h256::hash {}; template<> struct hash: dev::h512::hash {}; diff --git a/libdevcore/StructuredLogger.cpp b/libdevcore/StructuredLogger.cpp index f51ed310a..7d8a17722 100644 --- a/libdevcore/StructuredLogger.cpp +++ b/libdevcore/StructuredLogger.cpp @@ -34,6 +34,15 @@ using namespace std; namespace dev { +void StructuredLogger::initialize(bool _enabled, std::string const& _timeFormat, std::string const& _destinationURL) +{ + m_enabled = _enabled; + m_timeFormat = _timeFormat; + if (_destinationURL.size() > 7 && _destinationURL.substr(0, 7) == "file://") + m_out.open(_destinationURL.substr(7)); + // TODO: support tcp:// +} + void StructuredLogger::outputJson(Json::Value const& _value, std::string const& _name) const { Json::Value event; @@ -41,7 +50,7 @@ void StructuredLogger::outputJson(Json::Value const& _value, std::string const& Json::FastWriter fastWriter; Guard l(s_lock); event[_name] = _value; - cout << fastWriter.write(event) << endl; + (m_out.is_open() ? m_out : cout) << fastWriter.write(event) << endl; } void StructuredLogger::starting(string const& _clientImpl, const char* _ethVersion) @@ -51,6 +60,7 @@ void StructuredLogger::starting(string const& _clientImpl, const char* _ethVersi Json::Value event; event["client_impl"] = _clientImpl; event["eth_version"] = std::string(_ethVersion); + // TODO net_version event["ts"] = dev::toString(chrono::system_clock::now(), get().m_timeFormat.c_str()); get().outputJson(event, "starting"); @@ -64,6 +74,7 @@ void StructuredLogger::stopping(string const& _clientImpl, const char* _ethVersi Json::Value event; event["client_impl"] = _clientImpl; event["eth_version"] = std::string(_ethVersion); + // TODO net_version event["ts"] = dev::toString(chrono::system_clock::now(), get().m_timeFormat.c_str()); get().outputJson(event, "stopping"); diff --git a/libdevcore/StructuredLogger.h b/libdevcore/StructuredLogger.h index 2c30541e4..913d7b9b2 100644 --- a/libdevcore/StructuredLogger.h +++ b/libdevcore/StructuredLogger.h @@ -25,6 +25,7 @@ #pragma once +#include #include #include @@ -46,11 +47,7 @@ public: * http://en.cppreference.com/w/cpp/chrono/c/strftime * with which to display timestamps */ - void initialize(bool _enabled, std::string const& _timeFormat) - { - m_enabled = _enabled; - m_timeFormat = _timeFormat; - } + void initialize(bool _enabled, std::string const& _timeFormat, std::string const& _destinationURL = ""); static StructuredLogger& get() { @@ -92,6 +89,11 @@ public: std::string const& _prevHash ); static void transactionReceived(std::string const& _hash, std::string const& _remoteId); + // TODO: static void pendingQueueChanged(std::vector const& _hashes); + // TODO: static void miningStarted(); + // TODO: static void stillMining(unsigned _hashrate); + // TODO: static void miningStopped(); + private: // Singleton class. Private default ctor and no copying StructuredLogger() = default; @@ -102,6 +104,8 @@ private: bool m_enabled = false; std::string m_timeFormat = "%Y-%m-%dT%H:%M:%S"; + + mutable std::ofstream m_out; }; } diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 858f2a09f..9be0b89e8 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -104,6 +104,7 @@ void SecretStore::save(std::string const& _keysPath) void SecretStore::load(std::string const& _keysPath) { fs::path p(_keysPath); + boost::filesystem::create_directories(p); js::mValue v; for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) if (is_regular_file(it->path())) diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 7f4ae08f1..1fb6adf4a 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -23,6 +23,7 @@ #include #include +#include #include "Common.h" #include "FileSystem.h" @@ -48,8 +49,8 @@ private: static std::string encrypt(bytes const& _v, std::string const& _pass); static bytes decrypt(std::string const& _v, std::string const& _pass); - mutable std::map m_cached; - std::map> m_keys; + mutable std::unordered_map m_cached; + std::unordered_map> m_keys; }; } diff --git a/libethcore/Common.h b/libethcore/Common.h index 84459c6bf..8855bc3a7 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -136,5 +136,16 @@ private: using Handler = std::shared_ptr; +struct TransactionSkeleton +{ + bool creation = false; + Address from; + Address to; + u256 value; + bytes data; + u256 gas = UndefinedU256; + u256 gasPrice = UndefinedU256; +}; + } } diff --git a/libethcore/CommonJS.cpp b/libethcore/CommonJS.cpp index 286641bc3..167879db3 100644 --- a/libethcore/CommonJS.cpp +++ b/libethcore/CommonJS.cpp @@ -26,8 +26,6 @@ namespace dev { -const u256 UndefinedU256 = ~(u256)0; - Address toAddress(std::string const& _sn) { if (_sn.size() == 40) diff --git a/libethcore/CommonJS.h b/libethcore/CommonJS.h index 72625122d..0ff21afdf 100644 --- a/libethcore/CommonJS.h +++ b/libethcore/CommonJS.h @@ -48,8 +48,6 @@ inline Address jsToAddress(std::string const& _s) { return jsToFixedWrite(m_writeOptions, &blocksBatch); m_extrasDB->Write(m_writeOptions, &extrasBatch); - DEV_WRITE_GUARDED(x_lastBlockHash) + if (isKnown(bi.hash()) && !details(bi.hash())) { - m_lastBlockHash = newLastBlockHash; - m_lastBlockNumber = newLastBlockNumber; + clog(BlockChainDebug) << "Known block just inserted has no details."; + clog(BlockChainDebug) << "Block:" << bi; + clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE"; + exit(-1); } + try { + State canary(_db, *this, bi.hash()); + } + catch (...) + { + clog(BlockChainDebug) << "Failed to initialise State object form imported block."; + clog(BlockChainDebug) << "Block:" << bi; + clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE"; + exit(-1); + } + + if (m_lastBlockHash != newLastBlockHash) + DEV_WRITE_GUARDED(x_lastBlockHash) + { + m_lastBlockHash = newLastBlockHash; + m_lastBlockNumber = newLastBlockNumber; + m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&m_lastBlockHash, 32)); + } + #if ETH_PARANOIA || !ETH_TRUE checkConsistency(); #endif @@ -646,14 +667,6 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import if (!route.empty()) noteCanonChanged(); - if (isKnown(bi.hash()) && !details(bi.hash())) - { - clog(BlockChainDebug) << "Known block just inserted has no details."; - clog(BlockChainDebug) << "Block:" << bi; - clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE"; - exit(-1); - } - h256s fresh; h256s dead; bool isOld = true; diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 32fd36cce..235a6f540 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -81,6 +81,7 @@ public: /// Submits a new contract-creation transaction. /// @returns the new contract's address (assuming it all goes through). virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; + using Interface::submitTransaction; /// Makes the given call. Nothing is recorded into the state. virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override; diff --git a/libethereum/Interface.h b/libethereum/Interface.h index a82595e2d..f7253ad29 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -72,6 +72,10 @@ public: /// @returns the new contract's address (assuming it all goes through). virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; + /// Submits a new contract-creation transaction. + /// @returns the new contract's address (assuming it all goes through). + Address submitTransaction(Secret const& _secret, TransactionSkeleton const& _t) { if (_t.creation) return submitTransaction(_secret, _t.value, _t.data, _t.gas, _t.gasPrice); submitTransaction(_secret, _t.value, _t.to, _t.data, _t.gas, _t.gasPrice); return Address(); } + /// Blocks until all pending transactions have been processed. virtual void flushTransactions() = 0; diff --git a/libethereum/KeyManager.cpp b/libethereum/KeyManager.cpp index 15f767c88..11b2cb2a6 100644 --- a/libethereum/KeyManager.cpp +++ b/libethereum/KeyManager.cpp @@ -28,6 +28,7 @@ #include using namespace std; using namespace dev; +using namespace eth; namespace fs = boost::filesystem; KeyManager::KeyManager(std::string const& _keysFile): @@ -151,9 +152,18 @@ void KeyManager::kill(Address const& _a) m_store.kill(id); } -std::map> KeyManager::keys() const +AddressHash KeyManager::accounts() const { - std::map> ret; + AddressHash ret; + for (auto const& i: m_addrLookup) + if (m_keyInfo.count(i.second) > 0) + ret.insert(i.first); + return ret; +} + +std::unordered_map> KeyManager::accountDetails() const +{ + std::unordered_map> ret; for (auto const& i: m_addrLookup) if (m_keyInfo.count(i.second) > 0) ret[i.first] = make_pair(m_keyInfo.at(i.second).info, m_passwordInfo.at(m_keyInfo.at(i.second).passHash)); diff --git a/libethereum/KeyManager.h b/libethereum/KeyManager.h index 9cb22e5b3..38e8d8853 100644 --- a/libethereum/KeyManager.h +++ b/libethereum/KeyManager.h @@ -28,7 +28,8 @@ namespace dev { - +namespace eth +{ class UnknownPassword: public Exception {}; struct KeyInfo @@ -65,7 +66,10 @@ public: bool load(std::string const& _pass); void save(std::string const& _pass) const { write(_pass, m_keysFile); } - std::map> keys() const; + void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; } + + AddressHash accounts() const; + std::unordered_map> accountDetails() const; h128 uuid(Address const& _a) const; Address address(h128 const& _uuid) const; @@ -92,12 +96,12 @@ private: void write(h128 const& _key, std::string const& _keysFile) const; // Ethereum keys. - std::map m_addrLookup; - std::map m_keyInfo; - std::map m_passwordInfo; + std::unordered_map m_addrLookup; + std::unordered_map m_keyInfo; + std::unordered_map m_passwordInfo; // Passwords that we're storing. - mutable std::map m_cachedPasswords; + mutable std::unordered_map m_cachedPasswords; // The default password for keys in the keystore - protected by the master password. std::string m_password; @@ -108,3 +112,4 @@ private: }; } +} diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 2e4fe5749..50e4b26ab 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -707,6 +707,7 @@ void State::cleanup(bool _fullCommit) { if (_fullCommit) { + paranoia("immediately before database commit", true); // Commit the new trie to disk. diff --git a/libethereum/State.h b/libethereum/State.h index 74d75685c..a11812c6f 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -49,7 +49,7 @@ class BlockChain; class State; struct StateChat: public LogChannel { static const char* name(); static const int verbosity = 4; }; -struct StateTrace: public LogChannel { static const char* name(); static const int verbosity = 7; }; +struct StateTrace: public LogChannel { static const char* name(); static const int verbosity = 5; }; struct StateDetail: public LogChannel { static const char* name(); static const int verbosity = 14; }; struct StateSafeExceptions: public LogChannel { static const char* name(); static const int verbosity = 21; }; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 4560c1564..9b5a6dca7 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -389,6 +389,16 @@ string Host::pocHost() return "poc-" + strs[1] + ".ethdev.com"; } +std::unordered_map const& Host::pocHosts() +{ + static const std::unordered_map c_ret = { +// { Public(""), "poc-9.ethdev.com:30303" }, + { Public("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c"), "52.16.188.185:30303" }, + { Public("7f25d3eab333a6b98a8b5ed68d962bb22c876ffcd5561fca54e3c2ef27f754df6f7fd7c9b74cc919067abac154fb8e1f8385505954f161ae440abc355855e034"), "54.207.93.166:30303" } + }; + return c_ret; +} + void Host::addNode(NodeId const& _node, NodeIPEndpoint const& _endpoint) { // return if network is stopped while waiting on Host::run() or nodeTable to start diff --git a/libp2p/Host.h b/libp2p/Host.h index b9af0e8b0..4cfca7718 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -95,6 +95,8 @@ public: /// Default host for current version of client. static std::string pocHost(); + static std::unordered_map const& pocHosts(); + /// Register a peer-capability; all new peer connections will have this capability. template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; std::shared_ptr ret(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index e926356b7..35cfda64b 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -127,20 +127,19 @@ void NodeTable::discover() list NodeTable::nodes() const { list nodes; - Guard l(x_nodes); - for (auto& i: m_nodes) - nodes.push_back(i.second->id); + DEV_GUARDED(x_nodes) + for (auto& i: m_nodes) + nodes.push_back(i.second->id); return move(nodes); } list NodeTable::snapshot() const { list ret; - Guard l(x_state); - for (auto s: m_state) - for (auto np: s.nodes) - if (auto n = np.lock()) - if (!!n) + DEV_GUARDED(x_state) + for (auto const& s: m_state) + for (auto const& np: s.nodes) + if (auto n = np.lock()) ret.push_back(*n); return move(ret); } @@ -151,8 +150,7 @@ Node NodeTable::node(NodeId const& _id) if (m_nodes.count(_id)) { auto entry = m_nodes[_id]; - Node n(_id, entry->endpoint, entry->required); - return move(n); + return Node(_id, entry->endpoint, entry->required); } return UnspecifiedNode; } @@ -173,7 +171,7 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr>()); @@ -228,7 +226,7 @@ vector> NodeTable::nearestNodeEntries(NodeId _target) while (head != tail && head < s_bins && count < s_bucketSize) { Guard l(x_state); - for (auto n: m_state[head].nodes) + for (auto const& n: m_state[head].nodes) if (auto p = n.lock()) { if (count < s_bucketSize) @@ -238,7 +236,7 @@ vector> NodeTable::nearestNodeEntries(NodeId _target) } if (count < s_bucketSize && tail) - for (auto n: m_state[tail].nodes) + for (auto const& n: m_state[tail].nodes) if (auto p = n.lock()) { if (count < s_bucketSize) @@ -255,7 +253,7 @@ vector> NodeTable::nearestNodeEntries(NodeId _target) while (head < s_bins && count < s_bucketSize) { Guard l(x_state); - for (auto n: m_state[head].nodes) + for (auto const& n: m_state[head].nodes) if (auto p = n.lock()) { if (count < s_bucketSize) @@ -269,7 +267,7 @@ vector> NodeTable::nearestNodeEntries(NodeId _target) while (tail > 0 && count < s_bucketSize) { Guard l(x_state); - for (auto n: m_state[tail].nodes) + for (auto const& n: m_state[tail].nodes) if (auto p = n.lock()) { if (count < s_bucketSize) @@ -282,7 +280,7 @@ vector> NodeTable::nearestNodeEntries(NodeId _target) vector> ret; for (auto& nodes: found) - for (auto n: nodes.second) + for (auto const& n: nodes.second) if (ret.size() < s_bucketSize && !!n->endpoint && n->endpoint.isAllowed()) ret.push_back(n); return move(ret); @@ -306,12 +304,15 @@ void NodeTable::evict(shared_ptr _leastSeen, shared_ptr _n if (!m_socketPointer->isOpen()) return; + unsigned ec; + DEV_GUARDED(x_evictions) { - Guard l(x_evictions); m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id)); - if (m_evictions.size() == 1) - doCheckEvictions(boost::system::error_code()); + ec = m_evictions.size(); } + + if (ec == 1) + doCheckEvictions(boost::system::error_code()); ping(_leastSeen.get()); } @@ -428,24 +429,27 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes Pong in = Pong::fromBytesConstRef(_from, rlpBytes); // whenever a pong is received, check if it's in m_evictions - Guard le(x_evictions); - bool evictionEntry = false; - for (auto it = m_evictions.begin(); it != m_evictions.end(); it++) - if (it->first.first == nodeid && it->first.second > std::chrono::steady_clock::now()) - { - evictionEntry = true; - if (auto n = nodeEntry(it->second)) - dropNode(n); - - if (auto n = nodeEntry(it->first.first)) - n->pending = false; - - it = m_evictions.erase(it); - } - - // if not, check if it's known/pending or a pubk discovery ping - if (!evictionEntry) + bool found = false; + EvictionTimeout evictionEntry; + DEV_GUARDED(x_evictions) + for (auto it = m_evictions.begin(); it != m_evictions.end(); ++it) + if (it->first.first == nodeid && it->first.second > std::chrono::steady_clock::now()) + { + found = true; + evictionEntry = *it; + m_evictions.erase(it); + break; + } + if (found) + { + if (auto n = nodeEntry(evictionEntry.second)) + dropNode(n); + if (auto n = nodeEntry(evictionEntry.first.first)) + n->pending = false; + } + else { + // if not, check if it's known/pending or a pubk discovery ping if (auto n = nodeEntry(nodeid)) n->pending = false; else @@ -584,7 +588,7 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) if (chrono::steady_clock::now() - e.first.second > c_reqTimeout) if (m_nodes.count(e.second)) drop.push_back(m_nodes[e.second]); - evictionsRemain = m_evictions.size() - drop.size() > 0; + evictionsRemain = (m_evictions.size() - drop.size() > 0); } drop.unique(); diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 92bf17f79..2e10dd891 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -45,10 +45,12 @@ struct NodeEntry: public Node bool pending = true; ///< Node will be ignored until Pong is received }; -enum NodeTableEventType { +enum NodeTableEventType +{ NodeEntryAdded, NodeEntryDropped }; + class NodeTable; class NodeTableEventHandler { diff --git a/libtestutils/FixedWebThreeServer.cpp b/libtestutils/FixedWebThreeServer.cpp index c72a106c6..0be34cc93 100644 --- a/libtestutils/FixedWebThreeServer.cpp +++ b/libtestutils/FixedWebThreeServer.cpp @@ -16,7 +16,12 @@ */ /** @file FixedWebThreeStubServer.cpp * @author Marek Kotewicz + * @author Gav Wood * @date 2015 */ #include "FixedWebThreeServer.h" +#include +using namespace std; +using namespace dev; +using namespace eth; diff --git a/libtestutils/FixedWebThreeServer.h b/libtestutils/FixedWebThreeServer.h index 33ccbf71e..bcaacecd8 100644 --- a/libtestutils/FixedWebThreeServer.h +++ b/libtestutils/FixedWebThreeServer.h @@ -23,6 +23,7 @@ #include #include +#include /** * @brief dummy JSON-RPC api implementation @@ -33,7 +34,10 @@ class FixedWebThreeServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace { public: - FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client): WebThreeStubServerBase(_conn, _accounts), m_client(_client) {}; + FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector const& _allAccounts, dev::eth::Interface* _client): + WebThreeStubServerBase(_conn, std::make_shared([=](){return _client;}, _allAccounts), _allAccounts), + m_client(_client) + {} private: dev::eth::Interface* client() override { return m_client; } diff --git a/libweb3jsonrpc/AccountHolder.cpp b/libweb3jsonrpc/AccountHolder.cpp index b88397953..a73f20680 100644 --- a/libweb3jsonrpc/AccountHolder.cpp +++ b/libweb3jsonrpc/AccountHolder.cpp @@ -26,6 +26,7 @@ #include #include #include +#include using namespace std; using namespace dev; @@ -35,31 +36,23 @@ vector g_emptyQueue; static std::mt19937 g_randomNumberGenerator(time(0)); static Mutex x_rngMutex; -void AccountHolder::setAccounts(vector const& _accounts) +vector
AccountHolder::allAccounts() const { - m_accounts.clear(); - for (auto const& keyPair: _accounts) - { - m_accounts.push_back(keyPair.address()); - m_keyPairs[keyPair.address()] = keyPair; - } -} - -vector
AccountHolder::getAllAccounts() const -{ - vector
accounts = m_accounts; + vector
accounts; + accounts += realAccounts(); for (auto const& pair: m_proxyAccounts) if (!isRealAccount(pair.first)) accounts.push_back(pair.first); return accounts; } -Address const& AccountHolder::getDefaultTransactAccount() const +Address const& AccountHolder::defaultTransactAccount() const { - if (m_accounts.empty()) + auto accounts = realAccounts(); + if (accounts.empty()) return ZeroAddress; - Address const* bestMatch = &m_accounts.front(); - for (auto const& account: m_accounts) + Address const* bestMatch = &*accounts.begin(); + for (auto const& account: accounts) if (m_client()->balanceAt(account) > m_client()->balanceAt(*bestMatch)) bestMatch = &account; return *bestMatch; @@ -94,7 +87,7 @@ void AccountHolder::queueTransaction(TransactionSkeleton const& _transaction) m_transactionQueues[id].second.push_back(_transaction); } -vector const& AccountHolder::getQueuedTransactions(int _id) const +vector const& AccountHolder::queuedTransactions(int _id) const { if (!m_transactionQueues.count(_id)) return g_emptyQueue; @@ -106,3 +99,27 @@ void AccountHolder::clearQueue(int _id) if (m_transactionQueues.count(_id)) m_transactionQueues.at(_id).second.clear(); } + +AddressHash SimpleAccountHolder::realAccounts() const +{ + return m_keyManager.accounts(); +} + +void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t) +{ + if (isRealAccount(_t.from)) + m_client()->submitTransaction(m_keyManager.secret(_t.from, [&](){ return m_getPassword(_t.from); }), _t); + else if (isProxyAccount(_t.from)) + queueTransaction(_t); +} + +void FixedAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t) +{ + if (isRealAccount(_t.from)) + m_client()->submitTransaction(m_accounts[_t.from], _t); + else if (isProxyAccount(_t.from)) + queueTransaction(_t); +} + + + diff --git a/libweb3jsonrpc/AccountHolder.h b/libweb3jsonrpc/AccountHolder.h index 52005b51f..10b036226 100644 --- a/libweb3jsonrpc/AccountHolder.h +++ b/libweb3jsonrpc/AccountHolder.h @@ -24,17 +24,20 @@ #pragma once #include +#include #include #include #include #include +#include namespace dev { namespace eth { + +class KeyManager; class Interface; -} /** * Manages real accounts (where we know the secret key) and proxy accounts (where transactions @@ -43,32 +46,84 @@ class Interface; class AccountHolder { public: - explicit AccountHolder(std::function const& _client): m_client(_client) {} + explicit AccountHolder(std::function const& _client): m_client(_client) {} + + // easiest to return keyManager.addresses(); + virtual AddressHash realAccounts() const = 0; + // use m_web3's submitTransaction + // or use AccountHolder::queueTransaction(_t) to accept + virtual void authenticate(dev::eth::TransactionSkeleton const& _t) = 0; - /// Sets or resets the list of real accounts. - void setAccounts(std::vector const& _accounts); - std::vector
const& getRealAccounts() const { return m_accounts; } - bool isRealAccount(Address const& _account) const { return m_keyPairs.count(_account) > 0; } + Addresses allAccounts() const; + bool isRealAccount(Address const& _account) const { return realAccounts().count(_account) > 0; } bool isProxyAccount(Address const& _account) const { return m_proxyAccounts.count(_account) > 0; } - Secret const& secretKey(Address const& _account) const { return m_keyPairs.at(_account).secret(); } - std::vector
getAllAccounts() const; - Address const& getDefaultTransactAccount() const; + Address const& defaultTransactAccount() const; int addProxyAccount(Address const& _account); bool removeProxyAccount(unsigned _id); void queueTransaction(eth::TransactionSkeleton const& _transaction); - std::vector const& getQueuedTransactions(int _id) const; + std::vector const& queuedTransactions(int _id) const; void clearQueue(int _id); +protected: + std::function m_client; + private: using TransactionQueue = std::vector; - std::map m_keyPairs; - std::vector
m_accounts; - std::map m_proxyAccounts; - std::map> m_transactionQueues; - std::function m_client; + std::unordered_map m_proxyAccounts; + std::unordered_map> m_transactionQueues; +}; + +class SimpleAccountHolder: public AccountHolder +{ +public: + SimpleAccountHolder(std::function const& _client, std::function const& _getPassword, KeyManager& _keyman): + AccountHolder(_client), + m_getPassword(_getPassword), + m_keyManager(_keyman) + {} + + AddressHash realAccounts() const override; + void authenticate(dev::eth::TransactionSkeleton const& _t) override; + +private: + std::function m_getPassword; + KeyManager& m_keyManager; +}; + +class FixedAccountHolder: public AccountHolder +{ +public: + FixedAccountHolder(std::function const& _client, std::vector const& _accounts): + AccountHolder(_client) + { + setAccounts(_accounts); + } + + void setAccounts(std::vector const& _accounts) + { + for (auto const& i: _accounts) + m_accounts[i.address()] = i.secret(); + } + + dev::AddressHash realAccounts() const override + { + dev::AddressHash ret; + for (auto const& i: m_accounts) + ret.insert(i.first); + return ret; + } + + // use m_web3's submitTransaction + // or use AccountHolder::queueTransaction(_t) to accept + void authenticate(dev::eth::TransactionSkeleton const& _t) override; + +private: + std::unordered_map m_accounts; }; + +} } diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 30a634b3d..5235b0c4f 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -33,8 +33,8 @@ using namespace std; using namespace dev; using namespace dev::eth; -WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, std::vector const& _accounts): - WebThreeStubServerBase(_conn, _accounts), +WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr const& _ethAccounts, std::vector const& _shhAccounts): + WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), m_web3(_web3) { auto path = getDataDir() + "/.web3"; diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 08991d2f5..35c35c3f0 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -41,7 +41,7 @@ class WebThreeDirect; class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace { public: - WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector const& _accounts); + WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts); virtual std::string web3_clientVersion(); diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 12486d529..cac634a61 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -297,10 +297,11 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message return res; } -WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, vector const& _accounts): - AbstractWebThreeStubServer(_conn), m_ethAccounts(make_shared(bind(&WebThreeStubServerBase::client, this))) +WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, std::shared_ptr const& _ethAccounts, vector const& _sshAccounts): + AbstractWebThreeStubServer(_conn), + m_ethAccounts(_ethAccounts) { - m_ethAccounts->setAccounts(_accounts); + setIdentities(_sshAccounts); } void WebThreeStubServerBase::setIdentities(vector const& _ids) @@ -353,7 +354,7 @@ string WebThreeStubServerBase::eth_gasPrice() Json::Value WebThreeStubServerBase::eth_accounts() { Json::Value ret(Json::arrayValue); - for (auto const& i: m_ethAccounts->getAllAccounts()) + for (auto const& i: m_ethAccounts->allAccounts()) ret.append(toJS(i)); return ret; } @@ -499,7 +500,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json) TransactionSkeleton t = toTransaction(_json); if (!t.from) - t.from = m_ethAccounts->getDefaultTransactAccount(); + t.from = m_ethAccounts->defaultTransactAccount(); if (t.creation) ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; if (t.gasPrice == UndefinedU256) @@ -507,10 +508,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json) if (t.gas == UndefinedU256) t.gas = min(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); - if (m_ethAccounts->isRealAccount(t.from)) - authenticate(t, false); - else if (m_ethAccounts->isProxyAccount(t.from)) - authenticate(t, true); + m_ethAccounts->authenticate(t); return ret; } @@ -528,7 +526,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json) TransactionSkeleton t = toTransaction(_json); if (!t.from) - t.from = m_ethAccounts->getDefaultTransactAccount(); + t.from = m_ethAccounts->defaultTransactAccount(); if (t.creation) ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; if (t.gasPrice == UndefinedU256) @@ -536,10 +534,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json) if (t.gas == UndefinedU256) t.gas = min(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); - if (m_ethAccounts->isRealAccount(t.from)) - authenticate(t, false); - else if (m_ethAccounts->isProxyAccount(t.from)) - authenticate(t, true); + m_ethAccounts->authenticate(t); return toJS((t.creation ? Transaction(t.value, t.gasPrice, t.gas, t.data) : Transaction(t.value, t.gasPrice, t.gas, t.to, t.data)).sha3(WithoutSignature)); } @@ -579,7 +574,7 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const& { TransactionSkeleton t = toTransaction(_json); if (!t.from) - t.from = m_ethAccounts->getDefaultTransactAccount(); + t.from = m_ethAccounts->defaultTransactAccount(); // if (!m_accounts->isRealAccount(t.from)) // return ret; if (t.gasPrice == UndefinedU256) @@ -593,7 +588,6 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const& { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } - } bool WebThreeStubServerBase::eth_flush() @@ -906,7 +900,7 @@ Json::Value WebThreeStubServerBase::eth_fetchQueuedTransactions(string const& _a auto id = jsToInt(_accountId); Json::Value ret(Json::arrayValue); // TODO: throw an error on no account with given id - for (TransactionSkeleton const& t: m_ethAccounts->getQueuedTransactions(id)) + for (TransactionSkeleton const& t: m_ethAccounts->queuedTransactions(id)) ret.append(toJson(t)); m_ethAccounts->clearQueue(id); return ret; @@ -1077,18 +1071,3 @@ Json::Value WebThreeStubServerBase::shh_getMessages(string const& _filterId) BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } - -void WebThreeStubServerBase::authenticate(TransactionSkeleton const& _t, bool _toProxy) -{ - if (_toProxy) - m_ethAccounts->queueTransaction(_t); - else if (_t.to) - client()->submitTransaction(m_ethAccounts->secretKey(_t.from), _t.value, _t.to, _t.data, _t.gas, _t.gasPrice); - else - client()->submitTransaction(m_ethAccounts->secretKey(_t.from), _t.value, _t.data, _t.gas, _t.gasPrice); -} - -void WebThreeStubServerBase::setAccounts(const vector& _accounts) -{ - m_ethAccounts->setAccounts(_accounts); -} diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 425e6567e..f3f7edfe7 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -36,10 +36,10 @@ namespace dev { class WebThreeNetworkFace; -class AccountHolder; class KeyPair; namespace eth { +class AccountHolder; struct TransactionSkeleton; class Interface; } @@ -68,7 +68,7 @@ public: class WebThreeStubServerBase: public AbstractWebThreeStubServer { public: - WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts); + WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr const& _ethAccounts, std::vector const& _sshAccounts); virtual std::string web3_sha3(std::string const& _param1); virtual std::string web3_clientVersion() { return "C++ (ethereum-cpp)"; } @@ -134,20 +134,16 @@ public: virtual Json::Value shh_getFilterChanges(std::string const& _filterId); virtual Json::Value shh_getMessages(std::string const& _filterId); - void setAccounts(std::vector const& _accounts); void setIdentities(std::vector const& _ids); std::map const& ids() const { return m_shhIds; } -protected: - virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy); - protected: virtual dev::eth::Interface* client() = 0; virtual std::shared_ptr face() = 0; virtual dev::WebThreeNetworkFace* network() = 0; virtual dev::WebThreeStubDatabaseFace* db() = 0; - std::shared_ptr m_ethAccounts; + std::shared_ptr m_ethAccounts; std::map m_shhIds; std::map m_shhWatches; diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index c558c71f3..b355c336f 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -85,7 +85,8 @@ ClientModel::ClientModel(): connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); - m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector(), m_client.get())); + m_ethAccounts = make_shared([=](){return m_client.get();}, std::vector()); + m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector(), m_client.get())); connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); } @@ -280,7 +281,7 @@ void ClientModel::setupState(QVariantMap _state) transactionSequence.push_back(transactionSettings); } } - m_web3Server->setAccounts(userAccounts); + m_ethAccounts->setAccounts(userAccounts); executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString())); } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index b88ae8511..910c0ed01 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -35,7 +35,7 @@ namespace dev { -namespace eth { class Account; } +namespace eth { class Account; class FixedAccountHolder; } namespace mix { @@ -235,6 +235,7 @@ private: std::unique_ptr m_client; std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; + std::shared_ptr m_ethAccounts; QList m_gasCosts; std::map m_contractAddresses; std::map m_contractNames; diff --git a/mix/Web3Server.cpp b/mix/Web3Server.cpp index 7edc73060..855f33ca5 100644 --- a/mix/Web3Server.cpp +++ b/mix/Web3Server.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "Web3Server.h" using namespace dev::mix; @@ -108,8 +109,8 @@ class EmptyNetwork : public dev::WebThreeNetworkFace } -Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client): - WebThreeStubServerBase(_conn, _accounts), +Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, dev::eth::Interface* _client): + WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), m_client(_client), m_network(new EmptyNetwork()) { diff --git a/mix/Web3Server.h b/mix/Web3Server.h index b8a059295..2383a0a3c 100644 --- a/mix/Web3Server.h +++ b/mix/Web3Server.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace dev @@ -38,7 +39,7 @@ class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev Q_OBJECT public: - Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client); + Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, dev::eth::Interface* _client); virtual ~Web3Server(); signals: diff --git a/test/libweb3jsonrpc/AccountHolder.cpp b/test/libweb3jsonrpc/AccountHolder.cpp index e8e42ff18..c9500a6ef 100644 --- a/test/libweb3jsonrpc/AccountHolder.cpp +++ b/test/libweb3jsonrpc/AccountHolder.cpp @@ -22,6 +22,9 @@ #include #include +using namespace std; +using namespace dev; +using namespace eth; namespace dev { @@ -32,16 +35,17 @@ BOOST_AUTO_TEST_SUITE(AccountHolderTest) BOOST_AUTO_TEST_CASE(ProxyAccountUseCase) { - AccountHolder h = AccountHolder(std::function()); - BOOST_CHECK(h.getAllAccounts().empty()); - BOOST_CHECK(h.getRealAccounts().empty()); + FixedAccountHolder h = FixedAccountHolder(function(), vector()); + + BOOST_CHECK(h.allAccounts().empty()); + BOOST_CHECK(h.realAccounts().empty()); Address addr("abababababababababababababababababababab"); Address addr2("abababababababababababababababababababab"); int id = h.addProxyAccount(addr); - BOOST_CHECK(h.getQueuedTransactions(id).empty()); + BOOST_CHECK(h.queuedTransactions(id).empty()); // register it again int secondID = h.addProxyAccount(addr); - BOOST_CHECK(h.getQueuedTransactions(secondID).empty()); + BOOST_CHECK(h.queuedTransactions(secondID).empty()); eth::TransactionSkeleton t1; eth::TransactionSkeleton t2; @@ -49,20 +53,20 @@ BOOST_AUTO_TEST_CASE(ProxyAccountUseCase) t1.data = fromHex("12345678"); t2.from = addr; t2.data = fromHex("abcdef"); - BOOST_CHECK(h.getQueuedTransactions(id).empty()); + BOOST_CHECK(h.queuedTransactions(id).empty()); h.queueTransaction(t1); - BOOST_CHECK_EQUAL(1, h.getQueuedTransactions(id).size()); + BOOST_CHECK_EQUAL(1, h.queuedTransactions(id).size()); h.queueTransaction(t2); - BOOST_REQUIRE_EQUAL(2, h.getQueuedTransactions(id).size()); + BOOST_REQUIRE_EQUAL(2, h.queuedTransactions(id).size()); // second proxy should not see transactions - BOOST_CHECK(h.getQueuedTransactions(secondID).empty()); + BOOST_CHECK(h.queuedTransactions(secondID).empty()); - BOOST_CHECK(h.getQueuedTransactions(id)[0].data == t1.data); - BOOST_CHECK(h.getQueuedTransactions(id)[1].data == t2.data); + BOOST_CHECK(h.queuedTransactions(id)[0].data == t1.data); + BOOST_CHECK(h.queuedTransactions(id)[1].data == t2.data); h.clearQueue(id); - BOOST_CHECK(h.getQueuedTransactions(id).empty()); + BOOST_CHECK(h.queuedTransactions(id).empty()); // removing fails because it never existed BOOST_CHECK(!h.removeProxyAccount(secondID)); BOOST_CHECK(h.removeProxyAccount(id));