Browse Source

Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into develop

cl-refactor
Marek Kotewicz 10 years ago
parent
commit
aa637ee661
  1. 3
      CMakeLists.txt
  2. 5
      alethzero/Context.h
  3. 7
      alethzero/Main.ui
  4. 208
      alethzero/MainWin.cpp
  5. 17
      alethzero/MainWin.h
  6. 69
      alethzero/OurWebThreeStubServer.cpp
  7. 43
      alethzero/OurWebThreeStubServer.h
  8. 55
      alethzero/Transact.cpp
  9. 5
      alethzero/Transact.h
  10. 261
      alethzero/Transact.ui
  11. 190
      eth/main.cpp
  12. 2
      exp/main.cpp
  13. 4
      libdevcore/Common.cpp
  14. 2
      libdevcore/Common.h
  15. 14
      libdevcore/CommonIO.cpp
  16. 2
      libdevcore/CommonIO.h
  17. 1
      libdevcore/FixedHash.h
  18. 13
      libdevcore/StructuredLogger.cpp
  19. 14
      libdevcore/StructuredLogger.h
  20. 1
      libdevcrypto/SecretStore.cpp
  21. 5
      libdevcrypto/SecretStore.h
  22. 11
      libethcore/Common.h
  23. 2
      libethcore/CommonJS.cpp
  24. 14
      libethcore/CommonJS.h
  25. 39
      libethereum/BlockChain.cpp
  26. 1
      libethereum/ClientBase.h
  27. 4
      libethereum/Interface.h
  28. 14
      libethereum/KeyManager.cpp
  29. 17
      libethereum/KeyManager.h
  30. 1
      libethereum/State.cpp
  31. 2
      libethereum/State.h
  32. 10
      libp2p/Host.cpp
  33. 2
      libp2p/Host.h
  34. 78
      libp2p/NodeTable.cpp
  35. 4
      libp2p/NodeTable.h
  36. 5
      libtestutils/FixedWebThreeServer.cpp
  37. 6
      libtestutils/FixedWebThreeServer.h
  38. 51
      libweb3jsonrpc/AccountHolder.cpp
  39. 85
      libweb3jsonrpc/AccountHolder.h
  40. 4
      libweb3jsonrpc/WebThreeStubServer.cpp
  41. 2
      libweb3jsonrpc/WebThreeStubServer.h
  42. 43
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  43. 10
      libweb3jsonrpc/WebThreeStubServerBase.h
  44. 5
      mix/ClientModel.cpp
  45. 3
      mix/ClientModel.h
  46. 5
      mix/Web3Server.cpp
  47. 3
      mix/Web3Server.h
  48. 28
      test/libweb3jsonrpc/AccountHolder.cpp

3
CMakeLists.txt

@ -377,7 +377,8 @@ if (TOOLS)
endif() endif()
if (NCURSES) if (NCURSES)
add_subdirectory(neth) # Commented out until caktux refactors with the new wallet API.
# add_subdirectory(neth)
endif () endif ()
if (GUI) if (GUI)

5
alethzero/Context.h

@ -29,7 +29,7 @@
class QComboBox; class QComboBox;
namespace dev { namespace eth { struct StateDiff; } } namespace dev { namespace eth { struct StateDiff; class KeyManager; } }
#define Small "font-size: small; " #define Small "font-size: small; "
#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; " #define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; "
@ -64,5 +64,8 @@ public:
virtual std::pair<dev::Address, dev::bytes> fromString(std::string const& _a) const = 0; virtual std::pair<dev::Address, dev::bytes> fromString(std::string const& _a) const = 0;
virtual std::string renderDiff(dev::eth::StateDiff const& _d) const = 0; virtual std::string renderDiff(dev::eth::StateDiff const& _d) const = 0;
virtual std::string render(dev::Address const& _a) 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;
}; };

7
alethzero/Main.ui

@ -548,12 +548,15 @@
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::NoFrame</enum> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="dragDropMode"> <property name="showDropIndicator" stdset="0">
<enum>QAbstractItemView::InternalMove</enum> <bool>false</bool>
</property> </property>
<property name="alternatingRowColors"> <property name="alternatingRowColors">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

208
alethzero/MainWin.cpp

@ -31,6 +31,7 @@
#include <QtWidgets/QDialog> #include <QtWidgets/QDialog>
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
#include <QtWidgets/QInputDialog> #include <QtWidgets/QInputDialog>
#include <QtWidgets/QListWidgetItem>
#include <QtWebEngine/QtWebEngine> #include <QtWebEngine/QtWebEngine>
#include <QtWebEngineWidgets/QWebEngineView> #include <QtWebEngineWidgets/QWebEngineView>
#include <QtWebEngineWidgets/QWebEngineCallback> #include <QtWebEngineWidgets/QWebEngineCallback>
@ -143,6 +144,38 @@ Main::Main(QWidget *parent) :
// ui->log->addItem(QString::fromStdString(s)); // 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 #if ETH_DEBUG
m_servers.append("127.0.0.1:30300"); m_servers.append("127.0.0.1:30300");
#endif #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)); 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"); QSettings s("ethereum", "alethzero");
m_networkConfig = s.value("peers").toByteArray(); m_networkConfig = s.value("peers").toByteArray();
bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size()); 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_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_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))); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned())); m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening(); m_server->StartListening();
@ -198,6 +229,7 @@ Main::Main(QWidget *parent) :
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector(); // QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page); // inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin());
readSettings(); readSettings();
#if !ETH_FATDB #if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts); removeDockWidget(ui->dockWidget_accounts);
@ -358,9 +390,9 @@ void Main::installBalancesWatch()
// TODO: Update for new currencies reg. // TODO: Update for new currencies reg.
for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i) for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i)
altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); 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) 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); uninstallWatch(m_balancesFilter);
m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); }); m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); });
@ -429,7 +461,7 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered() 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(); m_transact.exec();
} }
@ -616,17 +648,7 @@ void Main::on_paranoia_triggered()
void Main::writeSettings() void Main::writeSettings()
{ {
QSettings s("ethereum", "alethzero"); QSettings s("ethereum", "alethzero");
{ s.remove("address");
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);
}
{ {
QByteArray b; QByteArray b;
b.resize(sizeof(Secret) * m_myIdentities.size()); b.resize(sizeof(Secret) * m_myIdentities.size());
@ -666,6 +688,20 @@ void Main::writeSettings()
s.setValue("windowState", saveState()); 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<Main*>(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<Main*>(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) void Main::readSettings(bool _skipGeometry)
{ {
QSettings s("ethereum", "alethzero"); QSettings s("ethereum", "alethzero");
@ -675,22 +711,17 @@ void Main::readSettings(bool _skipGeometry)
restoreState(s.value("windowState").toByteArray()); restoreState(s.value("windowState").toByteArray());
{ {
m_myKeys.clear();
QByteArray b = s.value("address").toByteArray(); QByteArray b = s.value("address").toByteArray();
if (b.isEmpty()) if (!b.isEmpty())
m_myKeys.append(KeyPair::create());
else
{ {
h256 k; h256 k;
for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i)
{ {
memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret));
if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k))) if (!m_keyManager.accounts().count(KeyPair(k).address()))
m_myKeys.append(KeyPair(k)); 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(); 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() 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()); bytes b = fromHex(s.toStdString());
if (b.size() == 32) if (b.size() == 32)
{ {
auto k = KeyPair(h256(b)); 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(); keysChanged();
} }
else else
@ -785,15 +838,8 @@ void Main::on_importKeyFile_triggered()
} }
cnote << k.address(); cnote << k.address();
if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end()) if (!m_keyManager.accounts().count(k.address()))
{ ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice());
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());
}
else else
QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); 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() 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()]; auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray();
QMessageBox::information(this, "Export Account Key", "Secret key to account " + QString::fromStdString(render(k.address()) + " is:\n" + toHex(k.sec().ref()))); 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() void Main::refreshBalances()
{ {
cwatch << "refreshBalances()"; cwatch << "refreshBalances()";
@ -931,11 +997,13 @@ void Main::refreshBalances()
// cdebug << n << addr << denom << sha3(h256(n).asBytes()); // cdebug << n << addr << denom << sha3(h256(n).asBytes());
altCoins[addr] = make_tuple(fromRaw(n), 0, denom); altCoins[addr] = make_tuple(fromRaw(n), 0, denom);
}*/ }*/
for (auto i: m_myKeys) for (pair<Address, std::pair<std::string, std::string>> const& i: m_keyManager.accountDetails())
{ {
u256 b = ethereum()->balanceAt(i.address()); u256 b = ethereum()->balanceAt(i.first);
(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)) 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);
->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size)); 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; totalBalance += b;
// for (auto& c: altCoins) // for (auto& c: altCoins)
@ -1385,23 +1453,6 @@ void Main::on_transactionQueue_currentItemChanged()
ui->pendingInfo->moveCursor(QTextCursor::Start); ui->pendingInfo->moveCursor(QTextCursor::Start);
} }
void Main::ourAccountsRowsMoved()
{
QList<KeyPair> 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() void Main::on_inject_triggered()
{ {
QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex"); 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()) if (ui->mine->isChecked())
{ {
ethereum()->setAddress(m_myKeys.last().address()); ethereum()->setAddress(m_beneficiary);
ethereum()->startMining(); ethereum()->startMining();
} }
else else
@ -1837,7 +1888,6 @@ void Main::on_mine_triggered()
void Main::keysChanged() void Main::keysChanged()
{ {
onBalancesChange(); onBalancesChange();
m_server->setAccounts(keysAsVector(m_myKeys));
} }
bool beginsWith(Address _a, bytes const& _b) bool beginsWith(Address _a, bytes const& _b)
@ -1901,24 +1951,39 @@ void Main::on_newAccount_triggered()
t->join(); t->join();
delete t; 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(); keysChanged();
} }
void Main::on_killAccount_triggered() 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 ( if (
ethereum()->balanceAt(k.address()) != 0 && ethereum()->balanceAt(h) != 0 &&
QMessageBox::critical(this, "Kill Account?!", QMessageBox::critical(this, QString::fromStdString("Kill Account " + k.first + "?!"),
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" 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?"), "Are you sure you want to continue?"),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
return; 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(); keysChanged();
if (m_beneficiary == h)
setBeneficiary(*m_keyManager.accounts().begin());
} }
} }
@ -1929,7 +1994,8 @@ void Main::on_go_triggered()
ui->net->setChecked(true); ui->net->setChecked(true);
on_net_triggered(); 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 std::string Main::prettyU256(dev::u256 const& _n) const

