Browse Source

First set of commits. Not yet working.

cl-refactor
Gav Wood 11 years ago
parent
commit
114354126e
  1. 6
      alethzero/Main.ui
  2. 418
      alethzero/MainWin.cpp
  3. 54
      alethzero/MainWin.h
  4. 7
      eth/EthStubServer.cpp
  5. 10
      eth/eth.js
  6. 1
      eth/main.cpp
  7. 2
      libethential/FixedHash.h
  8. 10
      libethential/RLP.h
  9. 38
      libethereum/BlockChain.cpp
  10. 31
      libethereum/BlockChain.h
  11. 286
      libethereum/Client.cpp
  12. 122
      libethereum/Client.h
  13. 12
      libethereum/PeerServer.cpp
  14. 2
      libethereum/PeerServer.h
  15. 6
      libethereum/PeerSession.cpp
  16. 19
      libethereum/State.cpp
  17. 10
      libethereum/State.h
  18. 227
      libqethereum/QEthereum.cpp
  19. 372
      libqethereum/QEthereum.h
  20. 188
      libqethereum/QmlEthereum.cpp
  21. 283
      libqethereum/QmlEthereum.h
  22. 1
      neth/main.cpp
  23. 6
      walleth/MainWin.cpp
  24. 5
      walleth/MainWin.h

6
alethzero/Main.ui

@ -485,6 +485,7 @@
<addaction name="connect"/>
<addaction name="preview"/>
<addaction name="mine"/>
<addaction name="refresh"/>
</widget>
<widget class="QDockWidget" name="dockWidget_5">
<property name="sizePolicy">
@ -1637,6 +1638,11 @@ font-size: 14pt</string>
<string>&amp;Pretty...</string>
</property>
</action>
<action name="refresh">
<property name="text">
<string>&amp;Refresh</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

418
alethzero/MainWin.cpp

@ -90,6 +90,32 @@ static void initUnits(QComboBox* _b)
_b->addItem(QString::fromStdString(units()[n].second), n);
}
static QString fromRaw(eth::h256 _n, unsigned* _inc = nullptr)
{
if (_n)
{
std::string s((char const*)_n.data(), 32);
auto l = s.find_first_of('\0');
if (!l)
return QString();
if (l != string::npos)
{
auto p = s.find_first_not_of('\0', l);
if (!(p == string::npos || (_inc && p == 31)))
return QString();
if (_inc)
*_inc = (byte)s[31];
s.resize(l);
}
for (auto i: s)
if (i < 32)
return QString();
return QString::fromStdString(s);
}
return QString();
}
Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f");
Main::Main(QWidget *parent) :
@ -145,24 +171,22 @@ Main::Main(QWidget *parent) :
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
m_client.reset(new Client("AlethZero"));
m_client->start();
connect(ui->webView, &QWebView::loadStarted, [this]()
{
QEthereum *eth = new QEthereum(this, this->m_client.get(), this->owned());
this->m_ethereum = eth;
connect(this, SIGNAL(changed()), this->m_ethereum, SIGNAL(changed()));
// NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it.
m_ethereum = new QEthereum(this, m_client.get(), owned());
QWebFrame* f = this->ui->webView->page()->mainFrame();
QWebFrame* f = ui->webView->page()->mainFrame();
f->disconnect(SIGNAL(javaScriptWindowObjectCleared()));
eth->setup(f);
f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership);
connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE);
m_ethereum->setup(f);
auto qeth = m_ethereum;
connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, qeth, this));
});
connect(ui->webView, &QWebView::loadFinished, [=]()
{
this->changed();
m_ethereum->poll();
});
connect(ui->webView, &QWebView::titleChanged, [=]()
@ -171,11 +195,10 @@ Main::Main(QWidget *parent) :
});
readSettings();
refresh();
m_refresh = new QTimer(this);
connect(m_refresh, SIGNAL(timeout()), SLOT(refresh()));
m_refresh->start(100);
installWatches();
startTimer(100);
{
QSettings s("ethereum", "alethzero");
@ -185,7 +208,6 @@ Main::Main(QWidget *parent) :
s.setValue("splashMessage", false);
}
}
m_pcWarp.clear();
}
Main::~Main()
@ -194,6 +216,106 @@ Main::~Main()
writeSettings();
}
void Main::onKeysChanged()
{
installBalancesWatch();
}
unsigned Main::installWatch(eth::TransactionFilter const& _tf, std::function<void()> const& _f)
{
auto ret = m_client->installWatch(_tf);
m_handlers[ret] = _f;
return ret;
}
unsigned Main::installWatch(eth::h256 _tf, std::function<void()> const& _f)
{
auto ret = m_client->installWatch(_tf);
m_handlers[ret] = _f;
return ret;
}
void Main::installWatches()
{
installWatch(eth::TransactionFilter().altered(c_config, 0), [=](){ installNameRegWatch(); });
installWatch(eth::TransactionFilter().altered(c_config, 1), [=](){ installCurrenciesWatch(); });
installWatch(eth::NewPendingFilter, [=](){ onNewPending(); });
installWatch(eth::NewBlockFilter, [=](){ onNewBlock(); });
}
void Main::installNameRegWatch()
{
m_client->uninstallWatch(m_nameRegFilter);
m_nameRegFilter = installWatch(eth::TransactionFilter().altered((u160)state().storage(c_config, 0)), [=](){ onNameRegChange(); });
}
void Main::installCurrenciesWatch()
{
m_client->uninstallWatch(m_currenciesFilter);
m_currenciesFilter = installWatch(eth::TransactionFilter().altered((u160)m_client->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); });
}
void Main::installBalancesWatch()
{
eth::TransactionFilter tf;
vector<Address> altCoins;
Address coinsAddr = right160(m_client->stateAt(c_config, 1));
for (unsigned i = 0; i < m_client->stateAt(coinsAddr, 0); ++i)
altCoins.push_back(right160(m_client->stateAt(coinsAddr, i + 1)));
for (auto i: m_myKeys)
{
tf.altered(i.address());
for (auto c: altCoins)
tf.altered(c, (u160)i.address());
}
m_client->uninstallWatch(m_balancesFilter);
m_balancesFilter = installWatch(tf, [=](){ onBalancesChange(); });
}
void Main::onNameRegChange()
{
cdebug << "NameReg changed!";
// update any namereg-dependent stuff - for now force a full update.
refreshAll();
}
void Main::onCurrenciesChange()
{
cdebug << "Currencies changed!";
installBalancesWatch();
// TODO: update any currency-dependent stuff?
}
void Main::onBalancesChange()
{
cdebug << "Our balances changed!";
refreshBalances();
}
void Main::onNewBlock()
{
cdebug << "Blockchain changed!";
// update blockchain dependent views.
refreshBlockCount();
refreshBlockChain();
refreshAccounts();
}
void Main::onNewPending()
{
cdebug << "Pending transactions changed!";
// update any pending-transaction dependent views.
refreshPending();
refreshAccounts();
}
void Main::on_clearPending_triggered()
{
m_client->clearPending();
@ -276,40 +398,15 @@ void Main::eval(QString const& _js)
ui->jsConsole->setHtml(s);
}
QString fromRaw(eth::h256 _n, unsigned* _inc = nullptr)
{
if (_n)
{
std::string s((char const*)_n.data(), 32);
auto l = s.find_first_of('\0');
if (!l)
return QString();
if (l != string::npos)
{
auto p = s.find_first_not_of('\0', l);
if (!(p == string::npos || (_inc && p == 31)))
return QString();
if (_inc)
*_inc = (byte)s[31];
s.resize(l);
}
for (auto i: s)
if (i < 32)
return QString();
return QString::fromStdString(s);
}
return QString();
}
QString Main::pretty(eth::Address _a) const
{
h256 n;
if (h160 nameReg = (u160)state().storage(c_config, 0))
n = state().storage(nameReg, (u160)(_a));
if (h160 nameReg = (u160)m_client->stateAt(c_config, 0))
n = m_client->stateAt(nameReg, (u160)(_a));
if (!n)
n = state().storage(m_nameReg, (u160)(_a));
n = m_client->stateAt(m_nameReg, (u160)(_a));
return fromRaw(n);
}
@ -470,12 +567,18 @@ void Main::on_nameReg_textChanged()
if (s.size() == 40)
{
m_nameReg = Address(fromHex(s));
refresh(true);
refreshAll();
}
else
m_nameReg = Address();
}
void Main::on_preview_triggered()
{
m_client->setDefault(ui->preview->isChecked() ? 0 : -1);
refreshAll();
}
void Main::refreshMining()
{
eth::ClientGuard g(m_client.get());
@ -495,6 +598,33 @@ void Main::refreshMining()
*/
}
void Main::refreshBalances()
{
// update all the balance-dependent stuff.
ui->ourAccounts->clear();
u256 totalBalance = 0;
map<Address, pair<QString, u256>> altCoins;
Address coinsAddr = right160(m_client->stateAt(c_config, 1));
for (unsigned i = 0; i < m_client->stateAt(coinsAddr, 0); ++i)
altCoins[right160(m_client->stateAt(coinsAddr, m_client->stateAt(coinsAddr, i + 1)))] = make_pair(fromRaw(m_client->stateAt(coinsAddr, i + 1)), 0);
for (auto i: m_myKeys)
{
u256 b = m_client->balanceAt(i.address());
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)m_client->countAt(i.address())), ui->ourAccounts))
->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size));
totalBalance += b;
for (auto& c: altCoins)
c.second.second += (u256)m_client->stateAt(c.first, (u160)i.address());
}
QString b;
for (auto const& c: altCoins)
if (c.second.second)
b += QString::fromStdString(toString(c.second.second)) + " " + c.second.first.toUpper() + " | ";
ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance)));
}
void Main::refreshNetwork()
{
auto ps = m_client->peers();
@ -510,24 +640,78 @@ eth::State const& Main::state() const
return ui->preview->isChecked() ? m_client->postState() : m_client->state();
}
void Main::updateBlockCount()
void Main::refreshAll()
{
auto d = m_client->blockChain().details();
auto diff = BlockInfo(m_client->blockChain().block()).difficulty;
ui->blockCount->setText(QString("#%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(eth::c_protocolVersion).arg(eth::c_databaseVersion));
refreshDestination();
refreshBlockChain();
refreshBlockCount();
refreshPending();
refreshAccounts();
}
void Main::on_blockChainFilter_textChanged()
void Main::refreshPending()
{
static QTimer* s_delayed = nullptr;
if (!s_delayed)
cdebug << "refreshPending()";
ui->transactionQueue->clear();
for (Transaction const& t: m_client->pending())
{
s_delayed = new QTimer(this);
s_delayed->setSingleShot(true);
connect(s_delayed, SIGNAL(timeout()), SLOT(refreshBlockChain()));
QString s = t.receiveAddress ?
QString("%2 %5> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(render(t.safeSender()))
.arg(render(t.receiveAddress))
.arg((unsigned)t.nonce)
.arg(m_client->codeAt(t.receiveAddress).size() ? '*' : '-') :
QString("%2 +> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(render(t.safeSender()))
.arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce)))))
.arg((unsigned)t.nonce);
ui->transactionQueue->addItem(s);
}
s_delayed->stop();
s_delayed->start(200);
}
void Main::refreshAccounts()
{
cdebug << "refreshAccounts()";
ui->accounts->clear();
ui->contracts->clear();
for (auto n = 0; n < 2; ++n)
for (auto i: m_client->addresses())
{
auto r = render(i);
if (r.contains('(') == !n)
{
if (n == 0 || ui->showAllAccounts->isChecked())
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(m_client->balanceAt(i)).c_str()).arg(r).arg((unsigned)m_client->countAt(i)), ui->accounts))
->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size));
if (m_client->codeAt(i).size())
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(m_client->balanceAt(i)).c_str()).arg(r).arg((unsigned)m_client->countAt(i)), ui->contracts))
->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size));
}
}
}
void Main::refreshDestination()
{
cdebug << "refreshDestination()";
QString s;
for (auto i: m_client->addresses())
if ((s = pretty(i)).size())
// A namereg address
if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1)
ui->destination->addItem(s);
for (int i = 0; i < ui->destination->count(); ++i)
if (ui->destination->itemText(i) != "(Create Contract)" && !fromString(ui->destination->itemText(i)))
ui->destination->removeItem(i--);
}
void Main::refreshBlockCount()
{
cdebug << "refreshBlockCount()";
auto d = m_client->blockChain().details();
auto diff = BlockInfo(m_client->blockChain().block()).difficulty;
ui->blockCount->setText(QString("#%1 @%3 T%2 N%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(eth::c_protocolVersion).arg(eth::c_databaseVersion));
}
static bool blockMatch(string const& _f, eth::BlockDetails const& _b, h256 _h, BlockChain const& _bc)
@ -557,6 +741,7 @@ static bool transactionMatch(string const& _f, Transaction const& _t)
void Main::refreshBlockChain()
{
cdebug << "refreshBlockChain()";
eth::ClientGuard g(m_client.get());
auto const& st = state();
@ -612,113 +797,49 @@ void Main::refreshBlockChain()
ui->blocks->setCurrentRow(0);
}
void Main::refresh(bool _override)
void Main::on_blockChainFilter_textChanged()
{
static QTimer* s_delayed = nullptr;
if (!s_delayed)
{
s_delayed = new QTimer(this);
s_delayed->setSingleShot(true);
connect(s_delayed, SIGNAL(timeout()), SLOT(refreshBlockChain()));
}
s_delayed->stop();
s_delayed->start(200);
}
void Main::on_refresh_triggered()
{
refreshAll();
}
void Main::timerEvent(QTimerEvent*)
{
// 7/18, Alex: aggregating timers, prelude to better threading?
// Runs much faster on slower dual-core processors
static int interval = 100;
// refresh mining every 200ms
if(interval / 100 % 2 == 0)
if (interval / 100 % 2 == 0)
refreshMining();
// refresh peer list every 1000ms, reset counter
if(interval == 1000)
if (interval == 1000)
{
interval = 0;
refreshNetwork();
} else
}
else
interval += 100;
eth::ClientGuard g(m_client.get());
auto const& st = state();
bool c = m_client->changed();
if (c || _override)
{
changed();
updateBlockCount();
auto acs = st.addresses();
ui->accounts->clear();
ui->contracts->clear();
for (auto n = 0; n < 2; ++n)
for (auto i: acs)
{
auto r = render(i.first);
if (r.contains('(') == !n)
{
if (n == 0 || ui->showAllAccounts->isChecked())
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(i.second).c_str()).arg(r).arg((unsigned)state().transactionsFrom(i.first)), ui->accounts))
->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size));
if (st.addressHasCode(i.first))
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(i.second).c_str()).arg(r).arg((unsigned)st.transactionsFrom(i.first)), ui->contracts))
->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size));
if (r.contains('('))
{
// A namereg address
QString s = pretty(i.first);
if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1)
ui->destination->addItem(s);
}
}
}
for (int i = 0; i < ui->destination->count(); ++i)
if (ui->destination->itemText(i) != "(Create Contract)" && !fromString(ui->destination->itemText(i)))
ui->destination->removeItem(i--);
ui->transactionQueue->clear();
for (Transaction const& t: m_client->pending())
{
QString s = t.receiveAddress ?
QString("%2 %5> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(render(t.safeSender()))
.arg(render(t.receiveAddress))
.arg((unsigned)t.nonce)
.arg(st.addressHasCode(t.receiveAddress) ? '*' : '-') :
QString("%2 +> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(render(t.safeSender()))
.arg(render(right160(sha3(rlpList(t.safeSender(), t.nonce)))))
.arg((unsigned)t.nonce);
ui->transactionQueue->addItem(s);
}
refreshBlockChain();
}
if (c || m_keysChanged || _override)
{
m_keysChanged = false;
ui->ourAccounts->clear();
u256 totalBalance = 0;
map<Address, pair<QString, u256>> altCoins;
Address coinsAddr = right160(st.storage(c_config, 1));
for (unsigned i = 0; i < st.storage(coinsAddr, 0); ++i)
altCoins[right160(st.storage(coinsAddr, st.storage(coinsAddr, i + 1)))] = make_pair(fromRaw(st.storage(coinsAddr, i + 1)), 0);
// u256 totalGavCoinBalance = 0;
for (auto i: m_myKeys)
{
u256 b = st.balance(i.address());
(new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)st.transactionsFrom(i.address())), ui->ourAccounts))
->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size));
totalBalance += b;
for (auto& c: altCoins)
c.second.second += (u256)st.storage(c.first, (u160)i.address());
}
if (m_ethereum)
m_ethereum->poll();
QString b;
for (auto const& c: altCoins)
if (c.second.second)
b += QString::fromStdString(toString(c.second.second)) + " " + c.second.first.toUpper() + " | ";
ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance)));
}
for (auto const& i: m_handlers)
if (m_client->checkWatch(i.first))
i.second();
}
string Main::renderDiff(eth::StateDiff const& _d) const
@ -846,7 +967,6 @@ void Main::on_inject_triggered()
QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex");
bytes b = fromHex(s.toStdString());
m_client->inject(&b);
refresh();
}
void Main::on_blocks_currentItemChanged()
@ -1203,7 +1323,6 @@ void Main::on_killBlockchain_triggered()
ui->net->setChecked(false);
m_client.reset();
m_client.reset(new Client("AlethZero", Address(), string(), true));
m_client->start();
readSettings();
}
@ -1321,7 +1440,6 @@ void Main::on_send_clicked()
m_client->transact(s, value(), m_data, ui->gas->value(), gasPrice());
else
m_client->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice());
refresh();
return;
}
statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount.");

