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. 249
      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. 33
      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. 62
      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()
if (NCURSES)
add_subdirectory(neth)
# Commented out until caktux refactors with the new wallet API.
# add_subdirectory(neth)
endif ()
if (GUI)

5
alethzero/Context.h

@ -29,7 +29,7 @@
class QComboBox;
namespace dev { namespace eth { struct StateDiff; } }
namespace dev { namespace eth { struct StateDiff; class KeyManager; } }
#define Small "font-size: small; "
#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; "
@ -64,5 +64,8 @@ public:
virtual std::pair<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 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">
<enum>QFrame::NoFrame</enum>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>

208
alethzero/MainWin.cpp

@ -31,6 +31,7 @@
#include <QtWidgets/QDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QListWidgetItem>
#include <QtWebEngine/QtWebEngine>
#include <QtWebEngineWidgets/QWebEngineView>
#include <QtWebEngineWidgets/QWebEngineCallback>
@ -143,6 +144,38 @@ Main::Main(QWidget *parent) :
// ui->log->addItem(QString::fromStdString(s));
};
// Open Key Store
bool opened = false;
if (m_keyManager.exists())
while (!opened)
{
QString s = QInputDialog::getText(nullptr, "Master password", "Enter your MASTER account password.", QLineEdit::Password, QString());
if (m_keyManager.load(s.toStdString()))
opened = true;
else if (QMessageBox::question(
nullptr,
"Invalid password entered",
"The password you entered is incorrect. If you have forgotten your password, and you wish to start afresh, manually remove the file: " + QString::fromStdString(getDataDir("ethereum")) + "/keys.info",
QMessageBox::Retry,
QMessageBox::Abort)
== QMessageBox::Abort)
exit(0);
}
if (!opened)
{
QString password;
while (true)
{
password = QInputDialog::getText(nullptr, "Master password", "Enter a MASTER password for your key store. Make it strong. You probably want to write it down somewhere and keep it safe and secure; your identity will rely on this - you never want to lose it.", QLineEdit::Password, QString());
QString confirm = QInputDialog::getText(nullptr, "Master password", "Confirm this password by typing it again", QLineEdit::Password, QString());
if (password == confirm)
break;
QMessageBox::warning(nullptr, "Try again", "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok);
}
m_keyManager.create(password.toStdString());
m_keyManager.import(Secret::random(), "Default identity");
}
#if ETH_DEBUG
m_servers.append("127.0.0.1:30300");
#endif
@ -168,15 +201,13 @@ Main::Main(QWidget *parent) :
ui->blockCount->setText(QString("PV%1.%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(eth::c_minorProtocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version));
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
QSettings s("ethereum", "alethzero");
m_networkConfig = s.value("peers").toByteArray();
bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size());
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth", "shh"}, p2p::NetworkPreferences(), network));
m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this));
connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening();
@ -198,6 +229,7 @@ Main::Main(QWidget *parent) :
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin());
readSettings();
#if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts);
@ -358,9 +390,9 @@ void Main::installBalancesWatch()
// TODO: Update for new currencies reg.
for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i)
altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1)));
for (auto i: m_myKeys)
for (auto const& i: m_keyManager.accounts())
for (auto c: altCoins)
tf.address(c).topic(0, h256(i.address(), h256::AlignRight));
tf.address(c).topic(0, h256(i, h256::AlignRight));
uninstallWatch(m_balancesFilter);
m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); });
@ -429,7 +461,7 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered()
{
m_transact.setEnvironment(m_myKeys, ethereum(), &m_natSpecDB);
m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB);
m_transact.exec();
}
@ -616,17 +648,7 @@ void Main::on_paranoia_triggered()
void Main::writeSettings()
{
QSettings s("ethereum", "alethzero");
{
QByteArray b;
b.resize(sizeof(Secret) * m_myKeys.size());
auto p = b.data();
for (auto i: m_myKeys)
{
memcpy(p, &(i.secret()), sizeof(Secret));
p += sizeof(Secret);
}
s.setValue("address", b);
}
s.remove("address");
{
QByteArray b;
b.resize(sizeof(Secret) * m_myIdentities.size());
@ -666,6 +688,20 @@ void Main::writeSettings()
s.setValue("windowState", saveState());
}
Secret Main::retrieveSecret(Address const& _a) const
{
auto info = m_keyManager.accountDetails()[_a];
while (true)
{
if (Secret s = m_keyManager.secret(_a, [&](){
return QInputDialog::getText(const_cast<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)
{
QSettings s("ethereum", "alethzero");
@ -675,22 +711,17 @@ void Main::readSettings(bool _skipGeometry)
restoreState(s.value("windowState").toByteArray());
{
m_myKeys.clear();
QByteArray b = s.value("address").toByteArray();
if (b.isEmpty())
m_myKeys.append(KeyPair::create());
else
if (!b.isEmpty())
{
h256 k;
for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i)
{
memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret));
if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k)))
m_myKeys.append(KeyPair(k));
if (!m_keyManager.accounts().count(KeyPair(k).address()))
m_keyManager.import(k, "Imported (UNSAFE) key.");
}
}
ethereum()->setAddress(m_myKeys.back().address());
m_server->setAccounts(keysAsVector(m_myKeys));
}
{
@ -735,16 +766,38 @@ void Main::readSettings(bool _skipGeometry)
on_urlEdit_returnPressed();
}
std::string Main::getPassword(std::string const& _title, std::string const& _for)
{
QString password;
while (true)
{
password = QInputDialog::getText(nullptr, QString::fromStdString(_title), QString::fromStdString(_for), QLineEdit::Password, QString());
QString confirm = QInputDialog::getText(nullptr, QString::fromStdString(_title), "Confirm this password by typing it again", QLineEdit::Password, QString());
if (password == confirm)
break;
QMessageBox::warning(nullptr, QString::fromStdString(_title), "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok);
}
return password.toStdString();
}
void Main::on_importKey_triggered()
{
QString s = QInputDialog::getText(this, "Import Account Key", "Enter account's secret key");
QString s = QInputDialog::getText(this, "Import Account Key", "Enter account's secret key", QLineEdit::Password);
bytes b = fromHex(s.toStdString());
if (b.size() == 32)
{
auto k = KeyPair(h256(b));
if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end())
if (!m_keyManager.accounts().count(k.address()))
{
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)
{
m_myKeys.append(k);
std::string password = getPassword("Import Account Key", "Enter the password you would like to use for this key. Don't forget it!");
std::string hint = QInputDialog::getText(this, "Import Account Key", "Enter a hint to help you remember this password.").toStdString();
m_keyManager.import(k.secret(), s.toStdString(), password, hint);
}
else
m_keyManager.import(k.secret(), s.toStdString());
keysChanged();
}
else
@ -785,15 +838,8 @@ void Main::on_importKeyFile_triggered()
}
cnote << k.address();
if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end())
{
if (m_myKeys.empty())
{
m_myKeys.push_back(KeyPair::create());
keysChanged();
}
ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_myKeys.back().address(), {}, c_txGas, gasPrice());
}
if (!m_keyManager.accounts().count(k.address()))
ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice());
else
QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account.");
}
@ -812,10 +858,12 @@ void Main::on_importKeyFile_triggered()
void Main::on_exportKey_triggered()
{
if (ui->ourAccounts->currentRow() >= 0 && ui->ourAccounts->currentRow() < m_myKeys.size())
if (ui->ourAccounts->currentRow() >= 0)
{
auto k = m_myKeys[ui->ourAccounts->currentRow()];
QMessageBox::information(this, "Export Account Key", "Secret key to account " + QString::fromStdString(render(k.address()) + " is:\n" + toHex(k.sec().ref())));
auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray();
Address h((byte const*)hba.data(), Address::ConstructFromPointer);
Secret s = retrieveSecret(h);
QMessageBox::information(this, "Export Account Key", "Secret key to account " + QString::fromStdString(render(h) + " is:\n" + s.hex()));
}
}
@ -913,6 +961,24 @@ void Main::refreshMining()
*/
}
void Main::setBeneficiary(Address const& _b)
{
for (int i = 0; i < ui->ourAccounts->count(); ++i)
{
auto hba = ui->ourAccounts->item(i)->data(Qt::UserRole).toByteArray();
auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer);
ui->ourAccounts->item(i)->setCheckState(h == _b ? Qt::Checked : Qt::Unchecked);
}
m_beneficiary = _b;
ethereum()->setAddress(_b);
}
void Main::on_ourAccounts_itemClicked(QListWidgetItem* _i)
{
auto hba = _i->data(Qt::UserRole).toByteArray();
setBeneficiary(Address((byte const*)hba.data(), Address::ConstructFromPointer));
}
void Main::refreshBalances()
{
cwatch << "refreshBalances()";
@ -931,11 +997,13 @@ void Main::refreshBalances()
// cdebug << n << addr << denom << sha3(h256(n).asBytes());
altCoins[addr] = make_tuple(fromRaw(n), 0, denom);
}*/
for (auto i: m_myKeys)
for (pair<Address, std::pair<std::string, std::string>> const& i: m_keyManager.accountDetails())
{
u256 b = ethereum()->balanceAt(i.address());
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.address()))).arg((unsigned)ethereum()->countAt(i.address())), ui->ourAccounts))
->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size));
u256 b = ethereum()->balanceAt(i.first);
QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.first))).arg((unsigned)ethereum()->countAt(i.first)).arg(QString::fromStdString(i.second.first)), ui->ourAccounts);
li->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size));
li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
li->setCheckState(m_beneficiary == i.first ? Qt::Checked : Qt::Unchecked);
totalBalance += b;
// for (auto& c: altCoins)
@ -1385,23 +1453,6 @@ void Main::on_transactionQueue_currentItemChanged()
ui->pendingInfo->moveCursor(QTextCursor::Start);
}
void Main::ourAccountsRowsMoved()
{
QList<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()
{
QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex");
@ -1827,7 +1878,7 @@ void Main::on_mine_triggered()
{
if (ui->mine->isChecked())
{
ethereum()->setAddress(m_myKeys.last().address());
ethereum()->setAddress(m_beneficiary);
ethereum()->startMining();
}
else
@ -1837,7 +1888,6 @@ void Main::on_mine_triggered()
void Main::keysChanged()
{
onBalancesChange();
m_server->setAccounts(keysAsVector(m_myKeys));
}
bool beginsWith(Address _a, bytes const& _b)
@ -1901,24 +1951,39 @@ void Main::on_newAccount_triggered()
t->join();
delete t;
}
m_myKeys.append(p);
QString s = QInputDialog::getText(this, "Create Account", "Enter this account's name");
if (QMessageBox::question(this, "Create Account", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
{
std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!");
std::string hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString();
m_keyManager.import(p.secret(), s.toStdString(), password, hint);
}
else
m_keyManager.import(p.secret(), s.toStdString());
keysChanged();
}
void Main::on_killAccount_triggered()
{
if (ui->ourAccounts->currentRow() >= 0 && ui->ourAccounts->currentRow() < m_myKeys.size())
if (ui->ourAccounts->currentRow() >= 0)
{
auto k = m_myKeys[ui->ourAccounts->currentRow()];
auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray();
Address h((byte const*)hba.data(), Address::ConstructFromPointer);
auto k = m_keyManager.accountDetails()[h];
if (
ethereum()->balanceAt(k.address()) != 0 &&
QMessageBox::critical(this, "Kill Account?!",
QString::fromStdString("Account " + render(k.address()) + " has " + formatBalance(ethereum()->balanceAt(k.address())) + " in it. It, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n"
ethereum()->balanceAt(h) != 0 &&
QMessageBox::critical(this, QString::fromStdString("Kill Account " + k.first + "?!"),
QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it. It, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n"
"Are you sure you want to continue?"),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
return;
m_myKeys.erase(m_myKeys.begin() + ui->ourAccounts->currentRow());
m_keyManager.kill(h);
if (m_keyManager.accounts().empty())
m_keyManager.import(Secret::random(), "Default account");
keysChanged();
if (m_beneficiary == h)
setBeneficiary(*m_keyManager.accounts().begin());
}
}
@ -1929,7 +1994,8 @@ void Main::on_go_triggered()
ui->net->setChecked(true);
on_net_triggered();
}
web3()->addNode(p2p::NodeId(), Host::pocHost());
for (auto const& i: Host::pocHosts())
web3()->requirePeer(i.first, i.second);
}
std::string Main::prettyU256(dev::u256 const& _n) const

17
alethzero/MainWin.h

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

69
alethzero/OurWebThreeStubServer.cpp

@ -20,23 +20,23 @@
*/
#include "OurWebThreeStubServer.h"
#include <QMessageBox>
#include <QAbstractButton>
#include <libwebthree/WebThree.h>
#include <libnatspec/NatspecExpressionEvaluator.h>
#include "MainWin.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3,
vector<KeyPair> const& _accounts, Main* _main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(_main)
OurWebThreeStubServer::OurWebThreeStubServer(
jsonrpc::AbstractServerConnector& _conn,
WebThreeDirect& _web3,
Main* _main
):
WebThreeStubServer(_conn, _web3, make_shared<OurAccountHolder>(_web3, _main), _main->owned().toVector().toStdVector()),
m_main(_main)
{
connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
}
string OurWebThreeStubServer::shh_newIdentity()
@ -46,7 +46,18 @@ string OurWebThreeStubServer::shh_newIdentity()
return toJS(kp.pub());
}
bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text)
OurAccountHolder::OurAccountHolder(
WebThreeDirect& _web3,
Main* _main
):
AccountHolder([=](){ return m_web3->ethereum(); }),
m_web3(&_web3),
m_main(_main)
{
connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
}
bool OurAccountHolder::showAuthenticationPopup(string const& _title, string const& _text)
{
if (!m_main->confirm())
{
@ -66,18 +77,18 @@ bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string
//return button == QMessageBox::Ok;
}
bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy)
bool OurAccountHolder::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy)
{
return showAuthenticationPopup("Contract Creation Transaction", string("ÐApp is attemping to create a contract; ") + (_toProxy ? "(this transaction is not executed directly, but forwarded to another ÐApp) " : "") + "to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
}
bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t, bool _toProxy)
bool OurAccountHolder::showSendNotice(TransactionSkeleton const& _t, bool _toProxy)
{
return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to) + (_toProxy ? " (this transaction is not executed directly, but forwarded to another ÐApp)" : "") +
", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
}
bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy)
bool OurAccountHolder::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy)
{
return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!",
"ÐApp is attempting to call into an unknown contract at address " +
@ -93,25 +104,47 @@ bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t,
"REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!");
}
void OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t, bool _toProxy)
void OurAccountHolder::authenticate(TransactionSkeleton const& _t)
{
Guard l(x_queued);
m_queued.push(make_pair(_t, _toProxy));
m_queued.push(_t);
}
void OurWebThreeStubServer::doValidations()
void OurAccountHolder::doValidations()
{
Guard l(x_queued);
while (!m_queued.empty())
{
auto q = m_queued.front();
auto t = m_queued.front();
m_queued.pop();
if (validateTransaction(q.first, q.second))
WebThreeStubServerBase::authenticate(q.first, q.second);
bool proxy = isProxyAccount(t.from);
if (!proxy && !isRealAccount(t.from))
{
cwarn << "Trying to send from non-existant account" << t.from;
return;
}
// TODO: determine gas price.
if (!validateTransaction(t, proxy))
return;
if (proxy)
queueTransaction(t);
else
// sign and submit.
if (Secret s = m_main->retrieveSecret(t.from))
m_web3->ethereum()->submitTransaction(s, t);
}
}
bool OurWebThreeStubServer::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)
AddressHash OurAccountHolder::realAccounts() const
{
return m_main->keyManager().accounts();
}
bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)
{
if (_t.creation)
{

43
alethzero/OurWebThreeStubServer.h

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

55
alethzero/Transact.cpp

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

5
alethzero/Transact.h

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

249
alethzero/Transact.ui

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

190
eth/main.cpp

@ -37,6 +37,7 @@
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libethereum/KeyManager.h>
#include <libwebthree/WebThree.h>
#if ETH_JSCONSOLE || !ETH_TRUE
#include <libjsconsole/JSConsole.h>
@ -46,6 +47,7 @@
#include <readline/history.h>
#endif
#if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/AccountHolder.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
@ -89,10 +91,8 @@ void interactiveHelp()
<< " minestart Starts mining." << endl
<< " minestop Stops mining." << 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
<< " balance Gives the current balance." << endl
<< " accounts Gives information on all owned accounts (balances, mining beneficiary and default signer)." << endl
<< " transact Execute a given transaction." << endl
<< " send Execute a given transaction with current secret." << endl
<< " contract Create a new contract with current secret." << endl
@ -101,7 +101,7 @@ void interactiveHelp()
<< " listaccounts List the accounts on the network." << endl
<< " listcontracts List the contracts on the network." << endl
#endif
<< " setsecret <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
<< " exportconfig <path> Export the config (.RLP) to the path provided." << endl
<< " importconfig <path> Import the config (.RLP) from the path provided." << endl
@ -125,13 +125,18 @@ void help()
#endif
<< " -K,--kill First kill the blockchain." << endl
<< " -R,--rebuild Rebuild the blockchain from the existing database." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -S,--session-secret <secretkeyhex> Set the secret key for use with send command, for this session only." << endl
<< " -s,--import-secret <secret> Import a secret key into the key store and use as the default." << 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
<< " --password <password> Give a password for a private key." << endl
<< endl
<< "Client transacting:" << 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
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
<< endl
<< "Client mining:" << 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
@ -141,6 +146,7 @@ void help()
<< " --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
<< " -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-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -b,--bootstrap Connect to the default Ethereum peerserver." << endl
@ -153,12 +159,15 @@ void help()
<< " --network-id <n> Only connect to other hosts with this network id (default:0)." << endl
<< " --upnp <on/off> Use UPnP for NAT (default: on)." << endl
#if ETH_JSONRPC || !ETH_TRUE
<< endl
<< "Work farming mode:" << 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
#endif
<< endl
<< "Ethash verify mode:" << endl
<< " -w,--check-pow <headerHash> <seedHash> <difficulty> <nonce> Check PoW credentials for validity." << endl
<< endl
<< "Benchmarking mode:" << endl
<< " -M,--benchmark Benchmark for mining and exit; use with --cpu and --opencl." << endl
<< " --benchmark-warmup <seconds> Set the duration of warmup for the benchmark tests (default: 3)." << endl
@ -167,14 +176,17 @@ void help()
#if ETH_JSONRPC || !ETH_TRUE
<< " --phone-home <on/off> When benchmarking, publish results (default: on)" << endl
#endif
<< endl
<< "DAG creation mode:" << 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
<< " -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
<< " --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
<< " --only <n> Equivalent to --export-from n --export-to n." << endl
<< endl
<< "General Options:" << endl
<< " -d,--db-path <path> Load database from path (default: " << getDataDir() << ")" << endl
#if ETH_EVMJIT || !ETH_TRUE
@ -537,13 +549,14 @@ int main(int argc, char** argv)
/// Mining params
unsigned mining = 0;
bool forceMining = false;
KeyPair sigKey = KeyPair::create();
Secret sessionSecret;
Address coinbase = sigKey.address();
Address signingKey;
Address sessionKey;
Address beneficiary = signingKey;
/// Structured logging params
bool structuredLogging = false;
string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S";
string structuredLoggingURL;
/// Transaction params
TransactionPriority priority = TransactionPriority::Medium;
@ -568,11 +581,20 @@ int main(int argc, char** argv)
string configFile = getDataDir() + "/config.rlp";
bytes b = contents(configFile);
strings passwordsToNote;
Secrets toImport;
if (b.size())
{
RLP config(b);
sigKey = KeyPair(config[0].toHash<Secret>());
coinbase = config[1].toHash<Address>();
if (config[0].size() == 32) // secret key - import and forget.
{
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)
@ -600,6 +622,8 @@ int main(int argc, char** argv)
cerr << "-p is DEPRECATED. It will be removed for the Frontier. Use --port instead (or place directly as host:port)." << endl;
remotePort = (short)atoi(argv[++i]);
}
else if (arg == "--password" && i + 1 < argc)
passwordsToNote.push_back(argv[++i]);
else if (arg == "--master" && i + 1 < argc)
masterPassword = argv[++i];
else if ((arg == "-I" || arg == "--import") && i + 1 < argc)
@ -742,7 +766,7 @@ int main(int argc, char** argv)
}
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
try {
coinbase = h160(fromHex(argv[++i], WhenError::Throw));
beneficiary = h160(fromHex(argv[++i], WhenError::Throw));
}
catch (BadHexCharacter&)
{
@ -758,14 +782,32 @@ int main(int argc, char** argv)
minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl")
minerType = MinerType::GPU;
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
sigKey = KeyPair(h256(fromHex(argv[++i])));
else if ((arg == "-S" || arg == "--session-secret") && i + 1 < argc)
sessionSecret = h256(fromHex(argv[++i]));
/*<< " -s,--import-secret <secret> Import a secret key into the key store and use as the default." << 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*/
else if ((arg == "-s" || arg == "--import-secret") && i + 1 < argc)
{
Secret s(fromHex(argv[++i]));
toImport.push_back(s);
signingKey = toAddress(s);
}
else if ((arg == "-S" || arg == "--import-session-secret") && i + 1 < argc)
{
Secret s(fromHex(argv[++i]));
toImport.push_back(s);
sessionKey = toAddress(s);
}
else if ((arg == "--sign-key") && i + 1 < argc)
sessionKey = Address(fromHex(argv[++i]));
else if ((arg == "--session-sign-key") && i + 1 < argc)
sessionKey = Address(fromHex(argv[++i]));
else if (arg == "--structured-logging-format" && i + 1 < argc)
structuredLoggingFormat = string(argv[++i]);
else if (arg == "--structured-logging")
structuredLogging = true;
else if (arg == "--structured-logging-destination" && i + 1 < argc)
structuredLoggingURL = argv[++i];
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc)
@ -949,16 +991,24 @@ int main(int argc, char** argv)
}
}
KeyManager keyManager;
for (auto const& s: passwordsToNote)
keyManager.notePassword(s);
for (auto const& s: toImport)
{
keyManager.import(s, "Imported key");
if (!signingKey)
signingKey = toAddress(s);
}
{
RLPStream config(2);
config << sigKey.secret() << coinbase;
config << signingKey << beneficiary;
writeFile(configFile, config.out());
}
if (sessionSecret)
sigKey = KeyPair(sessionSecret);
if (sessionKey)
signingKey = sessionKey;
if (minerType == MinerType::CPU)
ProofOfWork::CPUMiner::setNumInstances(miningThreads);
@ -983,7 +1033,7 @@ int main(int argc, char** argv)
if (!clientName.empty())
clientName += "/";
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat);
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
@ -1072,27 +1122,68 @@ int main(int argc, char** argv)
c->setGasPricer(gasPricer);
c->setForceMining(forceMining);
c->setTurboMining(minerType == MinerType::GPU);
c->setAddress(coinbase);
c->setAddress(beneficiary);
c->setNetworkId(networkId);
}
cout << "Transaction Signer: " << sigKey.address() << endl;
cout << "Mining Benefactor: " << coinbase << endl;
cout << "Transaction Signer: " << signingKey << endl;
cout << "Mining Benefactor: " << beneficiary << endl;
web3.startNetwork();
cout << "Node ID: " << web3.enode() << endl;
if (bootstrap)
web3.addNode(p2p::NodeId(), Host::pocHost());
for (auto const& i: Host::pocHosts())
web3.requirePeer(i.first, i.second);
if (remoteHost.size())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
#if ETH_JSONRPC
if (keyManager.exists())
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter your MASTER password: ");
if (!keyManager.load(masterPassword))
{
cout << "Password invalid. Try again." << endl;
masterPassword.clear();
}
}
else
{
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): ");
string confirm = getPassword("Please confirm the password by entering it again: ");
if (masterPassword != confirm)
{
cout << "Passwords were different. Try again." << endl;
masterPassword.clear();
}
}
keyManager.create(masterPassword);
}
string logbuf;
bool silence = false;
std::string additional;
g_logPost = [&](std::string const& a, char const*) { if (silence) logbuf += a + "\n"; else cout << "\r \r" << a << endl << additional << flush; };
// TODO: give hints &c.
auto getPassword = [&](Address const& a){
auto s = silence;
silence = true;
cout << endl;
string ret = dev::getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): ");
silence = s;
return ret;
};
#if ETH_JSONRPC || !ETH_TRUE
shared_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector;
if (jsonrpc > -1)
{
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();
}
#endif
@ -1103,15 +1194,15 @@ int main(int argc, char** argv)
if (interactive)
{
string logbuf;
additional = "Press Enter";
string l;
while (!g_exit)
{
g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << "Press Enter" << flush; };
silence = false;
cout << logbuf << "Press Enter" << flush;
std::getline(cin, l);
logbuf.clear();
g_logPost = [&](std::string const& a, char const*) { logbuf += a + "\n"; };
silence = true;
#if ETH_READLINE
if (l.size())
@ -1224,7 +1315,7 @@ int main(int argc, char** argv)
iss >> g_logVerbosity;
cout << "Verbosity: " << g_logVerbosity << endl;
}
#if ETH_JSONRPC
#if ETH_JSONRPC || !ETH_TRUE
else if (cmd == "jsonport")
{
if (iss.peek() != -1)
@ -1236,7 +1327,7 @@ int main(int argc, char** argv)
if (jsonrpc < 0)
jsonrpc = SensibleHttpPort;
jsonrpcConnector = unique_ptr<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();
}
else if (cmd == "jsonstop")
@ -1248,11 +1339,8 @@ int main(int argc, char** argv)
#endif
else if (cmd == "address")
{
cout << "Current address:" << endl << sigKey.address() << endl;
}
else if (cmd == "secret")
{
cout << "Secret Key: " << sigKey.secret() << endl;
cout << "Current mining beneficiary:" << endl << beneficiary << endl;
cout << "Current signing account:" << endl << signingKey << endl;
}
else if (c && cmd == "block")
{
@ -1267,7 +1355,15 @@ int main(int argc, char** argv)
}
else if (c && cmd == "balance")
{
cout << "Current balance: " << formatBalance( c->balanceAt(sigKey.address())) << " = " <<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")
{
@ -1383,7 +1479,7 @@ int main(int argc, char** argv)
try
{
Address dest = h160(fromHex(hexAddr, WhenError::Throw));
c->submitTransaction(sigKey.secret(), amount, dest, bytes(), minGas);
c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
@ -1452,7 +1548,7 @@ int main(int argc, char** argv)
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else
c->submitTransaction(sigKey.secret(), endowment, init, gas, gasPrice);
c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), endowment, init, gas, gasPrice);
}
else
cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX";
@ -1563,13 +1659,13 @@ int main(int argc, char** argv)
}
}
}
else if (cmd == "setsecret")
else if (cmd == "setsigningkey")
{
if (iss.peek() != -1)
{
string hexSec;
iss >> hexSec;
sigKey = KeyPair(h256(fromHex(hexSec)));
signingKey = Address(fromHex(hexSec));
}
else
cwarn << "Require parameter: setSecret HEXSECRETKEY";
@ -1586,7 +1682,7 @@ int main(int argc, char** argv)
{
try
{
coinbase = h160(fromHex(hexAddr, WhenError::Throw));
beneficiary = h160(fromHex(hexAddr, WhenError::Throw));
}
catch (BadHexCharacter& _e)
{
@ -1609,7 +1705,7 @@ int main(int argc, char** argv)
string path;
iss >> path;
RLPStream config(2);
config << sigKey.secret() << coinbase;
config << signingKey << beneficiary;
writeFile(path, config.out());
}
else
@ -1625,8 +1721,8 @@ int main(int argc, char** argv)
if (b.size())
{
RLP config(b);
sigKey = KeyPair(config[0].toHash<Secret>());
coinbase = config[1].toHash<Address>();
signingKey = config[0].toHash<Address>();
beneficiary = config[1].toHash<Address>();
}
else
cwarn << path << "has no content!";

2
exp/main.cpp

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

4
libdevcore/Common.cpp

@ -28,7 +28,9 @@ using namespace dev;
namespace dev
{
char const* Version = "0.9.17";
char const* Version = "0.9.18";
const u256 UndefinedU256 = ~(u256)0;
void HasInvariants::checkInvariants() const
{

2
libdevcore/Common.h

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

14
libdevcore/CommonIO.cpp

@ -20,7 +20,8 @@
*/
#include "CommonIO.h"
#include <iostream>
#include <cstdlib>
#include <fstream>
#include "Exceptions.h"
using namespace std;
@ -117,3 +118,14 @@ void dev::writeFile(std::string const& _file, bytesConstRef _data)
ofstream(_file, ios::trunc|ios::binary).write((char const*)_data.data(), _data.size());
}
std::string dev::getPassword(std::string const& _prompt)
{
#if WIN32
cout << _prompt << flush;
std::string ret;
std::getline(cin, ret);
return ret;
#else
return getpass(_prompt.c_str());
#endif
}

2
libdevcore/CommonIO.h

@ -42,6 +42,8 @@
namespace dev
{
std::string getPassword(std::string const& _prompt);
/// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes.
bytes contents(std::string const& _file);
std::string contentsString(std::string const& _file);

1
libdevcore/FixedHash.h

@ -282,6 +282,7 @@ namespace std
{
/// Forward std::hash<dev::FixedHash> to dev::FixedHash::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::h256>: dev::h256::hash {};
template<> struct hash<dev::h512>: dev::h512::hash {};

13
libdevcore/StructuredLogger.cpp

@ -34,6 +34,15 @@ using namespace std;
namespace dev
{
void StructuredLogger::initialize(bool _enabled, std::string const& _timeFormat, std::string const& _destinationURL)
{
m_enabled = _enabled;
m_timeFormat = _timeFormat;
if (_destinationURL.size() > 7 && _destinationURL.substr(0, 7) == "file://")
m_out.open(_destinationURL.substr(7));
// TODO: support tcp://
}
void StructuredLogger::outputJson(Json::Value const& _value, std::string const& _name) const
{
Json::Value event;
@ -41,7 +50,7 @@ void StructuredLogger::outputJson(Json::Value const& _value, std::string const&
Json::FastWriter fastWriter;
Guard l(s_lock);
event[_name] = _value;
cout << fastWriter.write(event) << endl;
(m_out.is_open() ? m_out : cout) << fastWriter.write(event) << endl;
}
void StructuredLogger::starting(string const& _clientImpl, const char* _ethVersion)
@ -51,6 +60,7 @@ void StructuredLogger::starting(string const& _clientImpl, const char* _ethVersi
Json::Value event;
event["client_impl"] = _clientImpl;
event["eth_version"] = std::string(_ethVersion);
// TODO net_version
event["ts"] = dev::toString(chrono::system_clock::now(), get().m_timeFormat.c_str());
get().outputJson(event, "starting");
@ -64,6 +74,7 @@ void StructuredLogger::stopping(string const& _clientImpl, const char* _ethVersi
Json::Value event;
event["client_impl"] = _clientImpl;
event["eth_version"] = std::string(_ethVersion);
// TODO net_version
event["ts"] = dev::toString(chrono::system_clock::now(), get().m_timeFormat.c_str());
get().outputJson(event, "stopping");

14
libdevcore/StructuredLogger.h

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

1
libdevcrypto/SecretStore.cpp

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

5
libdevcrypto/SecretStore.h

@ -23,6 +23,7 @@
#include <functional>
#include <mutex>
#include <libdevcore/FixedHash.h>
#include "Common.h"
#include "FileSystem.h"
@ -48,8 +49,8 @@ private:
static std::string encrypt(bytes const& _v, std::string const& _pass);
static bytes decrypt(std::string const& _v, std::string const& _pass);
mutable std::map<h128, bytes> m_cached;
std::map<h128, std::pair<std::string, std::string>> m_keys;
mutable std::unordered_map<h128, bytes> m_cached;
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>;
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
{
const u256 UndefinedU256 = ~(u256)0;
Address toAddress(std::string const& _sn)
{
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.
std::string prettyU256(u256 _n, bool _abridged = true);
extern const u256 UndefinedU256;
}
@ -59,18 +57,6 @@ namespace dev
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".
BlockNumber jsToBlockNumber(std::string const& _js);

33
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.
// 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);
BlockLogBlooms blb;
@ -467,6 +467,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
br.receipts.push_back(s.receipt(i));
}
s.cleanup(true);
td = pd.totalDifficulty + tdIncrease;
#if ETH_TIMED_IMPORTS
@ -603,7 +604,6 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
{
newLastBlockHash = bi.hash();
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;
@ -623,10 +623,31 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
m_blocksDB->Write(m_writeOptions, &blocksBatch);
m_extrasDB->Write(m_writeOptions, &extrasBatch);
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);
}
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
@ -646,14 +667,6 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
if (!route.empty())
noteCanonChanged();
if (isKnown(bi.hash()) && !details(bi.hash()))
{
clog(BlockChainDebug) << "Known block just inserted has no details.";
clog(BlockChainDebug) << "Block:" << bi;
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1);
}
h256s fresh;
h256s dead;
bool isOld = true;

1
libethereum/ClientBase.h

@ -81,6 +81,7 @@ public:
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override;
using Interface::submitTransaction;
/// Makes the given call. Nothing is recorded into the state.
virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override;

4
libethereum/Interface.h

@ -72,6 +72,10 @@ public:
/// @returns the new contract's address (assuming it all goes through).
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0;
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
Address submitTransaction(Secret const& _secret, TransactionSkeleton const& _t) { if (_t.creation) return submitTransaction(_secret, _t.value, _t.data, _t.gas, _t.gasPrice); submitTransaction(_secret, _t.value, _t.to, _t.data, _t.gas, _t.gasPrice); return Address(); }
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() = 0;

14
libethereum/KeyManager.cpp

@ -28,6 +28,7 @@
#include <libdevcore/RLP.h>
using namespace std;
using namespace dev;
using namespace eth;
namespace fs = boost::filesystem;
KeyManager::KeyManager(std::string const& _keysFile):
@ -151,9 +152,18 @@ void KeyManager::kill(Address const& _a)
m_store.kill(id);
}
std::map<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)
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));

17
libethereum/KeyManager.h

@ -28,7 +28,8 @@
namespace dev
{
namespace eth
{
class UnknownPassword: public Exception {};
struct KeyInfo
@ -65,7 +66,10 @@ public:
bool load(std::string const& _pass);
void save(std::string const& _pass) const { write(_pass, m_keysFile); }
std::map<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;
Address address(h128 const& _uuid) const;
@ -92,12 +96,12 @@ private:
void write(h128 const& _key, std::string const& _keysFile) const;
// Ethereum keys.
std::map<Address, h128> m_addrLookup;
std::map<h128, KeyInfo> m_keyInfo;
std::map<h256, std::string> m_passwordInfo;
std::unordered_map<Address, h128> m_addrLookup;
std::unordered_map<h128, KeyInfo> m_keyInfo;
std::unordered_map<h256, std::string> m_passwordInfo;
// 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.
std::string m_password;
@ -108,3 +112,4 @@ private:
};
}
}

1
libethereum/State.cpp

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

2
libethereum/State.h

@ -49,7 +49,7 @@ class BlockChain;
class State;
struct StateChat: public LogChannel { static const char* name(); static const int verbosity = 4; };
struct StateTrace: public LogChannel { static const char* name(); static const int verbosity = 7; };
struct StateTrace: public LogChannel { static const char* name(); static const int verbosity = 5; };
struct StateDetail: public LogChannel { static const char* name(); static const int verbosity = 14; };
struct StateSafeExceptions: public LogChannel { static const char* name(); static const int verbosity = 21; };

10
libp2p/Host.cpp

@ -389,6 +389,16 @@ string Host::pocHost()
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)
{
// 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.
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.
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; }

62
libp2p/NodeTable.cpp

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

4
libp2p/NodeTable.h

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

5
libtestutils/FixedWebThreeServer.cpp

@ -16,7 +16,12 @@
*/
/** @file FixedWebThreeStubServer.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#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 <libweb3jsonrpc/WebThreeStubServerBase.h>
#include <libweb3jsonrpc/AccountHolder.h>
/**
* @brief dummy JSON-RPC api implementation
@ -33,7 +34,10 @@
class FixedWebThreeServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
public:
FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector<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:
dev::eth::Interface* client() override { return m_client; }

51
libweb3jsonrpc/AccountHolder.cpp

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

85
libweb3jsonrpc/AccountHolder.h

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

2
libweb3jsonrpc/WebThreeStubServer.h

@ -41,7 +41,7 @@ class WebThreeDirect;
class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
public:
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<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();

43
libweb3jsonrpc/WebThreeStubServerBase.cpp

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

10
libweb3jsonrpc/WebThreeStubServerBase.h

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

5
mix/ClientModel.cpp

@ -85,7 +85,8 @@ ClientModel::ClientModel():
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector<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);
}
@ -280,7 +281,7 @@ void ClientModel::setupState(QVariantMap _state)
transactionSequence.push_back(transactionSettings);
}
}
m_web3Server->setAccounts(userAccounts);
m_ethAccounts->setAccounts(userAccounts);
executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString()));
}

3
mix/ClientModel.h

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

5
mix/Web3Server.cpp

@ -24,6 +24,7 @@
#include <libdevcore/Log.h>
#include <libethereum/Interface.h>
#include <libwebthree/WebThree.h>
#include <libweb3jsonrpc/AccountHolder.h>
#include "Web3Server.h"
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):
WebThreeStubServerBase(_conn, _accounts),
Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts),
m_client(_client),
m_network(new EmptyNetwork())
{

3
mix/Web3Server.h

@ -25,6 +25,7 @@
#include <map>
#include <string>
#include <QObject>
#include <libweb3jsonrpc/AccountHolder.h>
#include <libweb3jsonrpc/WebThreeStubServerBase.h>
namespace dev
@ -38,7 +39,7 @@ class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev
Q_OBJECT
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();
signals:

28
test/libweb3jsonrpc/AccountHolder.cpp

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

Loading…
Cancel
Save