17
alethzero/MainWin.h

@ -35,6 +35,7 @@
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libethereum/KeyManager.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include "Context.h" #include "Context.h"
@ -42,6 +43,8 @@
#include "NatspecHandler.h" #include "NatspecHandler.h"
#include "Connect.h" #include "Connect.h"
class QListWidgetItem;
namespace Ui { namespace Ui {
class Main; class Main;
} }
@ -86,10 +89,14 @@ public:
std::pair<dev::Address, dev::bytes> fromString(std::string const& _a) const override; std::pair<dev::Address, dev::bytes> fromString(std::string const& _a) const override;
std::string renderDiff(dev::eth::StateDiff const& _d) const override; std::string renderDiff(dev::eth::StateDiff const& _d) const override;
QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; } QList<dev::KeyPair> owned() const { return m_myIdentities; }
dev::u256 gasPrice() const { return 10 * dev::eth::szabo; } 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: public slots:
void load(QString _file); void load(QString _file);
void note(QString _entry); void note(QString _entry);
@ -144,7 +151,7 @@ private slots:
void on_exportState_triggered(); void on_exportState_triggered();
// Stuff concerning the blocks/transactions/accounts panels // Stuff concerning the blocks/transactions/accounts panels
void ourAccountsRowsMoved(); void on_ourAccounts_itemClicked(QListWidgetItem* _i);
void on_ourAccounts_doubleClicked(); void on_ourAccounts_doubleClicked();
void on_accounts_doubleClicked(); void on_accounts_doubleClicked();
void on_accounts_currentItemChanged(); void on_accounts_currentItemChanged();
@ -236,6 +243,9 @@ private:
void refreshBlockCount(); void refreshBlockCount();
void refreshBalances(); void refreshBalances();
void setBeneficiary(dev::Address const& _b);
std::string getPassword(std::string const& _title, std::string const& _for);
std::unique_ptr<Ui::Main> ui; std::unique_ptr<Ui::Main> ui;
std::unique_ptr<dev::WebThreeDirect> m_webThree; std::unique_ptr<dev::WebThreeDirect> m_webThree;
@ -247,10 +257,11 @@ private:
QByteArray m_networkConfig; QByteArray m_networkConfig;
QStringList m_servers; QStringList m_servers;
QList<dev::KeyPair> m_myKeys;
QList<dev::KeyPair> m_myIdentities; QList<dev::KeyPair> m_myIdentities;
dev::eth::KeyManager m_keyManager;
QString m_privateChain; QString m_privateChain;
dev::Address m_nameReg; dev::Address m_nameReg;
dev::Address m_beneficiary;
QList<QPair<QString, QString>> m_consoleHistory; QList<QPair<QString, QString>> m_consoleHistory;
QMutex m_logLock; QMutex m_logLock;

69
alethzero/OurWebThreeStubServer.cpp