54
alethzero/MainWin.h

@ -39,6 +39,7 @@ class Main;
namespace eth {
class Client;
class State;
class TransactionFilter;
}
class QQuickView;
@ -78,6 +79,8 @@ public slots:
void debug(QString _entry);
void warn(QString _entry);
void onKeysChanged();
private slots:
void eval(QString const& _js);
@ -106,7 +109,7 @@ private slots:
void on_about_triggered();
void on_paranoia_triggered();
void on_nameReg_textChanged();
void on_preview_triggered() { refresh(true); }
void on_preview_triggered();
void on_quit_triggered() { close(); }
void on_urlEdit_returnPressed();
void on_debugStep_triggered();
@ -118,8 +121,8 @@ private slots:
void on_importKey_triggered();
void on_exportKey_triggered();
void on_inject_triggered();
void on_showAll_triggered() { refresh(true); }
void on_showAllAccounts_triggered() { refresh(true); }
void on_showAll_triggered() { refreshBlockChain(); }
void on_showAllAccounts_triggered() { refreshAccounts(); }
void on_loadJS_triggered();
void on_blockChainFilter_textChanged();
void on_clearPending_triggered();
@ -134,18 +137,12 @@ private slots:
void on_debugCurrent_triggered();
void on_debugDumpState_triggered(int _add = 1);
void on_debugDumpStatePre_triggered();
void refresh(bool _override = false);
void refreshNetwork();
void refreshMining();
void refreshBlockChain();
void on_refresh_triggered();
signals:
void changed(); // TODO: manifest
void poll();
private:
void updateBlockCount();
QString pretty(eth::Address _a) const;
QString prettyU256(eth::u256 _n) const;
@ -171,13 +168,44 @@ private:
eth::u256 value() const;
eth::u256 gasPrice() const;
unsigned installWatch(eth::TransactionFilter const& _tf, std::function<void()> const& _f);
unsigned installWatch(eth::h256 _tf, std::function<void()> const& _f);
void onNewPending();
void onNewBlock();
void onNameRegChange();
void onCurrenciesChange();
void onBalancesChange();
void installWatches();
void installCurrenciesWatch();
void installNameRegWatch();
void installBalancesWatch();
virtual void timerEvent(QTimerEvent*);
void refreshNetwork();
void refreshMining();
void refreshAll();
void refreshPending();
void refreshAccounts();
void refreshDestination();
void refreshBlockChain();
void refreshBlockCount();
void refreshBalances();
std::unique_ptr<Ui::Main> ui;
std::unique_ptr<eth::Client> m_client;
std::map<unsigned, std::function<void()>> m_handlers;
unsigned m_nameRegFilter = (unsigned)-1;
unsigned m_currenciesFilter = (unsigned)-1;
unsigned m_balancesFilter = (unsigned)-1;
QByteArray m_peers;
QMutex m_guiLock;
QTimer* m_refresh;
QTimer* m_ticker;
QTimer* m_refreshNetwork;
QTimer* m_refreshMining;
QStringList m_servers;
@ -202,5 +230,5 @@ private:
QList<QPair<QString, QString>> m_consoleHistory;
QEthereum* m_ethereum;
QEthereum* m_ethereum = nullptr;
};

7
eth/EthStubServer.cpp

@ -72,14 +72,15 @@ std::string EthStubServer::balanceAt(std::string const& _a)
Json::Value EthStubServer::check(Json::Value const& _as)
{
if (m_client.changed())
// TODO
// if (m_client.changed())
return _as;
else
/* else
{
Json::Value ret;
ret.resize(0);
return ret;
}
}*/
}
std::string EthStubServer::create(const std::string& _bCode, const std::string& _sec, const std::string& _xEndowment, const std::string& _xGas, const std::string& _xGasPrice)

10
eth/eth.js

@ -75,19 +75,19 @@ window.eth = (function ethScope() {
p[s.order[j]] = (s.order[j][0] === "b") ? a[j].unbin() : a[j]
return p
};
if (m == "create" || m == "transact")
ret[m] = function() { return reqAsync(m, getParams(arguments), arguments[s.order.length]) }
if (m == "create" || m == "transact")
ret[m] = function() { return reqAsync(m, getParams(arguments), arguments[s.order.length]) }
else
{
ret[am] = function() { return reqAsync(m, getParams(arguments), arguments[s.order.length]) }
if (s.params)
ret[m] = function() { return reqSync(m, getParams(arguments)) }
else
else
Object.defineProperty(ret, m, {
get: function() { return reqSync(m, {}); },
set: function(v) {}
})
}
})
}
})(spec[si]);
ret.check = function(force) {

1
eth/main.cpp

@ -292,7 +292,6 @@ int main(int argc, char** argv)
if (!clientName.empty())
clientName += "/";
Client c("Ethereum(++)/" + clientName + "v" + eth::EthVersion + "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath);
c.start();
cout << credits();
cout << "Address: " << endl << toHex(us.address().asArray()) << endl;

2
libethential/FixedHash.h

@ -163,7 +163,7 @@ private:
/// Fast equality operator for h256.
template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const
{
const uint64_t* hash1 = (const uint64_t*)this->data();
const uint64_t* hash1 = (const uint64_t*)data();
const uint64_t* hash2 = (const uint64_t*)_other.data();
return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]);
}

10
libethential/RLP.h

@ -164,6 +164,10 @@ public:
explicit operator u256() const { return toInt<u256>(); }
explicit operator bigint() const { return toInt<bigint>(); }
template <unsigned _N> explicit operator FixedHash<_N>() const { return toHash<FixedHash<_N>>(); }
template <class T, class U> explicit operator std::pair<T, U>() const { return toPair<T, U>(); }
template <class T> explicit operator std::vector<T>() const { return toVector<T>(); }
template <class T> explicit operator std::set<T>() const { return toSet<T>(); }
template <class T, size_t N> explicit operator std::array<T, N>() const { return toArray<T, N>(); }
/// Converts to bytearray. @returns the empty byte array if not a string.
bytes toBytes() const { if (!isData()) return bytes(); return bytes(payload().data(), payload().data() + length()); }
@ -175,6 +179,8 @@ public:
std::string toStringStrict() const { if (!isData()) throw BadCast(); return payload().cropped(0, length()).toString(); }
template <class T> std::vector<T> toVector() const { std::vector<T> ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; }
template <class T> std::set<T> toSet() const { std::set<T> ret; if (isList()) { for (auto const& i: *this) ret.insert((T)i); } return ret; }
template <class T, class U> std::pair<T, U> toPair() const { std::pair<T, U> ret; if (isList()) { ret.first = (T)((*this)[0]); ret.second = (U)((*this)[1]); } return ret; }
template <class T, size_t N> std::array<T, N> toArray() const { if (itemCount() != N || !isList()) throw BadCast(); std::array<T, N> ret; for (uint i = 0; i < N; ++i) ret[i] = (T)operator[](i); return ret; }
/// Int conversion flags
@ -285,8 +291,10 @@ public:
/// Appends a sequence of data to the stream as a list.
template <class _T> RLPStream& append(std::vector<_T> const& _s) { return appendVector(_s); }
template <class _T, size_t S> RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T> RLPStream& appendVector(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T, size_t S> RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T> RLPStream& append(std::set<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class T, class U> RLPStream& append(std::pair<T, U> const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; }
/// Appends a list.
RLPStream& appendList(uint _items);

38
libethereum/BlockChain.cpp

@ -196,7 +196,7 @@ inline ldb::Slice toSlice(h256 _h, unsigned _sub = 0)
#endif
}
void BlockChain::import(bytes const& _block, OverlayDB const& _db)
h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
{
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi;
@ -295,9 +295,11 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db)
// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children.";
h256s ret;
// This might be the new best block...
if (td > details(m_lastBlockHash).totalDifficulty)
{
ret = treeRoute(m_lastBlockHash, newHash);
m_lastBlockHash = newHash;
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32));
clog(BlockChainNote) << " Imported and best. Has" << (details(bi.parentHash).children.size() - 1) << "siblings.";
@ -306,6 +308,40 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db)
{
clog(BlockChainNote) << " Imported but not best (oTD:" << details(m_lastBlockHash).totalDifficulty << ", TD:" << td << ")";
}
return ret;
}
h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common) const
{
h256s ret;
h256s back;
unsigned fn = details(_from).number;
unsigned tn = details(_to).number;
while (fn > tn)
{
ret.push_back(_from);
_from = details(_from).parent;
fn--;
}
while (fn < tn)
{
back.push_back(_to);
_to = details(_to).parent;
tn--;
}
while (_from != _to)
{
_from = details(_from).parent;
_to = details(_to).parent;
ret.push_back(_from);
back.push_back(_to);
}
if (o_common)
*o_common = _from;
ret.reserve(ret.size() + back.size());
for (auto it = back.cbegin(); it != back.cend(); ++it)
ret.push_back(*it);
return ret;
}
void BlockChain::checkConsistency()