@ -20,23 +20,23 @@
*/ */
#include "OurWebThreeStubServer.h" #include "OurWebThreeStubServer.h"
#include <QMessageBox> #include <QMessageBox>
#include <QAbstractButton> #include <QAbstractButton>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libnatspec/NatspecExpressionEvaluator.h> #include <libnatspec/NatspecExpressionEvaluator.h>
#include "MainWin.h" #include "MainWin.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, OurWebThreeStubServer::OurWebThreeStubServer(
vector<KeyPair> const& _accounts, Main* _main): jsonrpc::AbstractServerConnector& _conn,
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(_main) WebThreeDirect& _web3,
Main* _main
):
WebThreeStubServer(_conn, _web3, make_shared<OurAccountHolder>(_web3, _main), _main->owned().toVector().toStdVector()),
m_main(_main)
{ {
connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
} }
string OurWebThreeStubServer::shh_newIdentity() string OurWebThreeStubServer::shh_newIdentity()
@ -46,7 +46,18 @@ string OurWebThreeStubServer::shh_newIdentity()
return toJS(kp.pub()); 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()) if (!m_main->confirm())
{ {
@ -66,18 +77,18 @@ bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string
//return button == QMessageBox::Ok; //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) + "."); 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)" : "") + 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) + "."); ", 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!", return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!",
"ÐApp is attempting to call into an unknown contract at address " + "Ð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!"); "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); 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); Guard l(x_queued);
while (!m_queued.empty()) while (!m_queued.empty())
{ {
auto q = m_queued.front(); auto t = m_queued.front();
m_queued.pop(); 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) if (_t.creation)
{ {

43
alethzero/OurWebThreeStubServer.h

@ -25,26 +25,29 @@
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
#include <libweb3jsonrpc/AccountHolder.h>
class Main; class Main;
class OurWebThreeStubServer: public QObject, public WebThreeStubServer class OurAccountHolder: public QObject, public dev::eth::AccountHolder
{ {
Q_OBJECT Q_OBJECT
public: public:
OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, OurAccountHolder(
std::vector<dev::KeyPair> const& _accounts, Main* main); dev::WebThreeDirect& _web3,
Main* _main
virtual std::string shh_newIdentity() override; );
virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
signals:
void onNewId(QString _s);
public slots: public slots:
void doValidations(); 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: private:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text); bool showAuthenticationPopup(std::string const& _title, std::string const& _text);
bool showCreationNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy); bool showCreationNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
@ -53,9 +56,29 @@ private:
bool validateTransaction(dev::eth::TransactionSkeleton const& _t, bool _toProxy); bool validateTransaction(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
std::queue<std::pair<dev::eth::TransactionSkeleton, bool>> m_queued; std::queue<dev::eth::TransactionSkeleton> m_queued;
dev::Mutex x_queued; dev::Mutex x_queued;
dev::WebThreeDirect* m_web3; dev::WebThreeDirect* m_web3;
Main* m_main; 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;
};

55
alethzero/Transact.cpp

@ -39,6 +39,7 @@
#include <libnatspec/NatspecExpressionEvaluator.h> #include <libnatspec/NatspecExpressionEvaluator.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/Utility.h> #include <libethereum/Utility.h>
#include <libethereum/KeyManager.h>
#if ETH_SERPENT #if ETH_SERPENT
#include <libserpent/funcs.h> #include <libserpent/funcs.h>
#include <libserpent/util.h> #include <libserpent/util.h>
@ -69,11 +70,20 @@ Transact::~Transact()
delete ui; delete ui;
} }
void Transact::setEnvironment(QList<dev::KeyPair> _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_ethereum = _eth;
m_natSpecDB = _natSpecDB; 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 bool Transact::isCreation() const
@ -126,8 +136,8 @@ void Transact::updateFee()
ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str()));
bool ok = false; bool ok = false;
for (auto i: m_myKeys) for (auto const& i: m_accounts)
if (ethereum()->balanceAt(i.address()) >= totalReq) if (ethereum()->balanceAt(i) >= totalReq)
{ {
ok = true; ok = true;
break; break;
@ -388,22 +398,33 @@ Secret Transact::findSecret(u256 _totalReq) const
if (!ethereum()) if (!ethereum())
return Secret(); return Secret();
Secret best; Address best;
u256 bestBalance = 0; 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) if (b >= _totalReq)
return i.secret(); {
best = i;
break;
}
if (b > bestBalance) 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() 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); auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock);
if (!s || b < value() + fee()) if (!s || b < value() + fee())
{ {
@ -440,9 +461,10 @@ void Transact::on_send_clicked()
void Transact::on_debug_clicked() void Transact::on_debug_clicked()
{ {
Secret s = findSecret(value() + fee()); // Secret s = findSecret(value() + fee());
auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock); Address from = fromAccount();
if (!s || b < value() + fee()) 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."); QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
return; return;
@ -452,8 +474,9 @@ void Transact::on_debug_clicked()
{ {
State st(ethereum()->postState()); State st(ethereum()->postState());
Transaction t = isCreation() ? Transaction t = isCreation() ?
Transaction(value(), gasPrice(), ui->gas->value(), 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(dev::toAddress(s)), s); 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); Debugger dw(m_context, this);
Executive e(st, ethereum()->blockChain(), 0); Executive e(st, ethereum()->blockChain(), 0);
dw.populate(e, t); dw.populate(e, t);

5
alethzero/Transact.h

@ -41,7 +41,7 @@ public:
explicit Transact(Context* _context, QWidget* _parent = 0); explicit Transact(Context* _context, QWidget* _parent = 0);
~Transact(); ~Transact();
void setEnvironment(QList<dev::KeyPair> _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots: private slots:
void on_destination_currentTextChanged(QString); void on_destination_currentTextChanged(QString);
@ -60,6 +60,7 @@ private:
dev::eth::Client* ethereum() const { return m_ethereum; } dev::eth::Client* ethereum() const { return m_ethereum; }
void rejigData(); void rejigData();
dev::Address fromAccount();
void updateDestination(); void updateDestination();
void updateFee(); void updateFee();
bool isCreation() const; bool isCreation() const;
@ -76,7 +77,7 @@ private:
unsigned m_backupGas = 0; unsigned m_backupGas = 0;
dev::bytes m_data; dev::bytes m_data;
QList<dev::KeyPair> m_myKeys; dev::AddressHash m_accounts;
dev::eth::Client* m_ethereum = nullptr; dev::eth::Client* m_ethereum = nullptr;
Context* m_context = nullptr; Context* m_context = nullptr;
NatSpecFace* m_natSpecDB = nullptr; NatSpecFace* m_natSpecDB = nullptr;

261
alethzero/Transact.ui

@ -14,91 +14,69 @@
<string>Transact</string> <string>Transact</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="2" column="1" colspan="2"> <item row="5" column="0">
<widget class="QSpinBox" name="value"> <widget class="QLabel" name="label_2">
<property name="suffix"> <property name="sizePolicy">
<string/> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="maximum"> <property name="text">
<number>430000000</number> <string>D&amp;ata</string>
</property> </property>
<property name="value"> <property name="alignment">
<number>0</number> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>data</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="5" column="3">
<widget class="QLabel" name="label5_2"> <widget class="QCheckBox" name="optimize">
<property name="text"> <property name="text">
<string>&amp;Amount</string> <string>&amp;Optimise</string>
</property> </property>
<property name="buddy"> <property name="checked">
<cstring>value</cstring> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1" colspan="3"> <item row="4" column="1">
<widget class="QLineEdit" name="calculatedName"> <widget class="QSpinBox" name="gas">
<property name="enabled"> <property name="suffix">
<bool>false</bool> <string> gas</string>
</property> </property>
<property name="readOnly"> <property name="minimum">
<bool>true</bool> <number>1</number>
</property> </property>
<property name="placeholderText"> <property name="maximum">
<string/> <number>430000000</number>
</property>
<property name="value">
<number>10000</number>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="4"> <item row="8" column="0">
<widget class="QSplitter" name="splitter_5"> <widget class="QPushButton" name="cancel">
<property name="orientation"> <property name="text">
<enum>Qt::Vertical</enum> <string>&amp;Cancel</string>
</property>
<property name="shortcut">
<string>Esc</string>
</property> </property>
<widget class="QPlainTextEdit" name="data">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
<widget class="QTextEdit" name="code">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget> </widget>
</item> </item>
<item row="3" column="3"> <item row="8" column="2">
<widget class="QComboBox" name="gasPriceUnits"/> <widget class="QPushButton" name="debug">
</item>
<item row="4" column="1" colspan="2">
<widget class="QLabel" name="fee">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string/> <string>&amp;Debug</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label5"> <widget class="QLabel" name="label5">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -114,53 +92,68 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="3"> <item row="7" column="0" colspan="4">
<widget class="QComboBox" name="valueUnits"/> <widget class="QLabel" name="total">
</item> <property name="sizePolicy">
<item row="7" column="2"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<widget class="QPushButton" name="debug"> <horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>&amp;Debug</string> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="3"> <item row="3" column="1" colspan="2">
<widget class="QPushButton" name="send"> <widget class="QSpinBox" name="value">
<property name="text"> <property name="suffix">
<string>&amp;Execute</string> <string/>
</property> </property>
<property name="default"> <property name="maximum">
<bool>false</bool> <number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="3">
<widget class="QComboBox" name="gasPriceUnits"/>
</item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_6"> <widget class="QLabel" name="label5_2">
<property name="text"> <property name="text">
<string>&amp;Gas</string> <string>&amp;Amount</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>gas</cstring> <cstring>value</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="8" column="3">
<widget class="QSpinBox" name="gas"> <widget class="QPushButton" name="send">
<property name="suffix"> <property name="text">
<string> gas</string> <string>&amp;Execute</string>
</property>
<property name="minimum">
<number>1</number>
</property> </property>
<property name="maximum"> <property name="default">
<number>430000000</number> <bool>false</bool>
</property> </property>
<property name="value"> </widget>
<number>10000</number> </item>
<item row="1" column="1" colspan="3">
<widget class="QComboBox" name="destination">
<property name="editable">
<bool>true</bool>
</property> </property>
<item>
<property name="text">
<string>(Create Contract)</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="3" column="2"> <item row="4" column="2">
<widget class="QSpinBox" name="gasPrice"> <widget class="QSpinBox" name="gasPrice">
<property name="prefix"> <property name="prefix">
<string>@ </string> <string>@ </string>
@ -173,49 +166,63 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="3"> <item row="6" column="0" colspan="4">
<widget class="QCheckBox" name="optimize"> <widget class="QSplitter" name="splitter_5">
<property name="text"> <property name="orientation">
<string>&amp;Optimise</string> <enum>Qt::Vertical</enum>
</property>
<property name="checked">
<bool>true</bool>
</property> </property>
<widget class="QPlainTextEdit" name="data">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
<widget class="QTextEdit" name="code">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>D&amp;ata</string> <string>&amp;Gas</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>data</cstring> <cstring>gas</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1" colspan="3"> <item row="2" column="1" colspan="3">
<widget class="QComboBox" name="destination"> <widget class="QLineEdit" name="calculatedName">
<property name="editable"> <property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool> <bool>true</bool>
</property> </property>
<item> <property name="placeholderText">
<property name="text"> <string/>
<string>(Create Contract)</string> </property>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="6" column="0" colspan="4"> <item row="3" column="3">
<widget class="QLabel" name="total"> <widget class="QComboBox" name="valueUnits"/>
</item>
<item row="5" column="1" colspan="2">
<widget class="QLabel" name="fee">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -225,18 +232,24 @@
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="0" column="0">
<widget class="QPushButton" name="cancel"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>&amp;Cancel</string> <string>&amp;From</string>
</property> </property>
<property name="shortcut"> <property name="buddy">
<string>Esc</string> <cstring>from</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="from"/>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

190
eth/main.cpp

@ -37,6 +37,7 @@
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <libethereum/All.h> #include <libethereum/All.h>
#include <libethereum/KeyManager.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#if ETH_JSCONSOLE || !ETH_TRUE #if ETH_JSCONSOLE || !ETH_TRUE
#include <libjsconsole/JSConsole.h> #include <libjsconsole/JSConsole.h>
@ -46,6 +47,7 @@
#include <readline/history.h> #include <readline/history.h>
#endif #endif
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/AccountHolder.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h> #include <jsonrpccpp/server/connectors/httpserver.h>
#include <jsonrpccpp/client/connectors/httpclient.h> #include <jsonrpccpp/client/connectors/httpclient.h>
@ -89,10 +91,8 @@ void interactiveHelp()
<< " minestart Starts mining." << endl << " minestart Starts mining." << endl
<< " minestop Stops mining." << endl << " minestop Stops mining." << endl
<< " mineforce <enable> Forces mining, even when there are no transactions." << endl << " mineforce <enable> 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 << " 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 << " transact Execute a given transaction." << endl
<< " send Execute a given transaction with current secret." << endl << " send Execute a given transaction with current secret." << endl
<< " contract Create a new contract 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 << " listaccounts List the accounts on the network." << endl
<< " listcontracts List the contracts on the network." << endl << " listcontracts List the contracts on the network." << endl
#endif #endif
<< " setsecret <secret> Set the secret to the hex secret key." << endl << " setsigningkey <addr> Set the address with which to sign transactions." << endl
<< " setaddress <addr> Set the coinbase (mining payout) address." << endl << " setaddress <addr> Set the coinbase (mining payout) address." << endl
<< " exportconfig <path> Export the config (.RLP) to the path provided." << endl << " exportconfig <path> Export the config (.RLP) to the path provided." << endl
<< " importconfig <path> Import the config (.RLP) from the path provided." << endl << " importconfig <path> Import the config (.RLP) from the path provided." << endl
@ -125,13 +125,18 @@ void help()
#endif #endif
<< " -K,--kill First kill the blockchain." << endl << " -K,--kill First kill the blockchain." << endl
<< " -R,--rebuild Rebuild the blockchain from the existing database." << endl << " -R,--rebuild Rebuild the blockchain from the existing database." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl << " -s,--import-secret <secret> Import a secret key into the key store and use as the default." << endl
<< " -S,--session-secret <secretkeyhex> Set the secret key for use with send command, for this session only." << endl << " -S,--import-session-secret <secret> Import a secret key into the key store and use as the default for this session only." << endl
<< " --sign-key <address> Sign all transactions with the key of the given address." << endl
<< " --session-sign-key <address> Sign all transactions with the key of the given address for this session only." << endl
<< " --master <password> Give the master password for the key store." << endl << " --master <password> Give the master password for the key store." << endl
<< " --password <password> Give a password for a private key." << endl
<< endl
<< "Client transacting:" << endl << "Client transacting:" << endl
<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl << " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (default: 30.679)." << endl << " -e,--ether-price <n> 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 << " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
<< endl
<< "Client mining:" << endl << "Client mining:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl << " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
<< " -m,--mining <on/off/number> Enable mining, optionally for a specified number of blocks (default: off)" << endl << " -m,--mining <on/off/number> Enable mining, optionally for a specified number of blocks (default: off)" << endl
@ -141,6 +146,7 @@ void help()
<< " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl << " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl
<< " --opencl-device <n> When mining using -G/--opencl use OpenCL device n (default: 0)." << endl << " --opencl-device <n> When mining using -G/--opencl use OpenCL device n (default: 0)." << endl
<< " -t, --mining-threads <n> Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl << " -t, --mining-threads <n> Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl
<< endl
<< "Client networking:" << endl << "Client networking:" << endl
<< " --client-name <name> Add a name to your client's version string (default: blank)." << endl << " --client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -b,--bootstrap Connect to the default Ethereum peerserver." << endl << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl
@ -153,12 +159,15 @@ void help()
<< " --network-id <n> Only connect to other hosts with this network id (default:0)." << endl << " --network-id <n> Only connect to other hosts with this network id (default:0)." << endl
<< " --upnp <on/off> Use UPnP for NAT (default: on)." << endl << " --upnp <on/off> Use UPnP for NAT (default: on)." << endl
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
<< endl
<< "Work farming mode:" << endl << "Work farming mode:" << endl
<< " -F,--farm <url> Put into mining farm mode with the work server at URL. Use with -G/--opencl." << endl << " -F,--farm <url> Put into mining farm mode with the work server at URL. Use with -G/--opencl." << endl
<< " --farm-recheck <n> Leave n ms between checks for changed work (default: 500)." << endl << " --farm-recheck <n> Leave n ms between checks for changed work (default: 500)." << endl
#endif #endif
<< endl
<< "Ethash verify mode:" << endl << "Ethash verify mode:" << endl
<< " -w,--check-pow <headerHash> <seedHash> <difficulty> <nonce> Check PoW credentials for validity." << endl << " -w,--check-pow <headerHash> <seedHash> <difficulty> <nonce> Check PoW credentials for validity." << endl
<< endl
<< "Benchmarking mode:" << endl << "Benchmarking mode:" << endl
<< " -M,--benchmark Benchmark for mining and exit; use with --cpu and --opencl." << endl << " -M,--benchmark Benchmark for mining and exit; use with --cpu and --opencl." << endl
<< " --benchmark-warmup <seconds> Set the duration of warmup for the benchmark tests (default: 3)." << endl << " --benchmark-warmup <seconds> Set the duration of warmup for the benchmark tests (default: 3)." << endl
@ -167,14 +176,17 @@ void help()
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
<< " --phone-home <on/off> When benchmarking, publish results (default: on)" << endl << " --phone-home <on/off> When benchmarking, publish results (default: on)" << endl
#endif #endif
<< endl
<< "DAG creation mode:" << endl << "DAG creation mode:" << endl
<< " -D,--create-dag <this/next/number> Create the DAG in preparation for mining on given block and exit." << endl << " -D,--create-dag <this/next/number> Create the DAG in preparation for mining on given block and exit." << endl
<< endl
<< "Import/export modes:" << endl << "Import/export modes:" << endl
<< " -I,--import <file> Import file as a concatenated series of blocks and exit." << endl << " -I,--import <file> Import file as a concatenated series of blocks and exit." << endl
<< " -E,--export <file> Export file as a concatenated series of blocks and exit." << endl << " -E,--export <file> Export file as a concatenated series of blocks and exit." << endl
<< " --from <n> Export only from block n; n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl << " --from <n> Export only from block n; n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl
<< " --to <n> Export only to block n (inclusive); n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl << " --to <n> Export only to block n (inclusive); n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl
<< " --only <n> Equivalent to --export-from n --export-to n." << endl << " --only <n> Equivalent to --export-from n --export-to n." << endl
<< endl
<< "General Options:" << endl << "General Options:" << endl
<< " -d,--db-path <path> Load database from path (default: " << getDataDir() << ")" << endl << " -d,--db-path <path> Load database from path (default: " << getDataDir() << ")" << endl
#if ETH_EVMJIT || !ETH_TRUE #if ETH_EVMJIT || !ETH_TRUE
@ -537,13 +549,14 @@ int main(int argc, char** argv)
/// Mining params /// Mining params
unsigned mining = 0; unsigned mining = 0;
bool forceMining = false; bool forceMining = false;
KeyPair sigKey = KeyPair::create(); Address signingKey;
Secret sessionSecret; Address sessionKey;
Address coinbase = sigKey.address(); Address beneficiary = signingKey;
/// Structured logging params /// Structured logging params
bool structuredLogging = false; bool structuredLogging = false;
string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S"; string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S";
string structuredLoggingURL;
/// Transaction params /// Transaction params
TransactionPriority priority = TransactionPriority::Medium; TransactionPriority priority = TransactionPriority::Medium;
@ -568,11 +581,20 @@ int main(int argc, char** argv)
string configFile = getDataDir() + "/config.rlp"; string configFile = getDataDir() + "/config.rlp";
bytes b = contents(configFile); bytes b = contents(configFile);
strings passwordsToNote;
Secrets toImport;
if (b.size()) if (b.size())
{ {
RLP config(b); RLP config(b);
sigKey = KeyPair(config[0].toHash<Secret>()); if (config[0].size() == 32) // secret key - import and forget.
coinbase = config[1].toHash<Address>(); {
Secret s = config[0].toHash<Secret>();
toImport.push_back(s);
}
else // new format - just use it as an address.
signingKey = config[0].toHash<Address>();
beneficiary = config[1].toHash<Address>();
} }
for (int i = 1; i < argc; ++i) 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; 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]); remotePort = (short)atoi(argv[++i]);
} }
else if (arg == "--password" && i + 1 < argc)
passwordsToNote.push_back(argv[++i]);
else if (arg == "--master" && i + 1 < argc) else if (arg == "--master" && i + 1 < argc)
masterPassword = argv[++i]; masterPassword = argv[++i];
else if ((arg == "-I" || arg == "--import") && i + 1 < argc) 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) else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
try { try {
coinbase = h160(fromHex(argv[++i], WhenError::Throw)); beneficiary = h160(fromHex(argv[++i], WhenError::Throw));
} }
catch (BadHexCharacter&) catch (BadHexCharacter&)
{ {
@ -758,14 +782,32 @@ int main(int argc, char** argv)
minerType = MinerType::CPU; minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl") else if (arg == "-G" || arg == "--opencl")
minerType = MinerType::GPU; minerType = MinerType::GPU;
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) /*<< " -s,--import-secret <secret> Import a secret key into the key store and use as the default." << endl
sigKey = KeyPair(h256(fromHex(argv[++i]))); << " -S,--import-session-secret <secret> Import a secret key into the key store and use as the default for this session only." << endl
else if ((arg == "-S" || arg == "--session-secret") && i + 1 < argc) << " --sign-key <address> Sign all transactions with the key of the given address." << endl
sessionSecret = h256(fromHex(argv[++i])); << " --session-sign-key <address> 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) else if (arg == "--structured-logging-format" && i + 1 < argc)
structuredLoggingFormat = string(argv[++i]); structuredLoggingFormat = string(argv[++i]);
else if (arg == "--structured-logging") else if (arg == "--structured-logging")
structuredLogging = true; 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) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i]; dbPath = argv[++i];
else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc) 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); RLPStream config(2);
config << sigKey.secret() << coinbase; config << signingKey << beneficiary;
writeFile(configFile, config.out()); writeFile(configFile, config.out());
} }
if (sessionSecret) if (sessionKey)
sigKey = KeyPair(sessionSecret); signingKey = sessionKey;
if (minerType == MinerType::CPU) if (minerType == MinerType::CPU)
ProofOfWork::CPUMiner::setNumInstances(miningThreads); ProofOfWork::CPUMiner::setNumInstances(miningThreads);
@ -983,7 +1033,7 @@ int main(int argc, char** argv)
if (!clientName.empty()) if (!clientName.empty())
clientName += "/"; clientName += "/";
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
@ -1072,27 +1122,68 @@ int main(int argc, char** argv)
c->setGasPricer(gasPricer); c->setGasPricer(gasPricer);
c->setForceMining(forceMining); c->setForceMining(forceMining);
c->setTurboMining(minerType == MinerType::GPU); c->setTurboMining(minerType == MinerType::GPU);
c->setAddress(coinbase); c->setAddress(beneficiary);
c->setNetworkId(networkId); c->setNetworkId(networkId);
} }
cout << "Transaction Signer: " << sigKey.address() << endl; cout << "Transaction Signer: " << signingKey << endl;
cout << "Mining Benefactor: " << coinbase << endl; cout << "Mining Benefactor: " << beneficiary << endl;
web3.startNetwork(); web3.startNetwork();
cout << "Node ID: " << web3.enode() << endl; cout << "Node ID: " << web3.enode() << endl;
if (bootstrap) if (bootstrap)
web3.addNode(p2p::NodeId(), Host::pocHost()); for (auto const& i: Host::pocHosts())
web3.requirePeer(i.first, i.second);
if (remoteHost.size()) if (remoteHost.size())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); 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<WebThreeStubServer> jsonrpcServer; shared_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector; unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector;
if (jsonrpc > -1) if (jsonrpc > -1)
{ {
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({sigKey}))); jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getPassword, keyManager), vector<KeyPair>()));
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
} }
#endif #endif
@ -1103,15 +1194,15 @@ int main(int argc, char** argv)
if (interactive) if (interactive)
{ {
string logbuf; additional = "Press Enter";
string l; string l;
while (!g_exit) 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; cout << logbuf << "Press Enter" << flush;
std::getline(cin, l); std::getline(cin, l);
logbuf.clear(); logbuf.clear();
g_logPost = [&](std::string const& a, char const*) { logbuf += a + "\n"; }; silence = true;
#if ETH_READLINE #if ETH_READLINE
if (l.size()) if (l.size())
@ -1224,7 +1315,7 @@ int main(int argc, char** argv)
iss >> g_logVerbosity; iss >> g_logVerbosity;
cout << "Verbosity: " << g_logVerbosity << endl; cout << "Verbosity: " << g_logVerbosity << endl;
} }
#if ETH_JSONRPC #if ETH_JSONRPC || !ETH_TRUE
else if (cmd == "jsonport") else if (cmd == "jsonport")
{ {
if (iss.peek() != -1) if (iss.peek() != -1)
@ -1236,7 +1327,7 @@ int main(int argc, char** argv)
if (jsonrpc < 0) if (jsonrpc < 0)
jsonrpc = SensibleHttpPort; jsonrpc = SensibleHttpPort;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({sigKey}))); jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getPassword, keyManager), vector<KeyPair>()));
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
} }
else if (cmd == "jsonstop") else if (cmd == "jsonstop")
@ -1248,11 +1339,8 @@ int main(int argc, char** argv)
#endif #endif
else if (cmd == "address") else if (cmd == "address")
{ {
cout << "Current address:" << endl << sigKey.address() << endl; cout << "Current mining beneficiary:" << endl << beneficiary << endl;
} cout << "Current signing account:" << endl << signingKey << endl;
else if (cmd == "secret")
{
cout << "Secret Key: " << sigKey.secret() << endl;
} }
else if (c && cmd == "block") else if (c && cmd == "block")
{ {
@ -1267,7 +1355,15 @@ int main(int argc, char** argv)
} }
else if (c && cmd == "balance") else if (c && cmd == "balance")
{ {
cout << "Current balance: " << formatBalance( c->balanceAt(sigKey.address())) << " = " <<c->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") else if (c && cmd == "transact")
{ {
@ -1383,7 +1479,7 @@ int main(int argc, char** argv)
try try
{ {
Address dest = h160(fromHex(hexAddr, WhenError::Throw)); 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) catch (BadHexCharacter& _e)
{ {
@ -1452,7 +1548,7 @@ int main(int argc, char** argv)
else if (gas < minGas) else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas; cwarn << "Minimum gas amount is" << minGas;
else else
c->submitTransaction(sigKey.secret(), endowment, init, gas, gasPrice); c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), endowment, init, gas, gasPrice);
} }
else else
cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX"; 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) if (iss.peek() != -1)
{ {
string hexSec; string hexSec;
iss >> hexSec; iss >> hexSec;
sigKey = KeyPair(h256(fromHex(hexSec))); signingKey = Address(fromHex(hexSec));
} }
else else
cwarn << "Require parameter: setSecret HEXSECRETKEY"; cwarn << "Require parameter: setSecret HEXSECRETKEY";
@ -1586,7 +1682,7 @@ int main(int argc, char** argv)
{ {
try try
{ {
coinbase = h160(fromHex(hexAddr, WhenError::Throw)); beneficiary = h160(fromHex(hexAddr, WhenError::Throw));
} }
catch (BadHexCharacter& _e) catch (BadHexCharacter& _e)
{ {
@ -1609,7 +1705,7 @@ int main(int argc, char** argv)
string path; string path;
iss >> path; iss >> path;
RLPStream config(2); RLPStream config(2);
config << sigKey.secret() << coinbase; config << signingKey << beneficiary;
writeFile(path, config.out()); writeFile(path, config.out());
} }
else else
@ -1625,8 +1721,8 @@ int main(int argc, char** argv)
if (b.size()) if (b.size())
{ {
RLP config(b); RLP config(b);
sigKey = KeyPair(config[0].toHash<Secret>()); signingKey = config[0].toHash<Address>();
coinbase = config[1].toHash<Address>(); beneficiary = config[1].toHash<Address>();
} }
else else
cwarn << path << "has no content!"; cwarn << path << "has no content!";

2
exp/main.cpp

@ -81,7 +81,7 @@ int main()
// cdebug << toString(a2); // cdebug << toString(a2);
Address a2("19c486071651b2650449ba3c6a807f316a73e8fe"); Address a2("19c486071651b2650449ba3c6a807f316a73e8fe");
cdebug << keyman.keys(); cdebug << keyman.accountDetails();
cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; }); cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; });
cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2); cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2);

4
libdevcore/Common.cpp

@ -28,7 +28,9 @@ using namespace dev;
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 void HasInvariants::checkInvariants() const
{ {

2
libdevcore/Common.h

@ -82,6 +82,8 @@ using u160s = std::vector<u160>;
using u256Set = std::set<u256>; using u256Set = std::set<u256>;
using u160Set = std::set<u160>; using u160Set = std::set<u160>;
extern const u256 UndefinedU256;
// Map types. // Map types.
using StringMap = std::map<std::string, std::string>; using StringMap = std::map<std::string, std::string>;
using u256Map = std::map<u256, u256>; using u256Map = std::map<u256, u256>;

14
libdevcore/CommonIO.cpp

@ -20,7 +20,8 @@
*/ */
#include "CommonIO.h" #include "CommonIO.h"
#include <iostream>
#include <cstdlib>
#include <fstream> #include <fstream>
#include "Exceptions.h" #include "Exceptions.h"
using namespace std; 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()); 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
}