31
libethereum/BlockChain.h

@ -114,7 +114,8 @@ public:
bool attemptImport(bytes const& _block, OverlayDB const& _stateDB);
/// Import block into disk-backed DB
void import(bytes const& _block, OverlayDB const& _stateDB);
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
h256s import(bytes const& _block, OverlayDB const& _stateDB);
/// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
BlockDetails details(h256 _hash) const;
@ -144,13 +145,29 @@ public:
/// Get the hash of a block of a given number.
h256 numberHash(unsigned _n) const;
std::vector<std::pair<Address, AddressState>> interestQueue() { std::vector<std::pair<Address, AddressState>> ret; swap(ret, m_interestQueue); return ret; }
void pushInterest(Address _a) { m_interest[_a]++; }
void popInterest(Address _a) { if (m_interest[_a] > 1) m_interest[_a]--; else if (m_interest[_a]) m_interest.erase(_a); }
/// @returns the genesis block header.
static BlockInfo const& genesis() { if (!s_genesis) { auto gb = createGenesisBlock(); (s_genesis = new BlockInfo)->populate(&gb); } return *s_genesis; }
/// @returns the genesis block as its RLP-encoded byte array.
/// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static bytes createGenesisBlock();
/** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of
* blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent.
*
* If non-null, the h256 at @a o_common is set to the latest common ancestor of both blocks.
*
* e.g. if the block tree is 3a -> 2a -> 1a -> g and 2b -> 1b -> g (g is genesis, *a, *b are competing chains),
* then:
* @code
* treeRoute(3a, 2b) == { 3a, 2a, 1a, 1b, 2b }; // *o_common == g
* treeRoute(2a, 1a) == { 2a, 1a }; // *o_common == 1a
* treeRoute(1a, 2a) == { 1a, 2a }; // *o_common == 1a
* treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g
* @endcode
*/
h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr) const;
private:
void checkConsistency();
@ -162,10 +179,6 @@ private:
mutable std::map<h256, bytes> m_cache;
mutable std::recursive_mutex m_lock;
/// The queue of transactions that have happened that we're interested in.
std::map<Address, int> m_interest;
std::vector<std::pair<Address, AddressState>> m_interestQueue;
ldb::DB* m_db;
ldb::DB* m_extrasDB;

286
libethereum/Client.cpp

@ -30,6 +30,18 @@
using namespace std;
using namespace eth;
void TransactionFilter::fillStream(RLPStream& _s) const
{
_s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip;
}
h256 TransactionFilter::sha3() const
{
RLPStream s;
fillStream(s);
return eth::sha3(s.out());
}
VersionChecker::VersionChecker(string const& _dbPath):
m_path(_dbPath.size() ? _dbPath : Defaults::dbPath())
{
@ -62,23 +74,25 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
m_changed = true;
}
void Client::start() {
void Client::ensureWorking()
{
static const char* c_threadName = "eth";
m_work.reset(new thread([&](){
setThreadName(c_threadName);
while (m_workState.load(std::memory_order_acquire) != Deleting)
work();
m_workState.store(Deleted, std::memory_order_release);
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
m_preMine.sync(m_bc);
m_postMine = m_preMine;
}));
if (!m_work)
m_work.reset(new thread([&]()
{
setThreadName(c_threadName);
while (m_workState.load(std::memory_order_acquire) != Deleting)
work();
m_workState.store(Deleted, std::memory_order_release);
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
m_preMine.sync(m_bc);
m_postMine = m_preMine;
}));
}
Client::~Client()
@ -90,8 +104,89 @@ Client::~Client()
m_work->join();
}
void Client::flushTransactions()
{
work(true);
}
void Client::clearPending()
{
ClientGuard l(this);
if (!m_postMine.pending().size())
return;
h256Set changeds;
for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
appendFromNewPending(m_postMine.bloom(i), changeds);
changeds.insert(NewPendingFilter);
m_postMine = m_preMine;
noteChanged(changeds);
}
unsigned Client::installWatch(h256 _h)
{
auto ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
m_watches[ret] = Watch(_h);
return ret;
}
unsigned Client::installWatch(TransactionFilter const& _f)
{
lock_guard<mutex> l(m_filterLock);
h256 h = _f.sha3();
if (!m_filters.count(h))
m_filters.insert(make_pair(h, _f));
return installWatch(h);
}
void Client::uninstallWatch(unsigned _i)
{
lock_guard<mutex> l(m_filterLock);
auto it = m_watches.find(_i);
if (it == m_watches.end())
return;
auto fit = m_filters.find(it->second.id);
if (fit != m_filters.end())
if (!--fit->second.refCount)
m_filters.erase(fit);
m_watches.erase(it);
}
void Client::appendFromNewPending(h256 _bloom, h256Set& o_changed) const
{
lock_guard<mutex> l(m_filterLock);
for (pair<h256, InstalledFilter> const& i: m_filters)
if (numberOf(i.second.filter.earliest()) == m_postMine.info().number && i.second.filter.matches(_bloom))
o_changed.insert(i.first);
}
void Client::appendFromNewBlock(h256 _block, h256Set& o_changed) const
{
auto d = m_bc.details(_block);
lock_guard<mutex> l(m_filterLock);
for (pair<h256, InstalledFilter> const& i: m_filters)
if (numberOf(i.second.filter.earliest()) >= d.number && i.second.filter.matches(d.bloom))
o_changed.insert(i.first);
}
void Client::noteChanged(h256Set const& _filters)
{
lock_guard<mutex> l(m_filterLock);
for (auto& i: m_watches)
if (_filters.count(i.second.id))
i.second.changes++;
}
void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHost, unsigned short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp)
{
ensureWorking();
ClientGuard l(this);
if (m_net.get())
return;
@ -139,6 +234,8 @@ void Client::stopNetwork()
void Client::startMining()
{
ensureWorking();
m_doMine = true;
m_restartMining = true;
}
@ -184,23 +281,28 @@ void Client::inject(bytesConstRef _rlp)
{
ClientGuard l(this);
m_tq.attemptImport(_rlp);
m_changed = true;
}
void Client::work()
void Client::work(bool _justQueue)
{
bool changed = false;
h256Set changeds;
// Process network events.
// Synchronise block chain with network.
// Will broadcast any of our (new) transactions and blocks, and collect & add any of their (new) transactions and blocks.
if (m_net)
if (m_net && !_justQueue)
{
ClientGuard l(this);
m_net->process(); // must be in guard for now since it uses the blockchain. TODO: make BlockChain thread-safe.
if (m_net->sync(m_bc, m_tq, m_stateDB))
changed = true;
// TODO: return h256Set as block hashes, once for each block that has come in/gone out.
h256Set newBlocks = m_net->sync(m_bc, m_tq, m_stateDB);
if (newBlocks.size())
{
for (auto i: newBlocks)
appendFromNewBlock(i, changeds);
changeds.insert(NewBlockFilter);
}
}
// Synchronise state to block chain.
@ -216,74 +318,82 @@ void Client::work()
{
if (m_doMine)
cnote << "New block on chain: Restarting mining operation.";
changed = true;
m_restartMining = true; // need to re-commit to mine.
m_postMine = m_preMine;
}
if (m_postMine.sync(m_tq, &changed))
// returns h256s as blooms, once for each transaction.
h256s newPendingBlooms = m_postMine.sync(m_tq);
if (newPendingBlooms.size())
{
for (auto i: newPendingBlooms)
appendFromNewPending(i, changeds);
changeds.insert(NewPendingFilter);
if (m_doMine)
cnote << "Additional transaction ready: Restarting mining operation.";
m_restartMining = true;
}
}
if (m_doMine)
noteChanged(changeds);
if (!_justQueue)
{
if (m_restartMining)
if (m_doMine)
{
m_mineProgress.best = (double)-1;
m_mineProgress.hashes = 0;
m_mineProgress.ms = 0;
ClientGuard l(this);
if (m_paranoia)
if (m_restartMining)
{
if (m_postMine.amIJustParanoid(m_bc))
m_mineProgress.best = (double)-1;
m_mineProgress.hashes = 0;
m_mineProgress.ms = 0;
ClientGuard l(this);
if (m_paranoia)
{
cnote << "I'm just paranoid. Block is fine.";
m_postMine.commitToMine(m_bc);
if (m_postMine.amIJustParanoid(m_bc))
{
cnote << "I'm just paranoid. Block is fine.";
m_postMine.commitToMine(m_bc);
}
else
{
cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report.";
m_doMine = false;
}
}
else
{
cwarn << "I'm not just paranoid. Cannot mine. Please file a bug report.";
m_doMine = false;
}
m_postMine.commitToMine(m_bc);
}
else
m_postMine.commitToMine(m_bc);
}
}
if (m_doMine)
{
m_restartMining = false;
if (m_doMine)
{
m_restartMining = false;
// Mine for a while.
MineInfo mineInfo = m_postMine.mine(100);
// Mine for a while.
MineInfo mineInfo = m_postMine.mine(100);
m_mineProgress.best = min(m_mineProgress.best, mineInfo.best);
m_mineProgress.current = mineInfo.best;
m_mineProgress.requirement = mineInfo.requirement;
m_mineProgress.ms += 100;
m_mineProgress.hashes += mineInfo.hashes;
{
ClientGuard l(this);
m_mineHistory.push_back(mineInfo);
}
m_mineProgress.best = min(m_mineProgress.best, mineInfo.best);
m_mineProgress.current = mineInfo.best;
m_mineProgress.requirement = mineInfo.requirement;
m_mineProgress.ms += 100;
m_mineProgress.hashes += mineInfo.hashes;
{
ClientGuard l(this);
m_mineHistory.push_back(mineInfo);
}
if (mineInfo.completed)
{
// Import block.
ClientGuard l(this);
m_postMine.completeMine();
m_bc.attemptImport(m_postMine.blockData(), m_stateDB);
m_changed = true;
if (mineInfo.completed)
{
// Import block.
ClientGuard l(this);
m_postMine.completeMine();
m_bc.attemptImport(m_postMine.blockData(), m_stateDB);
}
}
else
this_thread::sleep_for(chrono::milliseconds(100));
}
else
this_thread::sleep_for(chrono::milliseconds(100));
m_changed = m_changed || changed;
}
void Client::lock() const
@ -316,6 +426,15 @@ State Client::asOf(int _h) const
return State(m_stateDB, m_bc, m_bc.numberHash(numberOf(_h)));
}
std::vector<Address> Client::addresses(int _block) const
{
ClientGuard l(this);
vector<Address> ret;
for (auto const& i: asOf(_block).addresses())
ret.push_back(i.first);
return ret;
}
u256 Client::balanceAt(Address _a, int _block) const
{
ClientGuard l(this);
@ -480,28 +599,24 @@ PastMessages Client::transactions(TransactionFilter const& _f) const
ret.insert(ret.begin(), pm[j].polish(h256(), ts, 0));
}
}
/* for (unsigned i = m_postMine.pending().size(); i-- && ret.size() != m;)
if (_f.matches(m_postMine, i))
{
if (s)
s--;
else
ret.insert(ret.begin(), PastMessage(m_postMine.pending()[i], h256(), i, time(0), 0));
}*/
// Early exit here since we can't rely on begin/end, being out of the blockchain as we are.
if (_f.earliest() == 0)
return ret;
}
#if ETH_DEBUG
unsigned skipped = 0;
unsigned falsePos = 0;
#endif
auto cn = m_bc.number();
auto h = m_bc.numberHash(begin);
unsigned n = begin;
for (; ret.size() != m && n != end; n--, h = m_bc.details(h).parent)
{
auto d = m_bc.details(h);
#if ETH_DEBUG
int total = 0;
#endif
if (_f.matches(d.bloom))
{
// Might have a block that contains a transaction that contains a matching message.
@ -517,7 +632,9 @@ PastMessages Client::transactions(TransactionFilter const& _f) const
PastMessages pm = _f.matches(changes, i);
if (pm.size())
{
#if ETH_DEBUG
total += pm.size();
#endif
auto ts = BlockInfo(m_bc.block(h)).timestamp;
for (unsigned j = 0; j < pm.size() && ret.size() != m; ++j)
if (s)
@ -527,35 +644,20 @@ PastMessages Client::transactions(TransactionFilter const& _f) const
ret.insert(ret.begin(), pm[j].polish(h, ts, cn - n + 2));
}
}
#if ETH_DEBUG
if (!total)
falsePos++;
/* try
{
State st(m_stateDB, m_bc, h);
unsigned os = s;
for (unsigned i = st.pending().size(); i-- && ret.size() != m;)
if (_f.matches(st, i))
{
if (s)
s--;
else
ret.insert(ret.begin(), PastMessage(st.pending()[i], h, i, BlockInfo(m_bc.block(h)).timestamp, cn - n + 2));
}
if (os - s == st.pending().size())
falsePos++;
}
catch (...)
{
// Gaa. bad state. not good at all. bury head in sand for now.
}
*/
}
else
skipped++;
#else
}
#endif
if (n == end)
break;
}
#if ETH_DEBUG
// cdebug << (begin - n) << "searched; " << skipped << "skipped; " << falsePos << "false +ves";
#endif
return ret;
}