2
libdevcore/CommonIO.h

@ -42,6 +42,8 @@
namespace dev 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. /// 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); bytes contents(std::string const& _file);
std::string contentsString(std::string const& _file); std::string contentsString(std::string const& _file);

1
libdevcore/FixedHash.h

@ -282,6 +282,7 @@ namespace std
{ {
/// Forward std::hash<dev::FixedHash> to dev::FixedHash::hash. /// Forward std::hash<dev::FixedHash> to dev::FixedHash::hash.
template<> struct hash<dev::h64>: dev::h64::hash {}; template<> struct hash<dev::h64>: dev::h64::hash {};
template<> struct hash<dev::h128>: dev::h128::hash {};
template<> struct hash<dev::h160>: dev::h160::hash {}; template<> struct hash<dev::h160>: dev::h160::hash {};
template<> struct hash<dev::h256>: dev::h256::hash {}; template<> struct hash<dev::h256>: dev::h256::hash {};
template<> struct hash<dev::h512>: dev::h512::hash {}; template<> struct hash<dev::h512>: dev::h512::hash {};

13
libdevcore/StructuredLogger.cpp

@ -34,6 +34,15 @@ using namespace std;
namespace dev 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 void StructuredLogger::outputJson(Json::Value const& _value, std::string const& _name) const
{ {
Json::Value event; Json::Value event;
@ -41,7 +50,7 @@ void StructuredLogger::outputJson(Json::Value const& _value, std::string const&
Json::FastWriter fastWriter; Json::FastWriter fastWriter;
Guard l(s_lock); Guard l(s_lock);
event[_name] = _value; 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) 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; Json::Value event;
event["client_impl"] = _clientImpl; event["client_impl"] = _clientImpl;
event["eth_version"] = std::string(_ethVersion); event["eth_version"] = std::string(_ethVersion);
// TODO net_version
event["ts"] = dev::toString(chrono::system_clock::now(), get().m_timeFormat.c_str()); event["ts"] = dev::toString(chrono::system_clock::now(), get().m_timeFormat.c_str());
get().outputJson(event, "starting"); get().outputJson(event, "starting");
@ -64,6 +74,7 @@ void StructuredLogger::stopping(string const& _clientImpl, const char* _ethVersi
Json::Value event; Json::Value event;
event["client_impl"] = _clientImpl; event["client_impl"] = _clientImpl;
event["eth_version"] = std::string(_ethVersion); event["eth_version"] = std::string(_ethVersion);
// TODO net_version
event["ts"] = dev::toString(chrono::system_clock::now(), get().m_timeFormat.c_str()); event["ts"] = dev::toString(chrono::system_clock::now(), get().m_timeFormat.c_str());
get().outputJson(event, "stopping"); get().outputJson(event, "stopping");

14
libdevcore/StructuredLogger.h

@ -25,6 +25,7 @@
#pragma once #pragma once
#include <fstream>
#include <string> #include <string>
#include <chrono> #include <chrono>
@ -46,11 +47,7 @@ public:
* http://en.cppreference.com/w/cpp/chrono/c/strftime * http://en.cppreference.com/w/cpp/chrono/c/strftime
* with which to display timestamps * with which to display timestamps
*/ */
void initialize(bool _enabled, std::string const& _timeFormat) void initialize(bool _enabled, std::string const& _timeFormat, std::string const& _destinationURL = "");
{
m_enabled = _enabled;
m_timeFormat = _timeFormat;
}
static StructuredLogger& get() static StructuredLogger& get()
{ {
@ -92,6 +89,11 @@ public:
std::string const& _prevHash std::string const& _prevHash
); );
static void transactionReceived(std::string const& _hash, std::string const& _remoteId); static void transactionReceived(std::string const& _hash, std::string const& _remoteId);
// TODO: static void pendingQueueChanged(std::vector<h256> const& _hashes);
// TODO: static void miningStarted();
// TODO: static void stillMining(unsigned _hashrate);
// TODO: static void miningStopped();
private: private:
// Singleton class. Private default ctor and no copying // Singleton class. Private default ctor and no copying
StructuredLogger() = default; StructuredLogger() = default;
@ -102,6 +104,8 @@ private:
bool m_enabled = false; bool m_enabled = false;
std::string m_timeFormat = "%Y-%m-%dT%H:%M:%S"; std::string m_timeFormat = "%Y-%m-%dT%H:%M:%S";
mutable std::ofstream m_out;
}; };
} }

1
libdevcrypto/SecretStore.cpp

@ -104,6 +104,7 @@ void SecretStore::save(std::string const& _keysPath)
void SecretStore::load(std::string const& _keysPath) void SecretStore::load(std::string const& _keysPath)
{ {
fs::path p(_keysPath); fs::path p(_keysPath);
boost::filesystem::create_directories(p);
js::mValue v; js::mValue v;
for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it)
if (is_regular_file(it->path())) if (is_regular_file(it->path()))

5
libdevcrypto/SecretStore.h

@ -23,6 +23,7 @@
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <libdevcore/FixedHash.h>
#include "Common.h" #include "Common.h"
#include "FileSystem.h" #include "FileSystem.h"
@ -48,8 +49,8 @@ private:
static std::string encrypt(bytes const& _v, std::string const& _pass); static std::string encrypt(bytes const& _v, std::string const& _pass);
static bytes decrypt(std::string const& _v, std::string const& _pass); static bytes decrypt(std::string const& _v, std::string const& _pass);
mutable std::map<h128, bytes> m_cached; mutable std::unordered_map<h128, bytes> m_cached;
std::map<h128, std::pair<std::string, std::string>> m_keys; std::unordered_map<h128, std::pair<std::string, std::string>> m_keys;
}; };
} }