122
libethereum/Client.h

@ -82,7 +82,7 @@ struct PastMessage
{
PastMessage(Manifest const& _m, std::vector<unsigned> _path, Address _o): to(_m.to), from(_m.from), value(_m.value), input(_m.input), output(_m.output), path(_path), origin(_o) {}
PastMessage& polish(h256 _b, u256 _ts, int _a) { block = _b; timestamp = _ts; age = _a; return *this; }
PastMessage& polish(h256 _b, u256 _ts, unsigned _n) { block = _b; timestamp = _ts; number = _n; return *this; }
Address to; ///< The receiving address of the transaction. Address() in the case of a creation.
Address from; ///< The receiving address of the transaction. Address() in the case of a creation.
@ -93,8 +93,8 @@ struct PastMessage
std::vector<unsigned> path; ///< Call path into the block transaction. size() is always > 0. First item is the transaction index in the block.
Address origin; ///< Originating sender of the transaction.
h256 block; ///< Block hash.
u256 timestamp; ///< Block timestamp.
int age; ///< Transaction age.
u256 timestamp; ///< Block timestamp.
unsigned number; ///< Block number.
};
typedef std::vector<PastMessage> PastMessages;
@ -104,6 +104,9 @@ class TransactionFilter
public:
TransactionFilter(int _earliest = GenesisBlock, int _latest = 0, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {}
void fillStream(RLPStream& _s) const;
h256 sha3() const;
int earliest() const { return m_earliest; }
int latest() const { return m_latest; }
unsigned max() const { return m_max; }
@ -134,6 +137,26 @@ private:
unsigned m_skip;
};
struct InstalledFilter
{
InstalledFilter(TransactionFilter const& _f): filter(_f) {}
TransactionFilter filter;
unsigned refCount = 1;
};
static const h256 NewPendingFilter = u256(0);
static const h256 NewBlockFilter = u256(1);
struct Watch
{
Watch() {}
explicit Watch(h256 _id): id(_id) {}
h256 id;
unsigned changes = 1;
};
/**
* @brief Main API hub for interfacing with Ethereum.
*/
@ -143,9 +166,6 @@ public:
/// Constructor.
explicit Client(std::string const& _clientVersion, Address _us = Address(), std::string const& _dbPath = std::string(), bool _forceClean = false);
// Start client. Boost require threads are started outside constructor.
void start();
/// Destructor.
~Client();
@ -156,36 +176,17 @@ public:
/// @returns the new contract's address (assuming it all goes through).
Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo);
/// Blocks until all pending transactions have been processed.
void flushTransactions();
/// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly.
void inject(bytesConstRef _rlp);
/// Makes the given call. Nothing is recorded into the state. TODO
// bytes call(Secret _secret, u256 _amount, u256 _gasPrice, Address _dest, u256 _gas, bytes _data = bytes());
/// Requires transactions involving this address be queued for inspection.
void setInterest(Address _dest);
/// @returns incoming minable transactions that we wanted to be notified of. Clears the queue.
Transactions pendingQueue() { ClientGuard g(this); return m_tq.interestQueue(); }
/// @returns alterations in state of a mined block that we wanted to be notified of. Clears the queue.
std::vector<std::pair<Address, AddressState>> minedQueue() { ClientGuard g(this); return m_bc.interestQueue(); }
// Not yet - probably best as using some sort of signals implementation.
/// Calls @a _f when a valid transaction is received that involves @a _dest and once per such transaction.
// void onPending(Address _dest, function<void(Transaction)> const& _f);
/// Calls @a _f when a transaction is mined that involves @a _dest and once per change.
// void onConfirmed(Address _dest, function<void(Transaction, AddressState)> const& _f);
// Informational stuff
/// Determines whether at least one of the state/blockChain/transactionQueue has changed since the last call to changed().
bool changed() const { auto ret = m_changed; m_changed = false; return ret; }
bool peekChanged() const { return m_changed; }
/// Get a map containing each of the pending transactions.
Transactions pending() const { return m_postMine.pending(); }
// [OLD API]:
/// Locks/unlocks the state/blockChain/transactionQueue for access.
@ -201,11 +202,34 @@ public:
// [NEW API]
u256 balanceAt(Address _a, int _block = -1) const;
u256 countAt(Address _a, int _block = -1) const;
u256 stateAt(Address _a, u256 _l, int _block = -1) const;
bytes codeAt(Address _a, int _block = -1) const;
PastMessages transactions(TransactionFilter const& _f) const;
void setDefault(int _block) { m_default = _block; }
u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); }
u256 countAt(Address _a) const { return countAt(_a, m_default); }
u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); }
bytes codeAt(Address _a) const { return codeAt(_a, m_default); }
u256 balanceAt(Address _a, int _block) const;
u256 countAt(Address _a, int _block) const;
u256 stateAt(Address _a, u256 _l, int _block) const;
bytes codeAt(Address _a, int _block) const;
PastMessages transactions(TransactionFilter const& _filter) const;
PastMessages transactions(unsigned _watchId) const { try { std::lock_guard<std::mutex> l(m_filterLock); return transactions(m_filters.at(m_watches.at(_watchId).id).filter); } catch (...) { return PastMessages(); } }
unsigned installWatch(TransactionFilter const& _filter);
unsigned installWatch(h256 _filterId);
void uninstallWatch(unsigned _watchId);
bool peekWatch(unsigned _watchId) const { std::lock_guard<std::mutex> l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return 0; } }
bool checkWatch(unsigned _watchId) { std::lock_guard<std::mutex> l(m_filterLock); bool ret = false; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes = 0; } catch (...) {} return ret; }
// [EXTRA API]:
/// Get a map containing each of the pending transactions.
/// @TODO: Remove in favour of transactions().
Transactions pending() const { return m_postMine.pending(); }
/// Get a list of all active addresses.
std::vector<Address> addresses() const { return addresses(m_default); }
std::vector<Address> addresses(int _block) const;
// Misc stuff:
@ -253,10 +277,27 @@ public:
std::list<MineInfo> miningHistory() { auto ret = m_mineHistory; m_mineHistory.clear(); return ret; }
/// Clears pending transactions. Just for debug use.
void clearPending() { ClientGuard l(this); m_postMine = m_preMine; changed(); }
void clearPending();
private:
void work();
/// Ensure the worker thread is running. Needed for networking & mining.
void ensureWorking();
/// Do some work. Handles networking and mining.
/// @param _justQueue If true will only processing the transaction queues.
void work(bool _justQueue = false);
/// Collate the changed filters for the bloom filter of the given pending transaction.
/// Insert any filters that are activated into @a o_changed.
void appendFromNewPending(h256 _pendingTransactionBloom, h256Set& o_changed) const;
/// Collate the changed filters for the hash of the given block.
/// Insert any filters that are activated into @a o_changed.
void appendFromNewBlock(h256 _blockHash, h256Set& o_changed) const;
/// Record that the set of filters @a _filters have changed.
/// This doesn't actually make any callbacks, but incrememnts some counters in m_watches.
void noteChanged(h256Set const& _filters);
/// Return the actual block number of the block with the given int-number (positive is the same, INT_MIN is genesis block, < 0 is negative age, thus -1 is most recently mined, 0 is pending.
unsigned numberOf(int _b) const;
@ -268,9 +309,10 @@ private:
VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
BlockChain m_bc; ///< Maintains block database.
TransactionQueue m_tq; ///< Maintains list of incoming transactions not yet on the block chain.
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
State m_preMine; ///< The present state of the client.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
std::unique_ptr<PeerServer> m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required.
std::unique_ptr<std::thread> m_work;///< The work thread.
@ -283,7 +325,11 @@ private:
std::list<MineInfo> m_mineHistory;
mutable bool m_restartMining = false;
mutable bool m_changed;
mutable std::mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters;
std::map<unsigned, Watch> m_watches;
int m_default = -1;
};
inline ClientGuard::ClientGuard(Client const* _c): m_client(_c)

12
libethereum/PeerServer.cpp

@ -350,9 +350,11 @@ bool PeerServer::noteBlock(h256 _hash, bytesConstRef _data)
return false;
}
bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
h256Set PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
{
bool ret = ensureInitialised(_bc, _tq);
h256Set ret;
bool netChange = ensureInitialised(_bc, _tq);
if (m_mode == NodeMode::Full)
{
@ -421,10 +423,11 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
{
try
{
_bc.import(*it, _o);
for (auto h: _bc.import(*it, _o))
ret.insert(h);
it = m_incomingBlocks.erase(it);
++accepted;
ret = true;
netChange = true;
}
catch (UnknownParent)
{
@ -506,6 +509,7 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, OverlayDB& _o)
worst->disconnect(TooManyPeers);
}
(void)netChange;
return ret;
}

2
libethereum/PeerServer.h

@ -58,7 +58,7 @@ public:
void connect(bi::tcp::endpoint const& _ep);
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
bool sync(BlockChain& _bc, TransactionQueue&, OverlayDB& _o);
h256Set sync(BlockChain& _bc, TransactionQueue&, OverlayDB& _o);
/// Conduct I/O, polling, syncing, whatever.
/// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway.

6
libethereum/PeerSession.cpp

@ -463,7 +463,7 @@ void PeerSession::writeImpl(bytes& _buffer)
if (m_writeq.size() > 1)
return;
this->write();
write();
}
void PeerSession::write()
@ -477,12 +477,12 @@ void PeerSession::write()
{
// must check que, as write callback can occur following dropped()
if (!m_writeq.empty())
this->m_writeq.pop_front();
m_writeq.pop_front();
if (ec)
{
cwarn << "Error sending: " << ec.message();
this->dropped();
dropped();
} else
m_strand.post(boost::bind(&PeerSession::write, this));
}));

19
libethereum/State.cpp

@ -454,28 +454,24 @@ bool State::cull(TransactionQueue& _tq) const
return ret;
}
bool State::sync(TransactionQueue& _tq, bool* _changed)
h256s State::sync(TransactionQueue& _tq, bool* o_transactionQueueChanged)
{
// TRANSACTIONS
bool ret = false;
h256s ret;
auto ts = _tq.transactions();
vector<pair<h256, bytes>> futures;
for (int goodTxs = 1; goodTxs;)
{
goodTxs = 0;
for (auto const& i: ts)
{
if (!m_transactionSet.count(i.first))
{
// don't have it yet! Execute it now.
try
{
ret = true;
uncommitToMine();
execute(i.second);
if (_changed)
*_changed = true;
ret.push_back(m_transactions.back().changes.bloom());
_tq.noteGood(i);
++goodTxs;
}
@ -485,8 +481,8 @@ bool State::sync(TransactionQueue& _tq, bool* _changed)
{
// too old
_tq.drop(i.first);
if (_changed)
*_changed = true;
if (o_transactionQueueChanged)
*o_transactionQueueChanged = true;
}
else
_tq.setFuture(i);
@ -495,11 +491,10 @@ bool State::sync(TransactionQueue& _tq, bool* _changed)
{
// Something else went wrong - drop it.
_tq.drop(i.first);
if (_changed)
*_changed = true;
if (o_transactionQueueChanged)
*o_transactionQueueChanged = true;
}
}
}
}
return ret;
}

10
libethereum/State.h

@ -138,6 +138,8 @@ public:
/// @returns the set containing all addresses currently in use in Ethereum.
std::map<Address, u256> addresses() const;
BlockInfo const& info() const { return m_currentBlock; }
/// @brief Checks that mining the current object will result in a valid block.
/// Effectively attempts to import the serialised block.
/// @returns true if all is ok. If it's false, worry.
@ -178,10 +180,10 @@ public:
// TODO: Cleaner interface.
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
/// @returns true if we uncommitted from mining during the operation.
/// @a o_changed boolean pointer, the value of which will be set to true if the state changed and the pointer
/// is non-null
bool sync(TransactionQueue& _tq, bool* o_changed = nullptr);
/// @returns a list of bloom filters one for each transaction placed from the queue into the state.
/// @a o_transactionQueueChanged boolean pointer, the value of which will be set to true if the transaction queue
/// changed and the pointer is non-null
h256s sync(TransactionQueue& _tq, bool* o_transactionQueueChanged = nullptr);
/// Like sync but only operate on _tq, killing the invalid/old ones.
bool cull(TransactionQueue& _tq) const;

227
libqethereum/QEthereum.cpp

@ -1,6 +1,3 @@
#if ETH_QTQML
#include <QtQml/QtQml>
#endif
#include <QtCore/QtCore>
#include <QtWebKitWidgets/QWebFrame>
#include <libethcore/FileSystem.h>
@ -47,143 +44,6 @@ using eth::g_logPost;
using eth::g_logVerbosity;
using eth::c_instructionInfo;
// Horrible global for the mainwindow. Needed for the QmlEthereums to find the Main window which acts as multiplexer for now.
// Can get rid of this once we've sorted out ITC for signalling & multiplexed querying.
eth::Client* g_qmlClient;
QObject* g_qmlMain;
QmlAccount::QmlAccount(QObject*)
{
}
QmlAccount::~QmlAccount()
{
}
void QmlAccount::setEthereum(QmlEthereum* _eth)
{
if (m_eth == _eth)
return;
if (m_eth)
disconnect(m_eth, SIGNAL(changed()), this, SIGNAL(changed()));
m_eth = _eth;
if (m_eth)
connect(m_eth, SIGNAL(changed()), this, SIGNAL(changed()));
ethChanged();
changed();
}
eth::u256 QmlAccount::balance() const
{
if (m_eth)
return m_eth->balanceAt(m_address);
return u256(0);
}
double QmlAccount::txCount() const
{
if (m_eth)
return m_eth->txCountAt(m_address);
return 0;
}
bool QmlAccount::isContract() const
{
if (m_eth)
return m_eth->isContractAt(m_address);
return 0;
}
QmlEthereum::QmlEthereum(QObject* _p): QObject(_p)
{
connect(g_qmlMain, SIGNAL(changed()), SIGNAL(changed()));
}
QmlEthereum::~QmlEthereum()
{
}
Client* QmlEthereum::client() const
{
return g_qmlClient;
}
Address QmlEthereum::coinbase() const
{
return client()->address();
}
void QmlEthereum::setCoinbase(Address _a)
{
if (client()->address() != _a)
{
client()->setAddress(_a);
changed();
}
}
u256 QmlEthereum::balanceAt(Address _a) const
{
return client()->postState().balance(_a);
}
bool QmlEthereum::isContractAt(Address _a) const
{
return client()->postState().addressHasCode(_a);
}
bool QmlEthereum::isMining() const
{
return client()->isMining();
}
bool QmlEthereum::isListening() const
{
return client()->haveNetwork();
}
void QmlEthereum::setMining(bool _l)
{
if (_l)
client()->startMining();
else
client()->stopMining();
}
void QmlEthereum::setListening(bool _l)
{
if (_l)
client()->startNetwork();
else
client()->stopNetwork();
}
double QmlEthereum::txCountAt(Address _a) const
{
return (double)client()->postState().transactionsFrom(_a);
}
unsigned QmlEthereum::peerCount() const
{
return (unsigned)client()->peerCount();
}
void QmlEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _init)
{
client()->transact(_secret, _amount, bytes(_init.data(), _init.data() + _init.size()), _gas, _gasPrice);
}
void QmlEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data)
{
client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
eth::bytes toBytes(QString const& _s)
{
if (_s.startsWith("0x"))
@ -209,11 +69,6 @@ QString padded(QString const& _s, unsigned _l, unsigned _r)
//"0xff".bin().unbin()
QString QEthereum::secretToAddress(QString _s) const
{
return toQJS(KeyPair(toSecret(_s)).address());
}
QString padded(QString const& _s, unsigned _l)
{
if (_s.startsWith("0x") || !_s.contains(QRegExp("[^0-9]")))
@ -234,11 +89,24 @@ QString unpadded(QString _s)
QEthereum::QEthereum(QObject* _p, Client* _c, QList<eth::KeyPair> _accounts): QObject(_p), m_client(_c), m_accounts(_accounts)
{
// required to prevent crash on osx when performing addto/evaluatejavascript calls
this->moveToThread(_p->thread());
moveToThread(_p->thread());
}
QEthereum::~QEthereum()
{
clearWatches();
}
void QEthereum::clearWatches()
{
for (auto i: m_watches)
m_client->uninstallWatch(i);
m_watches.clear();
}
QString QEthereum::secretToAddress(QString _s) const
{
return toQJS(KeyPair(toSecret(_s)).address());
}
void QEthereum::setup(QWebFrame*)
@ -310,7 +178,7 @@ void QEthereum::setCoinbase(QString _a)
if (client()->address() != toAddress(_a))
{
client()->setAddress(toAddress(_a));
changed();
coinbaseChanged();
}
}
@ -370,11 +238,11 @@ double QEthereum::countAt(QString _a, int _block) const
return (double)(uint64_t)client()->countAt(toAddress(_a), _block);
}
QString QEthereum::getTransactions(QString _a) const
static eth::TransactionFilter toTransactionFilter(QString _json)
{
eth::TransactionFilter filter;
QJsonObject f = QJsonDocument::fromJson(_a.toUtf8()).object();
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if (f.contains("earliest"))
filter.withEarliest(f["earliest"].toInt());
if (f.contains("latest"))
@ -413,12 +281,15 @@ QString QEthereum::getTransactions(QString _a) const
else
filter.altered(toAddress(f["altered"].toString()));
}
return filter;
}
QJsonArray ret;
for (eth::PastMessage const& t: m_client->transactions(filter))
static QString toJson(eth::PastMessages const& _pms)
{
QJsonArray jsonArray;
for (eth::PastMessage const& t: _pms)
{
QJsonObject v;
v["data"] = ::fromBinary(t.input);
v["input"] = ::fromBinary(t.input);
v["output"] = ::fromBinary(t.output);
v["to"] = toQJS(t.to);
@ -430,10 +301,15 @@ QString QEthereum::getTransactions(QString _a) const
for (int i: t.path)
path.append(i);
v["path"] = path;
v["age"] = (int)t.age;
ret.append(v);
v["number"] = (int)t.number;
jsonArray.append(v);
}
return QString::fromUtf8(QJsonDocument(ret).toJson());
return QString::fromUtf8(QJsonDocument(jsonArray).toJson());
}
QString QEthereum::getTransactions(QString _json) const
{
return toJson(m_client->transactions(toTransactionFilter(_json)));
}
bool QEthereum::isMining() const
@ -469,19 +345,48 @@ unsigned QEthereum::peerCount() const
QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice)
{
client()->changed();
auto ret = toQJS(client()->transact(toSecret(_secret), toU256(_amount), toBytes(_init), toU256(_gas), toU256(_gasPrice)));
while (!client()->peekChanged())
this_thread::sleep_for(chrono::milliseconds(10));
client()->flushTransactions();
return ret;
}
void QEthereum::doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice)
{
client()->changed();
client()->transact(toSecret(_secret), toU256(_amount), toAddress(_dest), toBytes(_data), toU256(_gas), toU256(_gasPrice));
while (!client()->peekChanged())
this_thread::sleep_for(chrono::milliseconds(10));
client()->flushTransactions();
}
unsigned QEthereum::newWatch(QString _json)
{
unsigned ret;
if (_json == "chainChanged")
ret = m_client->installWatch(eth::NewBlockFilter);
else if (_json == "pendingChanged")
ret = m_client->installWatch(eth::NewPendingFilter);
else
ret = m_client->installWatch(toTransactionFilter(_json));
m_watches.push_back(ret);
return ret;
}
QString QEthereum::watchTransactions(unsigned _w)
{
return toJson(m_client->transactions(_w));
}
void QEthereum::killWatch(unsigned _w)
{
m_client->uninstallWatch(_w);
std::remove(m_watches.begin(), m_watches.end(), _w);
}
void QEthereum::poll()
{
if (!m_client)
return;
for (auto w: m_watches)
if (m_client->checkWatch(w))
emit watchChanged(w);
}
// extra bits needed to link on VS

372
libqethereum/QEthereum.h