11
libethcore/Common.h

@ -136,5 +136,16 @@ private:
using Handler = std::shared_ptr<Signal::HandlerAux>; using Handler = std::shared_ptr<Signal::HandlerAux>;
struct TransactionSkeleton
{
bool creation = false;
Address from;
Address to;
u256 value;
bytes data;
u256 gas = UndefinedU256;
u256 gasPrice = UndefinedU256;
};
} }
} }

2
libethcore/CommonJS.cpp

@ -26,8 +26,6 @@
namespace dev namespace dev
{ {
const u256 UndefinedU256 = ~(u256)0;
Address toAddress(std::string const& _sn) Address toAddress(std::string const& _sn)
{ {
if (_sn.size() == 40) if (_sn.size() == 40)

14
libethcore/CommonJS.h

@ -48,8 +48,6 @@ inline Address jsToAddress(std::string const& _s) { return jsToFixed<sizeof(dev:
/// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256. /// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256.
std::string prettyU256(u256 _n, bool _abridged = true); std::string prettyU256(u256 _n, bool _abridged = true);
extern const u256 UndefinedU256;
} }
@ -59,18 +57,6 @@ namespace dev
namespace eth namespace eth
{ {
struct TransactionSkeleton
{
bool creation = false;
Address from;
Address to;
u256 value;
bytes data;
u256 gas = UndefinedU256;
u256 gasPrice = UndefinedU256;
};
/// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest". /// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest".
BlockNumber jsToBlockNumber(std::string const& _js); BlockNumber jsToBlockNumber(std::string const& _js);

39
libethereum/BlockChain.cpp

@ -456,7 +456,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
{ {
// Check transactions are valid and that they result in a state equivalent to our state_root. // Check transactions are valid and that they result in a state equivalent to our state_root.
// Get total difficulty increase and update state, checking it. // Get total difficulty increase and update state, checking it.
State s(_db); //, bi.coinbaseAddress State s(_db);
auto tdIncrease = s.enactOn(&_block, bi, *this, _ir); auto tdIncrease = s.enactOn(&_block, bi, *this, _ir);
BlockLogBlooms blb; BlockLogBlooms blb;
@ -467,6 +467,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
br.receipts.push_back(s.receipt(i)); br.receipts.push_back(s.receipt(i));
} }
s.cleanup(true); s.cleanup(true);
td = pd.totalDifficulty + tdIncrease; td = pd.totalDifficulty + tdIncrease;
#if ETH_TIMED_IMPORTS #if ETH_TIMED_IMPORTS
@ -603,7 +604,6 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
{ {
newLastBlockHash = bi.hash(); newLastBlockHash = bi.hash();
newLastBlockNumber = (unsigned)bi.number; newLastBlockNumber = (unsigned)bi.number;
extrasBatch.Put(ldb::Slice("best"), ldb::Slice((char const*)&(bi.hash()), 32));
} }
clog(BlockChainNote) << " Imported and best" << td << " (#" << bi.number << "). Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << route; clog(BlockChainNote) << " Imported and best" << td << " (#" << bi.number << "). Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << route;
@ -623,12 +623,33 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
m_blocksDB->Write(m_writeOptions, &blocksBatch); m_blocksDB->Write(m_writeOptions, &blocksBatch);
m_extrasDB->Write(m_writeOptions, &extrasBatch); m_extrasDB->Write(m_writeOptions, &extrasBatch);
DEV_WRITE_GUARDED(x_lastBlockHash) if (isKnown(bi.hash()) && !details(bi.hash()))
{ {
m_lastBlockHash = newLastBlockHash; clog(BlockChainDebug) << "Known block just inserted has no details.";
m_lastBlockNumber = newLastBlockNumber; 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 #if ETH_PARANOIA || !ETH_TRUE
checkConsistency(); checkConsistency();
#endif #endif
@ -646,14 +667,6 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
if (!route.empty()) if (!route.empty())
noteCanonChanged(); 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 fresh;
h256s dead; h256s dead;
bool isOld = true; bool isOld = true;

1
libethereum/ClientBase.h

@ -81,6 +81,7 @@ public:
/// Submits a new contract-creation transaction. /// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through). /// @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; 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. /// 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; 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;

4
libethereum/Interface.h

@ -72,6 +72,10 @@ public:
/// @returns the new contract's address (assuming it all goes through). /// @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; 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. /// Blocks until all pending transactions have been processed.
virtual void flushTransactions() = 0; virtual void flushTransactions() = 0;

14
libethereum/KeyManager.cpp

@ -28,6 +28,7 @@
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace eth;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
KeyManager::KeyManager(std::string const& _keysFile): KeyManager::KeyManager(std::string const& _keysFile):
@ -151,9 +152,18 @@ void KeyManager::kill(Address const& _a)
m_store.kill(id); m_store.kill(id);
} }
std::map<Address, std::pair<std::string, std::string>> KeyManager::keys() const AddressHash KeyManager::accounts() const
{ {
std::map<Address, std::pair<std::string, std::string>> 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<Address, std::pair<std::string, std::string>> KeyManager::accountDetails() const
{
std::unordered_map<Address, std::pair<std::string, std::string>> ret;
for (auto const& i: m_addrLookup) for (auto const& i: m_addrLookup)
if (m_keyInfo.count(i.second) > 0) 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)); ret[i.first] = make_pair(m_keyInfo.at(i.second).info, m_passwordInfo.at(m_keyInfo.at(i.second).passHash));

17
libethereum/KeyManager.h

@ -28,7 +28,8 @@
namespace dev namespace dev
{ {
namespace eth
{
class UnknownPassword: public Exception {}; class UnknownPassword: public Exception {};
struct KeyInfo struct KeyInfo
@ -65,7 +66,10 @@ public:
bool load(std::string const& _pass); bool load(std::string const& _pass);
void save(std::string const& _pass) const { write(_pass, m_keysFile); } void save(std::string const& _pass) const { write(_pass, m_keysFile); }
std::map<Address, std::pair<std::string, std::string>> keys() const; void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; }
AddressHash accounts() const;
std::unordered_map<Address, std::pair<std::string, std::string>> accountDetails() const;
h128 uuid(Address const& _a) const; h128 uuid(Address const& _a) const;
Address address(h128 const& _uuid) const; Address address(h128 const& _uuid) const;
@ -92,12 +96,12 @@ private:
void write(h128 const& _key, std::string const& _keysFile) const; void write(h128 const& _key, std::string const& _keysFile) const;
// Ethereum keys. // Ethereum keys.
std::map<Address, h128> m_addrLookup; std::unordered_map<Address, h128> m_addrLookup;
std::map<h128, KeyInfo> m_keyInfo; std::unordered_map<h128, KeyInfo> m_keyInfo;
std::map<h256, std::string> m_passwordInfo; std::unordered_map<h256, std::string> m_passwordInfo;
// Passwords that we're storing. // Passwords that we're storing.
mutable std::map<h256, std::string> m_cachedPasswords; mutable std::unordered_map<h256, std::string> m_cachedPasswords;
// The default password for keys in the keystore - protected by the master password. // The default password for keys in the keystore - protected by the master password.
std::string m_password; std::string m_password;
@ -108,3 +112,4 @@ private:
}; };
} }
}

1
libethereum/State.cpp

@ -707,6 +707,7 @@ void State::cleanup(bool _fullCommit)
{ {
if (_fullCommit) if (_fullCommit)
{ {
paranoia("immediately before database commit", true); paranoia("immediately before database commit", true);
// Commit the new trie to disk. // Commit the new trie to disk.

2
libethereum/State.h

@ -49,7 +49,7 @@ class BlockChain;
class State; class State;
struct StateChat: public LogChannel { static const char* name(); static const int verbosity = 4; }; 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 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; }; struct StateSafeExceptions: public LogChannel { static const char* name(); static const int verbosity = 21; };

10
libp2p/Host.cpp

@ -389,6 +389,16 @@ string Host::pocHost()
return "poc-" + strs[1] + ".ethdev.com"; return "poc-" + strs[1] + ".ethdev.com";
} }
std::unordered_map<Public, std::string> const& Host::pocHosts()
{
static const std::unordered_map<Public, std::string> 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) void Host::addNode(NodeId const& _node, NodeIPEndpoint const& _endpoint)
{ {
// return if network is stopped while waiting on Host::run() or nodeTable to start // return if network is stopped while waiting on Host::run() or nodeTable to start

2
libp2p/Host.h

@ -95,6 +95,8 @@ public:
/// Default host for current version of client. /// Default host for current version of client.
static std::string pocHost(); static std::string pocHost();
static std::unordered_map<Public, std::string> const& pocHosts();
/// Register a peer-capability; all new peer connections will have this capability. /// Register a peer-capability; all new peer connections will have this capability.
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; std::shared_ptr<T> ret(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; std::shared_ptr<T> ret(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; }

78
libp2p/NodeTable.cpp

@ -127,20 +127,19 @@ void NodeTable::discover()
list<NodeId> NodeTable::nodes() const list<NodeId> NodeTable::nodes() const
{ {
list<NodeId> nodes; list<NodeId> nodes;
Guard l(x_nodes); DEV_GUARDED(x_nodes)
for (auto& i: m_nodes) for (auto& i: m_nodes)
nodes.push_back(i.second->id); nodes.push_back(i.second->id);
return move(nodes); return move(nodes);
} }
list<NodeEntry> NodeTable::snapshot() const list<NodeEntry> NodeTable::snapshot() const
{ {
list<NodeEntry> ret; list<NodeEntry> ret;
Guard l(x_state); DEV_GUARDED(x_state)
for (auto s: m_state) for (auto const& s: m_state)
for (auto np: s.nodes) for (auto const& np: s.nodes)
if (auto n = np.lock()) if (auto n = np.lock())
if (!!n)
ret.push_back(*n); ret.push_back(*n);
return move(ret); return move(ret);
} }
@ -151,8 +150,7 @@ Node NodeTable::node(NodeId const& _id)
if (m_nodes.count(_id)) if (m_nodes.count(_id))
{ {
auto entry = m_nodes[_id]; auto entry = m_nodes[_id];
Node n(_id, entry->endpoint, entry->required); return Node(_id, entry->endpoint, entry->required);
return move(n);
} }
return UnspecifiedNode; return UnspecifiedNode;
} }
@ -173,7 +171,7 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr<set<shared_pt
clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds."; clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds.";
return; return;
} }
else if(!_round && !_tried) else if (!_round && !_tried)
// initialized _tried on first round // initialized _tried on first round
_tried.reset(new set<shared_ptr<NodeEntry>>()); _tried.reset(new set<shared_ptr<NodeEntry>>());
@ -228,7 +226,7 @@ vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeId _target)
while (head != tail && head < s_bins && count < s_bucketSize) while (head != tail && head < s_bins && count < s_bucketSize)
{ {
Guard l(x_state); 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 (auto p = n.lock())
{ {
if (count < s_bucketSize) if (count < s_bucketSize)
@ -238,7 +236,7 @@ vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeId _target)
} }
if (count < s_bucketSize && tail) 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 (auto p = n.lock())
{ {
if (count < s_bucketSize) if (count < s_bucketSize)
@ -255,7 +253,7 @@ vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeId _target)
while (head < s_bins && count < s_bucketSize) while (head < s_bins && count < s_bucketSize)
{ {
Guard l(x_state); 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 (auto p = n.lock())
{ {
if (count < s_bucketSize) if (count < s_bucketSize)
@ -269,7 +267,7 @@ vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeId _target)
while (tail > 0 && count < s_bucketSize) while (tail > 0 && count < s_bucketSize)
{ {
Guard l(x_state); 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 (auto p = n.lock())
{ {
if (count < s_bucketSize) if (count < s_bucketSize)
@ -282,7 +280,7 @@ vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeId _target)
vector<shared_ptr<NodeEntry>> ret; vector<shared_ptr<NodeEntry>> ret;
for (auto& nodes: found) 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()) if (ret.size() < s_bucketSize && !!n->endpoint && n->endpoint.isAllowed())
ret.push_back(n); ret.push_back(n);
return move(ret); return move(ret);
@ -306,12 +304,15 @@ void NodeTable::evict(shared_ptr<NodeEntry> _leastSeen, shared_ptr<NodeEntry> _n
if (!m_socketPointer->isOpen()) if (!m_socketPointer->isOpen())
return; 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)); m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id));
if (m_evictions.size() == 1) ec = m_evictions.size();
doCheckEvictions(boost::system::error_code());
} }
if (ec == 1)
doCheckEvictions(boost::system::error_code());
ping(_leastSeen.get()); ping(_leastSeen.get());
} }
@ -428,24 +429,27 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
Pong in = Pong::fromBytesConstRef(_from, rlpBytes); Pong in = Pong::fromBytesConstRef(_from, rlpBytes);
// whenever a pong is received, check if it's in m_evictions // whenever a pong is received, check if it's in m_evictions
Guard le(x_evictions); bool found = false;
bool evictionEntry = false; EvictionTimeout evictionEntry;
for (auto it = m_evictions.begin(); it != m_evictions.end(); it++) DEV_GUARDED(x_evictions)
if (it->first.first == nodeid && it->first.second > std::chrono::steady_clock::now()) 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)) found = true;
dropNode(n); evictionEntry = *it;
m_evictions.erase(it);
if (auto n = nodeEntry(it->first.first)) break;
n->pending = false; }
if (found)
it = m_evictions.erase(it); {
} if (auto n = nodeEntry(evictionEntry.second))
dropNode(n);
// if not, check if it's known/pending or a pubk discovery ping if (auto n = nodeEntry(evictionEntry.first.first))
if (!evictionEntry) n->pending = false;
}
else
{ {
// if not, check if it's known/pending or a pubk discovery ping
if (auto n = nodeEntry(nodeid)) if (auto n = nodeEntry(nodeid))
n->pending = false; n->pending = false;
else 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 (chrono::steady_clock::now() - e.first.second > c_reqTimeout)
if (m_nodes.count(e.second)) if (m_nodes.count(e.second))
drop.push_back(m_nodes[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(); drop.unique();

4
libp2p/NodeTable.h

@ -45,10 +45,12 @@ struct NodeEntry: public Node
bool pending = true; ///< Node will be ignored until Pong is received bool pending = true; ///< Node will be ignored until Pong is received
}; };
enum NodeTableEventType { enum NodeTableEventType
{
NodeEntryAdded, NodeEntryAdded,
NodeEntryDropped NodeEntryDropped
}; };
class NodeTable; class NodeTable;
class NodeTableEventHandler class NodeTableEventHandler
{ {

5
libtestutils/FixedWebThreeServer.cpp

@ -16,7 +16,12 @@
*/ */
/** @file FixedWebThreeStubServer.cpp /** @file FixedWebThreeStubServer.cpp
* @author Marek Kotewicz <marek@ethdev.com> * @author Marek Kotewicz <marek@ethdev.com>
* @author Gav Wood <i@gavwood.com>
* @date 2015 * @date 2015
*/ */
#include "FixedWebThreeServer.h" #include "FixedWebThreeServer.h"
#include <libethereum/Interface.h>
using namespace std;
using namespace dev;
using namespace eth;

6
libtestutils/FixedWebThreeServer.h

@ -23,6 +23,7 @@
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libweb3jsonrpc/WebThreeStubServerBase.h> #include <libweb3jsonrpc/WebThreeStubServerBase.h>
#include <libweb3jsonrpc/AccountHolder.h>
/** /**
* @brief dummy JSON-RPC api implementation * @brief dummy JSON-RPC api implementation
@ -33,7 +34,10 @@
class FixedWebThreeServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace class FixedWebThreeServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{ {
public: public:
FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client): WebThreeStubServerBase(_conn, _accounts), m_client(_client) {}; FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _allAccounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, std::make_shared<dev::eth::FixedAccountHolder>([=](){return _client;}, _allAccounts), _allAccounts),
m_client(_client)
{}
private: private:
dev::eth::Interface* client() override { return m_client; } dev::eth::Interface* client() override { return m_client; }

51
libweb3jsonrpc/AccountHolder.cpp

@ -26,6 +26,7 @@
#include <ctime> #include <ctime>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/KeyManager.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -35,31 +36,23 @@ vector<TransactionSkeleton> g_emptyQueue;
static std::mt19937 g_randomNumberGenerator(time(0)); static std::mt19937 g_randomNumberGenerator(time(0));
static Mutex x_rngMutex; static Mutex x_rngMutex;
void AccountHolder::setAccounts(vector<KeyPair> const& _accounts) vector<Address> AccountHolder::allAccounts() const
{ {
m_accounts.clear(); vector<Address> accounts;
for (auto const& keyPair: _accounts) accounts += realAccounts();
{
m_accounts.push_back(keyPair.address());
m_keyPairs[keyPair.address()] = keyPair;
}
}
vector<Address> AccountHolder::getAllAccounts() const
{
vector<Address> accounts = m_accounts;
for (auto const& pair: m_proxyAccounts) for (auto const& pair: m_proxyAccounts)
if (!isRealAccount(pair.first)) if (!isRealAccount(pair.first))
accounts.push_back(pair.first); accounts.push_back(pair.first);
return accounts; return accounts;
} }
Address const& AccountHolder::getDefaultTransactAccount() const Address const& AccountHolder::defaultTransactAccount() const
{ {
if (m_accounts.empty()) auto accounts = realAccounts();
if (accounts.empty())
return ZeroAddress; return ZeroAddress;
Address const* bestMatch = &m_accounts.front(); Address const* bestMatch = &*accounts.begin();
for (auto const& account: m_accounts) for (auto const& account: accounts)
if (m_client()->balanceAt(account) > m_client()->balanceAt(*bestMatch)) if (m_client()->balanceAt(account) > m_client()->balanceAt(*bestMatch))
bestMatch = &account; bestMatch = &account;
return *bestMatch; return *bestMatch;
@ -94,7 +87,7 @@ void AccountHolder::queueTransaction(TransactionSkeleton const& _transaction)
m_transactionQueues[id].second.push_back(_transaction); m_transactionQueues[id].second.push_back(_transaction);
} }
vector<TransactionSkeleton> const& AccountHolder::getQueuedTransactions(int _id) const vector<TransactionSkeleton> const& AccountHolder::queuedTransactions(int _id) const
{ {
if (!m_transactionQueues.count(_id)) if (!m_transactionQueues.count(_id))
return g_emptyQueue; return g_emptyQueue;
@ -106,3 +99,27 @@ void AccountHolder::clearQueue(int _id)
if (m_transactionQueues.count(_id)) if (m_transactionQueues.count(_id))
m_transactionQueues.at(_id).second.clear(); 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);
}

85
libweb3jsonrpc/AccountHolder.h

@ -24,17 +24,20 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <algorithm>
#include <vector> #include <vector>
#include <map> #include <map>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libethereum/Transaction.h>
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
class KeyManager;
class Interface; class Interface;
}
/** /**
* Manages real accounts (where we know the secret key) and proxy accounts (where transactions * Manages real accounts (where we know the secret key) and proxy accounts (where transactions
@ -43,32 +46,84 @@ class Interface;
class AccountHolder class AccountHolder
{ {
public: public:
explicit AccountHolder(std::function<eth::Interface*()> const& _client): m_client(_client) {} explicit AccountHolder(std::function<Interface*()> 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. Addresses allAccounts() const;
void setAccounts(std::vector<KeyPair> const& _accounts); bool isRealAccount(Address const& _account) const { return realAccounts().count(_account) > 0; }
std::vector<Address> const& getRealAccounts() const { return m_accounts; }
bool isRealAccount(Address const& _account) const { return m_keyPairs.count(_account) > 0; }
bool isProxyAccount(Address const& _account) const { return m_proxyAccounts.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(); } Address const& defaultTransactAccount() const;
std::vector<Address> getAllAccounts() const;
Address const& getDefaultTransactAccount() const;
int addProxyAccount(Address const& _account); int addProxyAccount(Address const& _account);
bool removeProxyAccount(unsigned _id); bool removeProxyAccount(unsigned _id);
void queueTransaction(eth::TransactionSkeleton const& _transaction); void queueTransaction(eth::TransactionSkeleton const& _transaction);
std::vector<eth::TransactionSkeleton> const& getQueuedTransactions(int _id) const; std::vector<eth::TransactionSkeleton> const& queuedTransactions(int _id) const;
void clearQueue(int _id); void clearQueue(int _id);
protected:
std::function<Interface*()> m_client;
private: private:
using TransactionQueue = std::vector<eth::TransactionSkeleton>; using TransactionQueue = std::vector<eth::TransactionSkeleton>;
std::map<Address, KeyPair> m_keyPairs; std::unordered_map<Address, int> m_proxyAccounts;
std::vector<Address> m_accounts; std::unordered_map<int, std::pair<Address, TransactionQueue>> m_transactionQueues;
std::map<Address, int> m_proxyAccounts; };
std::map<int, std::pair<Address, TransactionQueue>> m_transactionQueues;
std::function<eth::Interface*()> m_client; class SimpleAccountHolder: public AccountHolder
{
public:
SimpleAccountHolder(std::function<Interface*()> const& _client, std::function<std::string(Address)> 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<std::string(Address)> m_getPassword;
KeyManager& m_keyManager;
};
class FixedAccountHolder: public AccountHolder
{
public:
FixedAccountHolder(std::function<Interface*()> const& _client, std::vector<dev::KeyPair> const& _accounts):
AccountHolder(_client)
{
setAccounts(_accounts);
}
void setAccounts(std::vector<dev::KeyPair> 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<dev::Address, dev::Secret> m_accounts;
}; };
}
} }

4
libweb3jsonrpc/WebThreeStubServer.cpp

@ -33,8 +33,8 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts): WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr<AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts):
WebThreeStubServerBase(_conn, _accounts), WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts),
m_web3(_web3) m_web3(_web3)
{ {
auto path = getDataDir() + "/.web3"; auto path = getDataDir() + "/.web3";

2
libweb3jsonrpc/WebThreeStubServer.h

@ -41,7 +41,7 @@ class WebThreeDirect;
class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{ {
public: public:
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts); WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts);
virtual std::string web3_clientVersion(); virtual std::string web3_clientVersion();

43
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -297,10 +297,11 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message
return res; return res;
} }
WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, vector<dev::KeyPair> const& _accounts): WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, vector<dev::KeyPair> const& _sshAccounts):
AbstractWebThreeStubServer(_conn), m_ethAccounts(make_shared<AccountHolder>(bind(&WebThreeStubServerBase::client, this))) AbstractWebThreeStubServer(_conn),
m_ethAccounts(_ethAccounts)
{ {
m_ethAccounts->setAccounts(_accounts); setIdentities(_sshAccounts);
} }
void WebThreeStubServerBase::setIdentities(vector<dev::KeyPair> const& _ids) void WebThreeStubServerBase::setIdentities(vector<dev::KeyPair> const& _ids)
@ -353,7 +354,7 @@ string WebThreeStubServerBase::eth_gasPrice()
Json::Value WebThreeStubServerBase::eth_accounts() Json::Value WebThreeStubServerBase::eth_accounts()
{ {
Json::Value ret(Json::arrayValue); Json::Value ret(Json::arrayValue);
for (auto const& i: m_ethAccounts->getAllAccounts()) for (auto const& i: m_ethAccounts->allAccounts())
ret.append(toJS(i)); ret.append(toJS(i));
return ret; return ret;
} }
@ -499,7 +500,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json)
TransactionSkeleton t = toTransaction(_json); TransactionSkeleton t = toTransaction(_json);
if (!t.from) if (!t.from)
t.from = m_ethAccounts->getDefaultTransactAccount(); t.from = m_ethAccounts->defaultTransactAccount();
if (t.creation) if (t.creation)
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));;
if (t.gasPrice == UndefinedU256) if (t.gasPrice == UndefinedU256)
@ -507,10 +508,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json)
if (t.gas == UndefinedU256) if (t.gas == UndefinedU256)
t.gas = min<u256>(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); t.gas = min<u256>(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice);
if (m_ethAccounts->isRealAccount(t.from)) m_ethAccounts->authenticate(t);
authenticate(t, false);
else if (m_ethAccounts->isProxyAccount(t.from))
authenticate(t, true);
return ret; return ret;
} }
@ -528,7 +526,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json)
TransactionSkeleton t = toTransaction(_json); TransactionSkeleton t = toTransaction(_json);
if (!t.from) if (!t.from)
t.from = m_ethAccounts->getDefaultTransactAccount(); t.from = m_ethAccounts->defaultTransactAccount();
if (t.creation) if (t.creation)
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));;
if (t.gasPrice == UndefinedU256) if (t.gasPrice == UndefinedU256)
@ -536,10 +534,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json)
if (t.gas == UndefinedU256) if (t.gas == UndefinedU256)
t.gas = min<u256>(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); t.gas = min<u256>(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice);
if (m_ethAccounts->isRealAccount(t.from)) m_ethAccounts->authenticate(t);
authenticate(t, false);
else if (m_ethAccounts->isProxyAccount(t.from))
authenticate(t, true);
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)); 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); TransactionSkeleton t = toTransaction(_json);
if (!t.from) if (!t.from)
t.from = m_ethAccounts->getDefaultTransactAccount(); t.from = m_ethAccounts->defaultTransactAccount();
// if (!m_accounts->isRealAccount(t.from)) // if (!m_accounts->isRealAccount(t.from))
// return ret; // return ret;
if (t.gasPrice == UndefinedU256) 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)); BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS));
} }
} }
bool WebThreeStubServerBase::eth_flush() bool WebThreeStubServerBase::eth_flush()
@ -906,7 +900,7 @@ Json::Value WebThreeStubServerBase::eth_fetchQueuedTransactions(string const& _a
auto id = jsToInt(_accountId); auto id = jsToInt(_accountId);
Json::Value ret(Json::arrayValue); Json::Value ret(Json::arrayValue);
// TODO: throw an error on no account with given id // 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)); ret.append(toJson(t));
m_ethAccounts->clearQueue(id); m_ethAccounts->clearQueue(id);
return ret; return ret;
@ -1077,18 +1071,3 @@ Json::Value WebThreeStubServerBase::shh_getMessages(string const& _filterId)
BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); 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<KeyPair>& _accounts)
{
m_ethAccounts->setAccounts(_accounts);
}

10
libweb3jsonrpc/WebThreeStubServerBase.h

@ -36,10 +36,10 @@
namespace dev namespace dev
{ {
class WebThreeNetworkFace; class WebThreeNetworkFace;
class AccountHolder;
class KeyPair; class KeyPair;
namespace eth namespace eth
{ {
class AccountHolder;
struct TransactionSkeleton; struct TransactionSkeleton;
class Interface; class Interface;
} }
@ -68,7 +68,7 @@ public:
class WebThreeStubServerBase: public AbstractWebThreeStubServer class WebThreeStubServerBase: public AbstractWebThreeStubServer
{ {
public: public:
WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts); WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _sshAccounts);
virtual std::string web3_sha3(std::string const& _param1); virtual std::string web3_sha3(std::string const& _param1);
virtual std::string web3_clientVersion() { return "C++ (ethereum-cpp)"; } 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_getFilterChanges(std::string const& _filterId);
virtual Json::Value shh_getMessages(std::string const& _filterId); virtual Json::Value shh_getMessages(std::string const& _filterId);
void setAccounts(std::vector<dev::KeyPair> const& _accounts);
void setIdentities(std::vector<dev::KeyPair> const& _ids); void setIdentities(std::vector<dev::KeyPair> const& _ids);
std::map<dev::Public, dev::Secret> const& ids() const { return m_shhIds; } std::map<dev::Public, dev::Secret> const& ids() const { return m_shhIds; }
protected:
virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
protected: protected:
virtual dev::eth::Interface* client() = 0; virtual dev::eth::Interface* client() = 0;
virtual std::shared_ptr<dev::shh::Interface> face() = 0; virtual std::shared_ptr<dev::shh::Interface> face() = 0;
virtual dev::WebThreeNetworkFace* network() = 0; virtual dev::WebThreeNetworkFace* network() = 0;
virtual dev::WebThreeStubDatabaseFace* db() = 0; virtual dev::WebThreeStubDatabaseFace* db() = 0;
std::shared_ptr<dev::AccountHolder> m_ethAccounts; std::shared_ptr<dev::eth::AccountHolder> m_ethAccounts;
std::map<dev::Public, dev::Secret> m_shhIds; std::map<dev::Public, dev::Secret> m_shhIds;
std::map<unsigned, dev::Public> m_shhWatches; std::map<unsigned, dev::Public> m_shhWatches;

5
mix/ClientModel.cpp

@ -85,7 +85,8 @@ ClientModel::ClientModel():
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector<KeyPair>(), m_client.get())); m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>());
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector<KeyPair>(), m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
} }
@ -280,7 +281,7 @@ void ClientModel::setupState(QVariantMap _state)
transactionSequence.push_back(transactionSettings); transactionSequence.push_back(transactionSettings);
} }
} }
m_web3Server->setAccounts(userAccounts); m_ethAccounts->setAccounts(userAccounts);
executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString())); executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString()));
} }

3
mix/ClientModel.h

@ -35,7 +35,7 @@
namespace dev namespace dev
{ {
namespace eth { class Account; } namespace eth { class Account; class FixedAccountHolder; }
namespace mix namespace mix
{ {
@ -235,6 +235,7 @@ private:
std::unique_ptr<MixClient> m_client; std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector; std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server; std::unique_ptr<Web3Server> m_web3Server;
std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts;
QList<u256> m_gasCosts; QList<u256> m_gasCosts;
std::map<QString, Address> m_contractAddresses; std::map<QString, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames; std::map<Address, QString> m_contractNames;

5
mix/Web3Server.cpp

@ -24,6 +24,7 @@
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libethereum/Interface.h> #include <libethereum/Interface.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libweb3jsonrpc/AccountHolder.h>
#include "Web3Server.h" #include "Web3Server.h"
using namespace dev::mix; using namespace dev::mix;
@ -108,8 +109,8 @@ class EmptyNetwork : public dev::WebThreeNetworkFace
} }
Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client): Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, _accounts), WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts),
m_client(_client), m_client(_client),
m_network(new EmptyNetwork()) m_network(new EmptyNetwork())
{ {

3
mix/Web3Server.h

@ -25,6 +25,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <QObject> #include <QObject>
#include <libweb3jsonrpc/AccountHolder.h>
#include <libweb3jsonrpc/WebThreeStubServerBase.h> #include <libweb3jsonrpc/WebThreeStubServerBase.h>
namespace dev namespace dev
@ -38,7 +39,7 @@ class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev
Q_OBJECT Q_OBJECT
public: public:
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client); Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client);
virtual ~Web3Server(); virtual ~Web3Server();
signals: signals:

28
test/libweb3jsonrpc/AccountHolder.cpp

@ -22,6 +22,9 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libweb3jsonrpc/AccountHolder.h> #include <libweb3jsonrpc/AccountHolder.h>
using namespace std;
using namespace dev;
using namespace eth;
namespace dev namespace dev
{ {
@ -32,16 +35,17 @@ BOOST_AUTO_TEST_SUITE(AccountHolderTest)
BOOST_AUTO_TEST_CASE(ProxyAccountUseCase) BOOST_AUTO_TEST_CASE(ProxyAccountUseCase)
{ {
AccountHolder h = AccountHolder(std::function<eth::Interface*()>()); FixedAccountHolder h = FixedAccountHolder(function<Interface*()>(), vector<KeyPair>());
BOOST_CHECK(h.getAllAccounts().empty());
BOOST_CHECK(h.getRealAccounts().empty()); BOOST_CHECK(h.allAccounts().empty());
BOOST_CHECK(h.realAccounts().empty());
Address addr("abababababababababababababababababababab"); Address addr("abababababababababababababababababababab");
Address addr2("abababababababababababababababababababab"); Address addr2("abababababababababababababababababababab");
int id = h.addProxyAccount(addr); int id = h.addProxyAccount(addr);
BOOST_CHECK(h.getQueuedTransactions(id).empty()); BOOST_CHECK(h.queuedTransactions(id).empty());
// register it again // register it again
int secondID = h.addProxyAccount(addr); int secondID = h.addProxyAccount(addr);
BOOST_CHECK(h.getQueuedTransactions(secondID).empty()); BOOST_CHECK(h.queuedTransactions(secondID).empty());
eth::TransactionSkeleton t1; eth::TransactionSkeleton t1;
eth::TransactionSkeleton t2; eth::TransactionSkeleton t2;
@ -49,20 +53,20 @@ BOOST_AUTO_TEST_CASE(ProxyAccountUseCase)
t1.data = fromHex("12345678"); t1.data = fromHex("12345678");
t2.from = addr; t2.from = addr;
t2.data = fromHex("abcdef"); t2.data = fromHex("abcdef");
BOOST_CHECK(h.getQueuedTransactions(id).empty()); BOOST_CHECK(h.queuedTransactions(id).empty());
h.queueTransaction(t1); h.queueTransaction(t1);
BOOST_CHECK_EQUAL(1, h.getQueuedTransactions(id).size()); BOOST_CHECK_EQUAL(1, h.queuedTransactions(id).size());
h.queueTransaction(t2); 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 // 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.queuedTransactions(id)[0].data == t1.data);
BOOST_CHECK(h.getQueuedTransactions(id)[1].data == t2.data); BOOST_CHECK(h.queuedTransactions(id)[1].data == t2.data);
h.clearQueue(id); h.clearQueue(id);
BOOST_CHECK(h.getQueuedTransactions(id).empty()); BOOST_CHECK(h.queuedTransactions(id).empty());
// removing fails because it never existed // removing fails because it never existed
BOOST_CHECK(!h.removeProxyAccount(secondID)); BOOST_CHECK(!h.removeProxyAccount(secondID));
BOOST_CHECK(h.removeProxyAccount(id)); BOOST_CHECK(h.removeProxyAccount(id));

Loading…
Cancel
Save