@ -1,9 +1,8 @@
#pragma once
#include <QtCore/QAbstractListModel>
#if ETH_QTQML
#include <QtQml/QtQml>
#endif
#include <QtCore/QObject>
#include <QtCore/QStringList>
#include <QtCore/QList>
#include <libethential/CommonIO.h>
#include <libethcore/CommonEth.h>
@ -12,280 +11,10 @@ class Client;
class State;
}
class QQmlEngine;
class QJSEngine;
class QWebFrame;
class QEthereum;
class QmlAccount;
class QmlEthereum;
extern eth::Client* g_qmlClient;
extern QObject* g_qmlMain;
Q_DECLARE_METATYPE(eth::u256)
Q_DECLARE_METATYPE(eth::Address)
Q_DECLARE_METATYPE(eth::Secret)
Q_DECLARE_METATYPE(eth::KeyPair)
Q_DECLARE_METATYPE(QEthereum*)
Q_DECLARE_METATYPE(QmlAccount*)
Q_DECLARE_METATYPE(QmlEthereum*)
class QmlU256Helper: public QObject
{
Q_OBJECT
public:
QmlU256Helper(QObject* _p = nullptr): QObject(_p) {}
Q_INVOKABLE eth::u256 add(eth::u256 _a, eth::u256 _b) const { return _a + _b; }
Q_INVOKABLE eth::u256 sub(eth::u256 _a, eth::u256 _b) const { return _a - _b; }
Q_INVOKABLE eth::u256 mul(eth::u256 _a, int _b) const { return _a * _b; }
Q_INVOKABLE eth::u256 mul(int _a, eth::u256 _b) const { return _a * _b; }
Q_INVOKABLE eth::u256 div(eth::u256 _a, int _b) const { return _a / _b; }
Q_INVOKABLE eth::u256 wei(double _s) const { return (eth::u256)_s; }
Q_INVOKABLE eth::u256 szabo(double _s) const { return (eth::u256)(_s * (double)eth::szabo); }
Q_INVOKABLE eth::u256 finney(double _s) const { return (eth::u256)(_s * (double)eth::finney); }
Q_INVOKABLE eth::u256 ether(double _s) const { return (eth::u256)(_s * (double)eth::ether); }
Q_INVOKABLE eth::u256 wei(unsigned _s) const { return (eth::u256)_s; }
Q_INVOKABLE eth::u256 szabo(unsigned _s) const { return (eth::u256)(_s * eth::szabo); }
Q_INVOKABLE eth::u256 finney(unsigned _s) const { return (eth::u256)(_s * eth::finney); }
Q_INVOKABLE eth::u256 ether(unsigned _s) const { return (eth::u256)(_s * eth::ether); }
Q_INVOKABLE double toWei(eth::u256 _t) const { return (double)_t; }
Q_INVOKABLE double toSzabo(eth::u256 _t) const { return toWei(_t) / (double)eth::szabo; }
Q_INVOKABLE double toFinney(eth::u256 _t) const { return toWei(_t) / (double)eth::finney; }
Q_INVOKABLE double toEther(eth::u256 _t) const { return toWei(_t) / (double)eth::ether; }
Q_INVOKABLE double value(eth::u256 _t) const { return (double)_t; }
Q_INVOKABLE QString stringOf(eth::u256 _t) const { return QString::fromStdString(eth::formatBalance(_t)); }
};
class QmlKeyHelper: public QObject
{
Q_OBJECT
public:
QmlKeyHelper(QObject* _p = nullptr): QObject(_p) {}
Q_INVOKABLE eth::KeyPair create() const { return eth::KeyPair::create(); }
Q_INVOKABLE eth::Address address(eth::KeyPair _p) const { return _p.address(); }
Q_INVOKABLE eth::Secret secret(eth::KeyPair _p) const { return _p.secret(); }
Q_INVOKABLE eth::KeyPair keypair(eth::Secret _k) const { return eth::KeyPair(_k); }
Q_INVOKABLE bool isNull(eth::Address _a) const { return !_a; }
Q_INVOKABLE eth::Address addressOf(QString _s) const { return eth::Address(_s.toStdString()); }
Q_INVOKABLE QString stringOf(eth::Address _a) const { return QString::fromStdString(eth::toHex(_a.asArray())); }
Q_INVOKABLE QString toAbridged(eth::Address _a) const { return QString::fromStdString(_a.abridged()); }
};
class QmlAccount: public QObject
{
Q_OBJECT
public:
QmlAccount(QObject* _p = nullptr);
virtual ~QmlAccount();
Q_INVOKABLE QmlEthereum* ethereum() const { return m_eth; }
Q_INVOKABLE eth::u256 balance() const;
Q_INVOKABLE double txCount() const;
Q_INVOKABLE bool isContract() const;
Q_INVOKABLE eth::Address address() const { return m_address; }
// TODO: past transactions models.
public slots:
void setEthereum(QmlEthereum* _eth);
void setAddress(eth::Address _a) { m_address = _a; }
signals:
void changed();
void ethChanged();
private:
QmlEthereum* m_eth = nullptr;
eth::Address m_address;
Q_PROPERTY(eth::u256 balance READ balance NOTIFY changed STORED false)
Q_PROPERTY(double txCount READ txCount NOTIFY changed STORED false)
Q_PROPERTY(bool isContract READ isContract NOTIFY changed STORED false)
Q_PROPERTY(eth::Address address READ address WRITE setAddress NOTIFY changed)
Q_PROPERTY(QmlEthereum* ethereum READ ethereum WRITE setEthereum NOTIFY ethChanged)
};
class QmlEthereum: public QObject
{
Q_OBJECT
public:
QmlEthereum(QObject* _p = nullptr);
virtual ~QmlEthereum();
eth::Client* client() const;
static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new QmlU256Helper; }
static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new QmlKeyHelper; }
Q_INVOKABLE eth::Address coinbase() const;
Q_INVOKABLE bool isListening() const;
Q_INVOKABLE bool isMining() const;
Q_INVOKABLE eth::u256 balanceAt(eth::Address _a) const;
Q_INVOKABLE double txCountAt(eth::Address _a) const;
Q_INVOKABLE bool isContractAt(eth::Address _a) const;
Q_INVOKABLE unsigned peerCount() const;
Q_INVOKABLE QmlEthereum* self() { return this; }
public slots:
void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _data);
void transact(eth::Secret _secret, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _init);
void setCoinbase(eth::Address);
void setMining(bool _l);
void setListening(bool _l);
signals:
void changed();
// void netChanged();
// void miningChanged();
private:
Q_PROPERTY(eth::Address coinbase READ coinbase WRITE setCoinbase NOTIFY changed)
Q_PROPERTY(bool listening READ isListening WRITE setListening)
Q_PROPERTY(bool mining READ isMining WRITE setMining)
};
#if 0
template <class T> T to(QVariant const& _s) { if (_s.type() != QVariant::String) return T(); auto s = _s.toString().toLatin1(); assert(s.size() == sizeof(T)); return *(T*)s.data(); }
template <class T> QVariant toQJS(T const& _s) { QLatin1String ret((char*)&_s, sizeof(T)); assert(QVariant(QString(ret)).toString().toLatin1().size() == sizeof(T)); assert(*(T*)(QVariant(QString(ret)).toString().toLatin1().data()) == _s); return QVariant(QString(ret)); }
class U256Helper: public QObject
{
Q_OBJECT
public:
U256Helper(QObject* _p = nullptr): QObject(_p) {}
static eth::u256 in(QVariant const& _s) { return to<eth::u256>(_s); }
static QVariant out(eth::u256 const& _s) { return toQJS(_s); }
Q_INVOKABLE QVariant add(QVariant _a, QVariant _b) const { return out(in(_a) + in(_b)); }
Q_INVOKABLE QVariant sub(QVariant _a, QVariant _b) const { return out(in(_a) - in(_b)); }
Q_INVOKABLE QVariant mul(QVariant _a, int _b) const { return out(in(_a) * in(_b)); }
Q_INVOKABLE QVariant mul(int _a, QVariant _b) const { return out(in(_a) * in(_b)); }
Q_INVOKABLE QVariant div(QVariant _a, int _b) const { return out(in(_a) / in(_b)); }
Q_INVOKABLE QVariant wei(double _s) const { return out(eth::u256(_s)); }
Q_INVOKABLE QVariant szabo(double _s) const { return out(eth::u256(_s * (double)eth::szabo)); }
Q_INVOKABLE QVariant finney(double _s) const { return out(eth::u256(_s * (double)eth::finney)); }
Q_INVOKABLE QVariant ether(double _s) const { return out(eth::u256(_s * (double)eth::ether)); }
Q_INVOKABLE QVariant wei(unsigned _s) const { return value(_s); }
Q_INVOKABLE QVariant szabo(unsigned _s) const { return out(eth::u256(_s) * eth::szabo); }
Q_INVOKABLE QVariant finney(unsigned _s) const { return out(eth::u256(_s) * eth::finney); }
Q_INVOKABLE QVariant ether(unsigned _s) const { return out(eth::u256(_s) * eth::ether); }
Q_INVOKABLE double toWei(QVariant _t) const { return toValue(_t); }
Q_INVOKABLE double toSzabo(QVariant _t) const { return toWei(_t) / (double)eth::szabo; }
Q_INVOKABLE double toFinney(QVariant _t) const { return toWei(_t) / (double)eth::finney; }
Q_INVOKABLE double toEther(QVariant _t) const { return toWei(_t) / (double)eth::ether; }
Q_INVOKABLE QVariant value(unsigned _s) const { return out(eth::u256(_s)); }
Q_INVOKABLE double toValue(QVariant _t) const { return (double)in(_t); }
Q_INVOKABLE QString ethOf(QVariant _t) const { return QString::fromStdString(eth::formatBalance(in(_t))); }
Q_INVOKABLE QString stringOf(QVariant _t) const { return QString::fromStdString(eth::toString(in(_t))); }
Q_INVOKABLE QByteArray bytesOf(QVariant _t) const { eth::h256 b = in(_t); return QByteArray((char const*)&b, sizeof(eth::h256)); }
Q_INVOKABLE QVariant fromHex(QString _s) const { return out((eth::u256)eth::h256(_s.toStdString())); }
Q_INVOKABLE QVariant fromAddress(QVariant/*eth::Address*/ _a) const { return out((eth::u160)to<eth::Address>(_a)); }
Q_INVOKABLE QVariant toAddress(QVariant/*eth::Address*/ _a) const { return toQJS<eth::Address>((eth::u160)in(_a)); }
Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); }
};
class KeyHelper: public QObject
{
Q_OBJECT
public:
KeyHelper(QObject* _p = nullptr): QObject(_p) {}
static eth::Address in(QVariant const& _s) { return to<eth::Address>(_s); }
static QVariant out(eth::Address const& _s) { return toQJS(_s); }
Q_INVOKABLE QVariant/*eth::KeyPair*/ create() const { return toQJS(eth::KeyPair::create()); }
Q_INVOKABLE QVariant/*eth::Address*/ address(QVariant/*eth::KeyPair*/ _p) const { return out(to<eth::KeyPair>(_p).address()); }
Q_INVOKABLE QVariant/*eth::Secret*/ secret(QVariant/*eth::KeyPair*/ _p) const { return toQJS(to<eth::KeyPair>(_p).secret()); }
Q_INVOKABLE QVariant/*eth::KeyPair*/ keypair(QVariant/*eth::Secret*/ _k) const { return toQJS(eth::KeyPair(to<eth::Secret>(_k))); }
Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); }
Q_INVOKABLE QVariant/*eth::Address*/ addressOf(QString _s) const { return out(eth::Address(_s.toStdString())); }
Q_INVOKABLE QString stringOf(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(eth::toHex(in(_a).asArray())); }
Q_INVOKABLE QString toAbridged(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(in(_a).abridged()); }
};
class BytesHelper: public QObject
{
Q_OBJECT
public:
BytesHelper(QObject* _p = nullptr): QObject(_p) {}
Q_INVOKABLE QByteArray concat(QVariant _v, QVariant _w) const
{
QByteArray ba;
if (_v.type() == QVariant::ByteArray)
ba = _v.toByteArray();
else
ba = _v.toString().toLatin1();
QByteArray ba2;
if (_w.type() == QVariant::ByteArray)
ba2 = _w.toByteArray();
else
ba2 = _w.toString().toLatin1();
ba.append(ba2);
return QByteArray(ba);
}
Q_INVOKABLE QByteArray concat(QByteArray _v, QByteArray _w) const
{
_v.append(_w);
return _v;
}
Q_INVOKABLE QByteArray fromString(QString _s) const
{
return _s.toLatin1();
}
Q_INVOKABLE QByteArray fromString(QString _s, unsigned _padding) const
{
QByteArray b = _s.toLatin1();
for (unsigned i = b.size(); i < _padding; ++i)
b.append((char)0);
b.resize(_padding);
return b;
}
Q_INVOKABLE QString toString(QByteArray _b) const
{
while (_b.size() && !_b[_b.size() - 1])
_b.resize(_b.size() - 1);
return QString::fromLatin1(_b);
}
Q_INVOKABLE QVariant u256of(QByteArray _s) const
{
while (_s.size() < 32)
_s.append((char)0);
eth::h256 ret((uint8_t const*)_s.data(), eth::h256::ConstructFromPointer);
return toQJS<eth::u256>(ret);
}
};
#endif
inline eth::bytes asBytes(QString const& _s)
{
@ -324,17 +53,7 @@ template <unsigned N> eth::FixedHash<N> toFixed(QString const& _s)
return eth::FixedHash<N>(asBytes(padded(_s, N)));
}
template <unsigned N> boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> toInt(QString const& _s)
{
if (_s.startsWith("0x"))
return eth::fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(eth::fromHex(_s.toStdString().substr(2)));
else if (!_s.contains(QRegExp("[^0-9]")))
// Hex or Decimal
return boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>(_s.toStdString());
else
// Binary
return eth::fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(asBytes(padded(_s, N)));
}
template <unsigned N> inline boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> toInt(QString const& _s);
inline eth::Address toAddress(QString const& _s) { return toFixed<20>(_s); }
inline eth::Secret toSecret(QString const& _s) { return toFixed<32>(_s); }
@ -376,7 +95,7 @@ public:
void setup(QWebFrame* _e);
void teardown(QWebFrame* _e);
void setAccounts(QList<eth::KeyPair> _l) { m_accounts = _l; this->changed(); }
void setAccounts(QList<eth::KeyPair> _l) { m_accounts = _l; keysChanged(); }
Q_INVOKABLE QString ethTest() const { return "Hello world!"; }
Q_INVOKABLE QEthereum* self() { return this; }
@ -403,11 +122,16 @@ public:
Q_INVOKABLE double countAt(QString/*eth::Address*/ _a, int _block) const;
Q_INVOKABLE QString/*eth::u256*/ stateAt(QString/*eth::Address*/ _a, QString/*eth::u256*/ _p, int _block) const;
Q_INVOKABLE QString/*eth::u256*/ codeAt(QString/*eth::Address*/ _a, int _block) const;
Q_INVOKABLE QString getTransactions(QString _attribs) const;
Q_INVOKABLE QString/*json*/ getTransactions(QString _attribs/*json*/) const;
Q_INVOKABLE QString doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice);
Q_INVOKABLE void doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice);
Q_INVOKABLE unsigned newWatch(QString _json);
Q_INVOKABLE QString watchTransactions(unsigned _w);
Q_INVOKABLE void killWatch(unsigned _w);
void clearWatches();
bool isListening() const;
bool isMining() const;
@ -431,41 +155,61 @@ public slots:
void setMining(bool _l);
void setListening(bool _l);
/// Check to see if anything has changed, fire off signals if so.
/// @note Must be called in the QObject's thread.
void poll();
signals:
void changed();
// void netChanged();
// void miningChanged();
void watchChanged(unsigned _w);
void coinbaseChanged();
void keysChanged();
void netChanged();
void miningChanged();
private:
Q_PROPERTY(QString number READ number NOTIFY changed)
Q_PROPERTY(QString coinbase READ coinbase WRITE setCoinbase NOTIFY changed)
Q_PROPERTY(bool listening READ isListening WRITE setListening)
Q_PROPERTY(bool mining READ isMining WRITE setMining)
Q_PROPERTY(QString gasPrice READ gasPrice NOTIFY changed)
Q_PROPERTY(QString key READ key NOTIFY changed)
Q_PROPERTY(QStringList keys READ keys NOTIFY changed)
Q_PROPERTY(unsigned peerCount READ peerCount)
Q_PROPERTY(QString number READ number NOTIFY watchChanged)
Q_PROPERTY(QString coinbase READ coinbase WRITE setCoinbase NOTIFY coinbaseChanged)
Q_PROPERTY(QString gasPrice READ gasPrice)
Q_PROPERTY(QString key READ key NOTIFY keysChanged)
Q_PROPERTY(QStringList keys READ keys NOTIFY keysChanged)
Q_PROPERTY(bool mining READ isMining WRITE setMining NOTIFY netChanged)
Q_PROPERTY(bool listening READ isListening WRITE setListening NOTIFY netChanged)
Q_PROPERTY(unsigned peerCount READ peerCount NOTIFY miningChanged)
eth::Client* m_client;
std::vector<unsigned> m_watches;
QList<eth::KeyPair> m_accounts;
};
#define QETH_INSTALL_JS_NAMESPACE [f, eth, this]() \
#define QETH_INSTALL_JS_NAMESPACE(frame, eth, env) [frame, eth, env]() \
{ \
f->disconnect(); \
f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership); \
f->addToJavaScriptWindowObject("eth", eth, QWebFrame::ScriptOwnership); \
f->evaluateJavaScript("eth.watch = function(a, s, f) { eth.changed.connect(f ? f : s) }"); \
f->evaluateJavaScript("eth.newBlock = function(f) { eth.changed.connect(f) }"); \
\
f->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }"); \
f->evaluateJavaScript("eth.transact = function(s, v, t, d, g, p, f) { eth.doTransact(s, v, t, d, g, p); if (f) f() }"); \
f->evaluateJavaScript("eth.transactions = function(a) { return JSON.parse(eth.getTransactions(JSON.stringify(a))); }"); \
f->evaluateJavaScript("String.prototype.pad = function(l, r) { return eth.pad(this, l, r) }"); \
f->evaluateJavaScript("String.prototype.bin = function() { return eth.toBinary(this) }"); \
f->evaluateJavaScript("String.prototype.unbin = function(l) { return eth.fromBinary(this) }"); \
f->evaluateJavaScript("String.prototype.unpad = function(l) { return eth.unpad(this) }"); \
f->evaluateJavaScript("String.prototype.dec = function() { return eth.toDecimal(this) }"); \
f->evaluateJavaScript("String.prototype.sha3 = function() { return eth.sha3(this) }"); \
frame->disconnect(); \
frame->addToJavaScriptWindowObject("env", env, QWebFrame::QtOwnership); \
frame->addToJavaScriptWindowObject("eth", eth, QWebFrame::ScriptOwnership); \
frame->evaluateJavaScript("eth.makeWatch = function(a) { var ww = eth.newWatch(a); return { w: ww, uninstall: function() { eth.killWatch(w) }, changed: function(f) { eth.watchChanged.connect(function(nw) { if (nw == this.w) f() }) }, transactions: function() { return JSON.parse(eth.watchTransactions(w)) } }; }"); \
frame->evaluateJavaScript("eth.watch = function(a) { return makeWatch(JSON.stringify(a)); }"); \
frame->evaluateJavaScript("eth.watchChain = function() { return eth.makeWatch('chainChanged') }"); \
frame->evaluateJavaScript("eth.watchPending = function() { return eth.makeWatch('pendingChanged') }"); \
frame->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }"); \
frame->evaluateJavaScript("eth.transact = function(s, v, t, d, g, p, f) { eth.doTransact(s, v, t, d, g, p); if (f) f() }"); \
frame->evaluateJavaScript("eth.transactions = function(a) { return JSON.parse(eth.getTransactions(JSON.stringify(a))); }"); \
frame->evaluateJavaScript("String.prototype.pad = function(l, r) { return eth.pad(this, l, r) }"); \
frame->evaluateJavaScript("String.prototype.bin = function() { return eth.toBinary(this) }"); \
frame->evaluateJavaScript("String.prototype.unbin = function(l) { return eth.fromBinary(this) }"); \
frame->evaluateJavaScript("String.prototype.unpad = function(l) { return eth.unpad(this) }"); \
frame->evaluateJavaScript("String.prototype.dec = function() { return eth.toDecimal(this) }"); \
frame->evaluateJavaScript("String.prototype.sha3 = function() { return eth.sha3(this) }"); \
}
template <unsigned N> inline boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> toInt(QString const& _s)
{
if (_s.startsWith("0x"))
return eth::fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(eth::fromHex(_s.toStdString().substr(2)));
else if (!_s.contains(QRegExp("[^0-9]")))
// Hex or Decimal
return boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>(_s.toStdString());
else
// Binary
return eth::fromBigEndian<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>>(asBytes(padded(_s, N)));
}

188
libqethereum/QmlEthereum.cpp

@ -0,0 +1,188 @@
#if ETH_QTQML
#include <QtQml/QtQml>
#endif
#include <QtCore/QtCore>
#include <QtWebKitWidgets/QWebFrame>
#include <libethcore/FileSystem.h>
#include <libethcore/Dagger.h>
#include <libevmface/Instruction.h>
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libethereum/PeerServer.h>
#include "QmlEthereum.h"
using namespace std;
// types
using eth::bytes;
using eth::bytesConstRef;
using eth::h160;
using eth::h256;
using eth::u160;
using eth::u256;
using eth::u256s;
using eth::Address;
using eth::BlockInfo;
using eth::Client;
using eth::Instruction;
using eth::KeyPair;
using eth::NodeMode;
using eth::PeerInfo;
using eth::RLP;
using eth::Secret;
using eth::Transaction;
// functions
using eth::toHex;
using eth::disassemble;
using eth::formatBalance;
using eth::fromHex;
using eth::right160;
using eth::simpleDebugOut;
using eth::toLog2;
using eth::toString;
using eth::units;
// vars
using eth::g_logPost;
using eth::g_logVerbosity;
using eth::c_instructionInfo;
// Horrible global for the mainwindow. Needed for the QmlEthereums to find the Main window which acts as multiplexer for now.
// Can get rid of this once we've sorted out ITC for signalling & multiplexed querying.
eth::Client* g_qmlClient;
QObject* g_qmlMain;
QmlAccount::QmlAccount(QObject*)
{
}
QmlAccount::~QmlAccount()
{
}
void QmlAccount::setEthereum(QmlEthereum* _eth)
{
if (m_eth == _eth)
return;
// if (m_eth)
// disconnect(m_eth, SIGNAL(changed()), this, SIGNAL(changed()));
m_eth = _eth;
// if (m_eth)
// connect(m_eth, SIGNAL(changed()), this, SIGNAL(changed()));
ethChanged();
// changed();
}
eth::u256 QmlAccount::balance() const
{
if (m_eth)
return m_eth->balanceAt(m_address);
return u256(0);
}
double QmlAccount::txCount() const
{
if (m_eth)
return m_eth->txCountAt(m_address);
return 0;
}
bool QmlAccount::isContract() const
{
if (m_eth)
return m_eth->isContractAt(m_address);
return 0;
}
QmlEthereum::QmlEthereum(QObject* _p): QObject(_p)
{
// connect(g_qmlMain, SIGNAL(changed()), SIGNAL(changed()));
}
QmlEthereum::~QmlEthereum()
{
}
Client* QmlEthereum::client() const
{
return g_qmlClient;
}
Address QmlEthereum::coinbase() const
{
return client()->address();
}
void QmlEthereum::setCoinbase(Address _a)
{
if (client()->address() != _a)
{
client()->setAddress(_a);
// changed();
}
}
u256 QmlEthereum::balanceAt(Address _a) const
{
return client()->postState().balance(_a);
}
bool QmlEthereum::isContractAt(Address _a) const
{
return client()->postState().addressHasCode(_a);
}
bool QmlEthereum::isMining() const
{
return client()->isMining();
}
bool QmlEthereum::isListening() const
{
return client()->haveNetwork();
}
void QmlEthereum::setMining(bool _l)
{
if (_l)
client()->startMining();
else
client()->stopMining();
}
void QmlEthereum::setListening(bool _l)
{
if (_l)
client()->startNetwork();
else
client()->stopNetwork();
}
double QmlEthereum::txCountAt(Address _a) const
{
return (double)client()->postState().transactionsFrom(_a);
}
unsigned QmlEthereum::peerCount() const
{
return (unsigned)client()->peerCount();
}
void QmlEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _init)
{
client()->transact(_secret, _amount, bytes(_init.data(), _init.data() + _init.size()), _gas, _gasPrice);
}
void QmlEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data)
{
client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice);
}
// extra bits needed to link on VS
#ifdef _MSC_VER
// include moc file, ofuscated to hide from automoc
#include\
"moc_QmlEthereum.cpp"
#endif

283
libqethereum/QmlEthereum.h

@ -0,0 +1,283 @@
#pragma once
#include <QtCore/QAbstractListModel>
#if ETH_QTQML
#include <QtQml/QtQml>
#endif
#include <libethential/CommonIO.h>
#include <libethcore/CommonEth.h>
namespace eth {
class Client;
class State;
}
class QQmlEngine;
class QmlAccount;
class QmlEthereum;
extern eth::Client* g_qmlClient;
extern QObject* g_qmlMain;
Q_DECLARE_METATYPE(eth::u256)
Q_DECLARE_METATYPE(eth::Address)
Q_DECLARE_METATYPE(eth::Secret)
Q_DECLARE_METATYPE(eth::KeyPair)
Q_DECLARE_METATYPE(QmlAccount*)
Q_DECLARE_METATYPE(QmlEthereum*)
class QmlU256Helper: public QObject
{
Q_OBJECT
public:
QmlU256Helper(QObject* _p = nullptr): QObject(_p) {}
Q_INVOKABLE eth::u256 add(eth::u256 _a, eth::u256 _b) const { return _a + _b; }
Q_INVOKABLE eth::u256 sub(eth::u256 _a, eth::u256 _b) const { return _a - _b; }
Q_INVOKABLE eth::u256 mul(eth::u256 _a, int _b) const { return _a * _b; }
Q_INVOKABLE eth::u256 mul(int _a, eth::u256 _b) const { return _a * _b; }
Q_INVOKABLE eth::u256 div(eth::u256 _a, int _b) const { return _a / _b; }
Q_INVOKABLE eth::u256 wei(double _s) const { return (eth::u256)_s; }
Q_INVOKABLE eth::u256 szabo(double _s) const { return (eth::u256)(_s * (double)eth::szabo); }
Q_INVOKABLE eth::u256 finney(double _s) const { return (eth::u256)(_s * (double)eth::finney); }
Q_INVOKABLE eth::u256 ether(double _s) const { return (eth::u256)(_s * (double)eth::ether); }
Q_INVOKABLE eth::u256 wei(unsigned _s) const { return (eth::u256)_s; }
Q_INVOKABLE eth::u256 szabo(unsigned _s) const { return (eth::u256)(_s * eth::szabo); }
Q_INVOKABLE eth::u256 finney(unsigned _s) const { return (eth::u256)(_s * eth::finney); }
Q_INVOKABLE eth::u256 ether(unsigned _s) const { return (eth::u256)(_s * eth::ether); }
Q_INVOKABLE double toWei(eth::u256 _t) const { return (double)_t; }
Q_INVOKABLE double toSzabo(eth::u256 _t) const { return toWei(_t) / (double)eth::szabo; }
Q_INVOKABLE double toFinney(eth::u256 _t) const { return toWei(_t) / (double)eth::finney; }
Q_INVOKABLE double toEther(eth::u256 _t) const { return toWei(_t) / (double)eth::ether; }
Q_INVOKABLE double value(eth::u256 _t) const { return (double)_t; }
Q_INVOKABLE QString stringOf(eth::u256 _t) const { return QString::fromStdString(eth::formatBalance(_t)); }
};
class QmlKeyHelper: public QObject
{
Q_OBJECT
public:
QmlKeyHelper(QObject* _p = nullptr): QObject(_p) {}
Q_INVOKABLE eth::KeyPair create() const { return eth::KeyPair::create(); }
Q_INVOKABLE eth::Address address(eth::KeyPair _p) const { return _p.address(); }
Q_INVOKABLE eth::Secret secret(eth::KeyPair _p) const { return _p.secret(); }
Q_INVOKABLE eth::KeyPair keypair(eth::Secret _k) const { return eth::KeyPair(_k); }
Q_INVOKABLE bool isNull(eth::Address _a) const { return !_a; }
Q_INVOKABLE eth::Address addressOf(QString _s) const { return eth::Address(_s.toStdString()); }
Q_INVOKABLE QString stringOf(eth::Address _a) const { return QString::fromStdString(eth::toHex(_a.asArray())); }
Q_INVOKABLE QString toAbridged(eth::Address _a) const { return QString::fromStdString(_a.abridged()); }
};
class QmlAccount: public QObject
{
Q_OBJECT
public:
QmlAccount(QObject* _p = nullptr);
virtual ~QmlAccount();
Q_INVOKABLE QmlEthereum* ethereum() const { return m_eth; }
Q_INVOKABLE eth::u256 balance() const;
Q_INVOKABLE double txCount() const;
Q_INVOKABLE bool isContract() const;
Q_INVOKABLE eth::Address address() const { return m_address; }
// TODO: past transactions models.
public slots:
void setEthereum(QmlEthereum* _eth);
void setAddress(eth::Address _a) { m_address = _a; }
signals:
void changed();
void ethChanged();
private:
QmlEthereum* m_eth = nullptr;
eth::Address m_address;
Q_PROPERTY(eth::u256 balance READ balance NOTIFY changed STORED false)
Q_PROPERTY(double txCount READ txCount NOTIFY changed STORED false)
Q_PROPERTY(bool isContract READ isContract NOTIFY changed STORED false)
Q_PROPERTY(eth::Address address READ address WRITE setAddress NOTIFY changed)
Q_PROPERTY(QmlEthereum* ethereum READ ethereum WRITE setEthereum NOTIFY ethChanged)
};
class QmlEthereum: public QObject
{
Q_OBJECT
public:
QmlEthereum(QObject* _p = nullptr);
virtual ~QmlEthereum();
eth::Client* client() const;
static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new QmlU256Helper; }
static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new QmlKeyHelper; }
Q_INVOKABLE eth::Address coinbase() const;
Q_INVOKABLE bool isListening() const;
Q_INVOKABLE bool isMining() const;
Q_INVOKABLE eth::u256 balanceAt(eth::Address _a) const;
Q_INVOKABLE double txCountAt(eth::Address _a) const;
Q_INVOKABLE bool isContractAt(eth::Address _a) const;
Q_INVOKABLE unsigned peerCount() const;
Q_INVOKABLE QmlEthereum* self() { return this; }
public slots:
void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _data);
void transact(eth::Secret _secret, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _init);
void setCoinbase(eth::Address);
void setMining(bool _l);
void setListening(bool _l);
signals:
void coinbaseChanged();
// void netChanged();
// void miningChanged();
private:
Q_PROPERTY(eth::Address coinbase READ coinbase WRITE setCoinbase NOTIFY coinbaseChanged)
Q_PROPERTY(bool listening READ isListening WRITE setListening)
Q_PROPERTY(bool mining READ isMining WRITE setMining)
};
#if 0
template <class T> T to(QVariant const& _s) { if (_s.type() != QVariant::String) return T(); auto s = _s.toString().toLatin1(); assert(s.size() == sizeof(T)); return *(T*)s.data(); }
template <class T> QVariant toQJS(T const& _s) { QLatin1String ret((char*)&_s, sizeof(T)); assert(QVariant(QString(ret)).toString().toLatin1().size() == sizeof(T)); assert(*(T*)(QVariant(QString(ret)).toString().toLatin1().data()) == _s); return QVariant(QString(ret)); }
class U256Helper: public QObject
{
Q_OBJECT
public:
U256Helper(QObject* _p = nullptr): QObject(_p) {}
static eth::u256 in(QVariant const& _s) { return to<eth::u256>(_s); }
static QVariant out(eth::u256 const& _s) { return toQJS(_s); }
Q_INVOKABLE QVariant add(QVariant _a, QVariant _b) const { return out(in(_a) + in(_b)); }
Q_INVOKABLE QVariant sub(QVariant _a, QVariant _b) const { return out(in(_a) - in(_b)); }
Q_INVOKABLE QVariant mul(QVariant _a, int _b) const { return out(in(_a) * in(_b)); }
Q_INVOKABLE QVariant mul(int _a, QVariant _b) const { return out(in(_a) * in(_b)); }
Q_INVOKABLE QVariant div(QVariant _a, int _b) const { return out(in(_a) / in(_b)); }
Q_INVOKABLE QVariant wei(double _s) const { return out(eth::u256(_s)); }
Q_INVOKABLE QVariant szabo(double _s) const { return out(eth::u256(_s * (double)eth::szabo)); }
Q_INVOKABLE QVariant finney(double _s) const { return out(eth::u256(_s * (double)eth::finney)); }
Q_INVOKABLE QVariant ether(double _s) const { return out(eth::u256(_s * (double)eth::ether)); }
Q_INVOKABLE QVariant wei(unsigned _s) const { return value(_s); }
Q_INVOKABLE QVariant szabo(unsigned _s) const { return out(eth::u256(_s) * eth::szabo); }
Q_INVOKABLE QVariant finney(unsigned _s) const { return out(eth::u256(_s) * eth::finney); }
Q_INVOKABLE QVariant ether(unsigned _s) const { return out(eth::u256(_s) * eth::ether); }
Q_INVOKABLE double toWei(QVariant _t) const { return toValue(_t); }
Q_INVOKABLE double toSzabo(QVariant _t) const { return toWei(_t) / (double)eth::szabo; }
Q_INVOKABLE double toFinney(QVariant _t) const { return toWei(_t) / (double)eth::finney; }
Q_INVOKABLE double toEther(QVariant _t) const { return toWei(_t) / (double)eth::ether; }
Q_INVOKABLE QVariant value(unsigned _s) const { return out(eth::u256(_s)); }
Q_INVOKABLE double toValue(QVariant _t) const { return (double)in(_t); }
Q_INVOKABLE QString ethOf(QVariant _t) const { return QString::fromStdString(eth::formatBalance(in(_t))); }
Q_INVOKABLE QString stringOf(QVariant _t) const { return QString::fromStdString(eth::toString(in(_t))); }
Q_INVOKABLE QByteArray bytesOf(QVariant _t) const { eth::h256 b = in(_t); return QByteArray((char const*)&b, sizeof(eth::h256)); }
Q_INVOKABLE QVariant fromHex(QString _s) const { return out((eth::u256)eth::h256(_s.toStdString())); }
Q_INVOKABLE QVariant fromAddress(QVariant/*eth::Address*/ _a) const { return out((eth::u160)to<eth::Address>(_a)); }
Q_INVOKABLE QVariant toAddress(QVariant/*eth::Address*/ _a) const { return toQJS<eth::Address>((eth::u160)in(_a)); }
Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); }
};
class KeyHelper: public QObject
{
Q_OBJECT
public:
KeyHelper(QObject* _p = nullptr): QObject(_p) {}
static eth::Address in(QVariant const& _s) { return to<eth::Address>(_s); }
static QVariant out(eth::Address const& _s) { return toQJS(_s); }
Q_INVOKABLE QVariant/*eth::KeyPair*/ create() const { return toQJS(eth::KeyPair::create()); }
Q_INVOKABLE QVariant/*eth::Address*/ address(QVariant/*eth::KeyPair*/ _p) const { return out(to<eth::KeyPair>(_p).address()); }
Q_INVOKABLE QVariant/*eth::Secret*/ secret(QVariant/*eth::KeyPair*/ _p) const { return toQJS(to<eth::KeyPair>(_p).secret()); }
Q_INVOKABLE QVariant/*eth::KeyPair*/ keypair(QVariant/*eth::Secret*/ _k) const { return toQJS(eth::KeyPair(to<eth::Secret>(_k))); }
Q_INVOKABLE bool isNull(QVariant/*eth::Address*/ _a) const { return !in(_a); }
Q_INVOKABLE QVariant/*eth::Address*/ addressOf(QString _s) const { return out(eth::Address(_s.toStdString())); }
Q_INVOKABLE QString stringOf(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(eth::toHex(in(_a).asArray())); }
Q_INVOKABLE QString toAbridged(QVariant/*eth::Address*/ _a) const { return QString::fromStdString(in(_a).abridged()); }
};
class BytesHelper: public QObject
{
Q_OBJECT
public:
BytesHelper(QObject* _p = nullptr): QObject(_p) {}
Q_INVOKABLE QByteArray concat(QVariant _v, QVariant _w) const
{
QByteArray ba;
if (_v.type() == QVariant::ByteArray)
ba = _v.toByteArray();
else
ba = _v.toString().toLatin1();
QByteArray ba2;
if (_w.type() == QVariant::ByteArray)
ba2 = _w.toByteArray();
else
ba2 = _w.toString().toLatin1();
ba.append(ba2);
return QByteArray(ba);
}
Q_INVOKABLE QByteArray concat(QByteArray _v, QByteArray _w) const
{
_v.append(_w);
return _v;
}
Q_INVOKABLE QByteArray fromString(QString _s) const
{
return _s.toLatin1();
}
Q_INVOKABLE QByteArray fromString(QString _s, unsigned _padding) const
{
QByteArray b = _s.toLatin1();
for (unsigned i = b.size(); i < _padding; ++i)
b.append((char)0);
b.resize(_padding);
return b;
}
Q_INVOKABLE QString toString(QByteArray _b) const
{
while (_b.size() && !_b[_b.size() - 1])
_b.resize(_b.size() - 1);
return QString::fromLatin1(_b);
}
Q_INVOKABLE QVariant u256of(QByteArray _s) const
{
while (_s.size() < 32)
_s.append((char)0);
eth::h256 ret((uint8_t const*)_s.data(), eth::h256::ConstructFromPointer);
return toQJS<eth::u256>(ret);
}
};
#endif

1
neth/main.cpp

@ -417,7 +417,6 @@ int main(int argc, char** argv)
if (!clientName.empty())
clientName += "/";
Client c("NEthereum(++)/" + clientName + "v" + eth::EthVersion + "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath);
c.start();
cout << credits();
std::ostringstream ccout;

6
walleth/MainWin.cpp

@ -63,7 +63,6 @@ Main::Main(QWidget *parent) :
g_qmlMain = this;
m_client.reset(new Client("Walleth", Address(), eth::getDataDir() + "/Walleth"));
m_client->start();
g_qmlClient = m_client.get();
@ -105,8 +104,6 @@ Main::Main(QWidget *parent) :
connect(m_refreshNetwork, SIGNAL(timeout()), SLOT(refreshNetwork()));
m_refreshNetwork->start(1000);
connect(this, SIGNAL(changed()), SLOT(refresh()));
connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r)
{
m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts);
@ -135,8 +132,7 @@ Main::~Main()
void Main::timerEvent(QTimerEvent *)
{
if (m_client->changed())
changed();
}
void Main::on_about_triggered()

5
walleth/MainWin.h

@ -6,7 +6,7 @@
#include <QtCore/QMutex>
#include <QtWidgets/QMainWindow>
#include <libethcore/CommonEth.h>
#include <libqethereum/QEthereum.h>
#include <libqethereum/QmlEthereum.h>
namespace Ui {
class Main;
@ -43,9 +43,6 @@ private slots:
void refresh();
void refreshNetwork();
signals:
void changed();
protected:
virtual void timerEvent(QTimerEvent *);

Loading…
Cancel
Save