#include #include #include #include #include #include #include #include #include #include #include #include #include "BuildInfo.h" #include "MainWin.h" #include "ui_Main.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::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; using eth::Executive; // functions using eth::toHex; using eth::assemble; using eth::compileLisp; using eth::disassemble; using eth::formatBalance; using eth::fromHex; using eth::sha3; using eth::left160; 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 QEthereums 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. Main* g_main = nullptr; QEthereum::QEthereum(QObject* _p): QObject(_p) { connect(g_main, SIGNAL(changed()), SIGNAL(changed())); } QEthereum::~QEthereum() { } Client* QEthereum::client() const { return g_main->client(); } QVariant QEthereum::coinbase() const { return toQJS(client()->address()); } QVariant QEthereum::account() const { if (g_main->owned().empty()) return toQJS(Address()); return toQJS(g_main->owned()[0].address()); } QList QEthereum::accounts() const { QList ret; for (auto i: g_main->owned()) ret.push_back(toQJS(i.address())); return ret; } QVariant QEthereum::key() const { if (g_main->owned().empty()) return toQJS(KeyPair()); return toQJS(g_main->owned()[0]); } QList QEthereum::keys() const { QList ret; for (auto i: g_main->owned()) ret.push_back(toQJS(i)); return ret; } void QEthereum::setCoinbase(QVariant _a) { if (client()->address() != to
(_a)) { client()->setAddress(to
(_a)); changed(); } } QAccount::QAccount(QObject*) { } QAccount::~QAccount() { } void QAccount::setEthereum(QEthereum* _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(); } QVariant QAccount::balance() const { if (m_eth) return toQJS(m_eth->balanceAt(m_address)); return toQJS(0); } double QAccount::txCount() const { if (m_eth) return m_eth->txCountAt(m_address); return 0; } bool QAccount::isContract() const { if (m_eth) return m_eth->isContractAt(m_address); return 0; } QVariant QEthereum::balanceAt(QVariant _a) const { return toQJS(client()->postState().balance(to
(_a))); } QVariant QEthereum::storageAt(QVariant _a, QVariant _p) const { return toQJS(client()->postState().contractStorage(to
(_a), to(_p))); } u256 QEthereum::balanceAt(Address _a) const { return client()->postState().balance(_a); } bool QEthereum::isContractAt(QVariant _a) const { return client()->postState().isContractAddress(to
(_a)); } bool QEthereum::isContractAt(Address _a) const { return client()->postState().isContractAddress(_a); } bool QEthereum::isMining() const { return client()->isMining(); } bool QEthereum::isListening() const { return client()->haveNetwork(); } void QEthereum::setMining(bool _l) { if (_l) client()->startMining(); else client()->stopMining(); } void QEthereum::setListening(bool _l) { if (_l) client()->startNetwork(); else client()->stopNetwork(); } double QEthereum::txCountAt(QVariant _a) const { return (double)client()->postState().transactionsFrom(to
(_a)); } double QEthereum::txCountAt(Address _a) const { return (double)client()->postState().transactionsFrom(_a); } unsigned QEthereum::peerCount() const { return (unsigned)client()->peerCount(); } QVariant QEthereum::create(QVariant _secret, QVariant _amount, QByteArray _code, QByteArray _init, QVariant _gas, QVariant _gasPrice) { return toQJS(client()->transact(to(_secret), to(_amount), bytes(_code.data(), _code.data() + _code.size()), bytes(_init.data(), _init.data() + _init.size()), to(_gas), to(_gasPrice))); } void QEthereum::transact(QVariant _secret, QVariant _amount, QVariant _dest, QByteArray _data, QVariant _gas, QVariant _gasPrice) { client()->transact(to(_secret), to(_amount), to
(_dest), bytes(_data.data(), _data.data() + _data.size()), to(_gas), to(_gasPrice)); } static void initUnits(QComboBox* _b) { for (auto n = (::uint)units().size(); n-- != 0; ) _b->addItem(QString::fromStdString(units()[n].second), n); } string htmlDump(bytes const& _b, unsigned _w = 8) { stringstream ret; ret << "
";
	for (unsigned i = 0; i < _b.size(); i += _w)
	{
		ret << hex << setw(4) << setfill('0') << i << " ";
		for (unsigned j = i; j < i + _w; ++j)
			if (j < _b.size())
				if (_b[j] >= 32 && _b[j] < 128)
					ret << (char)_b[j];
				else ret << '?';
			else
				ret << ' ';
		ret << " ";
		for (unsigned j = i; j < i + _w && j < _b.size(); ++j)
			ret << setfill('0') << setw(2) << hex << (unsigned)_b[j] << " ";
		ret << "\n";
	}
	ret << "
"; return ret.str(); } Address c_config = Address("ccdeac59d35627b7de09332e819d5159e7bb7250"); Main::Main(QWidget *parent) : QMainWindow(parent), ui(new Ui::Main) { g_main = this; setWindowFlags(Qt::Window); ui->setupUi(this); g_logPost = [=](std::string const& s, char const* c) { simpleDebugOut(s, c); ui->log->addItem(QString::fromStdString(s)); }; m_client.reset(new Client("AlethZero")); m_refresh = new QTimer(this); connect(m_refresh, SIGNAL(timeout()), SLOT(refresh())); m_refresh->start(100); m_refreshNetwork = new QTimer(this); connect(m_refreshNetwork, SIGNAL(timeout()), SLOT(refreshNetwork())); m_refreshNetwork->start(1000); connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); #if ETH_DEBUG m_servers.append("192.168.0.10:30301"); #else int pocnumber = QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1).toInt(); if (pocnumber == 3) m_servers.push_back("54.201.28.117:30303"); else if (pocnumber == 4) m_servers.push_back("54.72.31.55:30303"); else { connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r) { m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); }); QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString::number(pocnumber) + ".txt")); r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); m_webCtrl.get(r); srand(time(0)); } #endif ui->configDock->close(); on_verbosity_sliderMoved(); initUnits(ui->gasPriceUnits); initUnits(ui->valueUnits); ui->valueUnits->setCurrentIndex(6); ui->gasPriceUnits->setCurrentIndex(4); ui->gasPrice->setValue(10); on_destination_textChanged(); statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->blockCount); connect(ui->webView, &QWebView::titleChanged, [=]() { ui->tabWidget->setTabText(0, ui->webView->title()); }); connect(ui->webView, &QWebView::loadFinished, [=]() { this->changed(); }); QWebFrame* f = ui->webView->page()->currentFrame(); connect(f, &QWebFrame::javaScriptWindowObjectCleared, [=](){ f->addToJavaScriptWindowObject("eth", new QEthereum, QWebFrame::ScriptOwnership); f->addToJavaScriptWindowObject("u256", new U256Helper, QWebFrame::ScriptOwnership); f->addToJavaScriptWindowObject("key", new KeyHelper, QWebFrame::ScriptOwnership); f->addToJavaScriptWindowObject("bytes", new BytesHelper, QWebFrame::ScriptOwnership); }); readSettings(); refresh(); { QSettings s("ethereum", "alethzero"); if (s.value("splashMessage", true).toBool()) { QMessageBox::information(this, "Here Be Dragons!", "This is proof-of-concept software. The project as a whole is not even at the alpha-testing stage. It here to show you, if you have a technical bent, the sort of thing that might be possible down the line.\nPlease don't blame us if it does something unexpected or if you're underwhelmed with the user-experience. We have great plans for it in terms of UX down the line but right now we just want to get the groundwork sorted. We welcome contributions, be they in code, testing or documentation!\nAfter you close this message it won't appear again."); s.setValue("splashMessage", false); } } } Main::~Main() { g_logPost = simpleDebugOut; writeSettings(); } QString Main::pretty(eth::Address _a) const { h256 n; if (h160 nameReg = (u160)state().contractStorage(c_config, 0)) n = state().contractStorage(nameReg, (u160)(_a)); if (!n) n = state().contractStorage(m_nameReg, (u160)(_a)); if (n) { std::string s((char const*)n.data(), 32); if (s.find_first_of('\0') != string::npos) s.resize(s.find_first_of('\0')); return QString::fromStdString(s); } return QString(); } QString Main::render(eth::Address _a) const { QString p = pretty(_a); if (!p.isNull()) return p + " (" + QString::fromStdString(_a.abridged()) + ")"; return QString::fromStdString(_a.abridged()); } Address Main::fromString(QString const& _a) const { string sn = _a.toStdString(); if (sn.size() > 32) sn.resize(32); h256 n; memcpy(n.data(), sn.data(), sn.size()); memset(n.data() + sn.size(), 0, 32 - sn.size()); if (_a.size()) { if (h160 nameReg = (u160)state().contractStorage(c_config, 0)) if (h256 a = state().contractStorage(nameReg, n)) return right160(a); if (h256 a = state().contractStorage(m_nameReg, n)) return right160(a); } if (_a.size() == 40) return Address(fromHex(_a.toStdString())); else return Address(); } void Main::on_about_triggered() { QMessageBox::about(this, "About AlethZero PoC-" + QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1), QString("AlethZero/v" ETH_QUOTED(ETH_VERSION) "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM) "\n" ETH_QUOTED(ETH_COMMIT_HASH)) + (ETH_CLEAN_REPO ? "\nCLEAN" : "\n+ LOCAL CHANGES") + "\n\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Eric Lombrozo, Marko Simovic, Alex Leverington, Tim Hughes and several others."); } void Main::writeSettings() { QSettings s("ethereum", "alethzero"); QByteArray b; b.resize(sizeof(Secret) * m_myKeys.size()); auto p = b.data(); for (auto i: m_myKeys) { memcpy(p, &(i.secret()), sizeof(Secret)); p += sizeof(Secret); } s.setValue("address", b); s.setValue("upnp", ui->upnp->isChecked()); s.setValue("clientName", ui->clientName->text()); s.setValue("idealPeers", ui->idealPeers->value()); s.setValue("port", ui->port->value()); if (m_client->peerServer()) { bytes d = m_client->peerServer()->savePeers(); m_peers = QByteArray((char*)d.data(), (int)d.size()); } s.setValue("peers", m_peers); s.setValue("nameReg", ui->nameReg->text()); s.setValue("geometry", saveGeometry()); s.setValue("windowState", saveState()); } void Main::readSettings() { QSettings s("ethereum", "alethzero"); restoreGeometry(s.value("geometry").toByteArray()); restoreState(s.value("windowState").toByteArray()); QByteArray b = s.value("address").toByteArray(); if (b.isEmpty()) m_myKeys.append(KeyPair::create()); else { h256 k; for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) { memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); m_myKeys.append(KeyPair(k)); } } m_client->setAddress(m_myKeys.back().address()); m_peers = s.value("peers").toByteArray(); ui->upnp->setChecked(s.value("upnp", true).toBool()); ui->clientName->setText(s.value("clientName", "").toString()); ui->idealPeers->setValue(s.value("idealPeers", ui->idealPeers->value()).toInt()); ui->port->setValue(s.value("port", ui->port->value()).toInt()); ui->nameReg->setText(s.value("NameReg", "").toString()); ui->urlEdit->setText(s.value("url", "http://gavwood.com/gavcoin.html").toString()); on_urlEdit_editingFinished(); } void Main::on_urlEdit_editingFinished() { ui->webView->setUrl(ui->urlEdit->text()); } void Main::on_nameReg_textChanged() { string s = ui->nameReg->text().toStdString(); if (s.size() == 40) { m_nameReg = Address(fromHex(s)); refresh(true); } else m_nameReg = Address(); } void Main::refreshNetwork() { auto ps = m_client->peers(); ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); ui->peers->clear(); for (PeerInfo const& i: ps) ui->peers->addItem(QString("%3 ms - %1:%2 - %4").arg(i.host.c_str()).arg(i.port).arg(chrono::duration_cast(i.lastPing).count()).arg(i.clientVersion.c_str())); } eth::State const& Main::state() const { return ui->preview->isChecked() ? m_client->postState() : m_client->state(); } void Main::refresh(bool _override) { m_client->lock(); auto const& st = state(); bool c = m_client->changed(); if (c || _override) { changed(); auto d = m_client->blockChain().details(); auto diff = BlockInfo(m_client->blockChain().block()).difficulty; ui->blockCount->setText(QString("#%1 @%3 T%2").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff))); 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) { (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.isContractAddress(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)); } } 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.isContractAddress(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); } ui->blocks->clear(); auto const& bc = m_client->blockChain(); for (auto h = bc.currentHash(); h != bc.genesisHash(); h = bc.details(h).parent) { auto d = bc.details(h); QListWidgetItem* blockItem = new QListWidgetItem(QString("#%1 %2").arg(d.number).arg(h.abridged().c_str()), ui->blocks); blockItem->setData(Qt::UserRole, QByteArray((char const*)h.data(), h.size)); int n = 0; for (auto const& i: RLP(bc.block(h))[1]) { Transaction t(i.data()); 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.isContractAddress(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); QListWidgetItem* txItem = new QListWidgetItem(s, ui->blocks); txItem->setData(Qt::UserRole, QByteArray((char const*)h.data(), h.size)); txItem->setData(Qt::UserRole + 1, n); n++; } } } if (c || m_keysChanged || _override) { m_keysChanged = false; ui->ourAccounts->clear(); u256 totalBalance = 0; u256 totalGavCoinBalance = 0; Address gavCoin("91a10664d0cd489085a7a018beb5245d4f2272f1"); 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; totalGavCoinBalance += st.contractStorage(gavCoin, (u160)i.address()); } ui->balance->setText(QString::fromStdString(toString(totalGavCoinBalance) + " GAV | " + formatBalance(totalBalance))); } m_client->unlock(); } void Main::ourAccountsRowsMoved() { QVector myKeys; for (int i = 0; i < ui->ourAccounts->count(); ++i) { auto hba = ui->ourAccounts->item(i)->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); for (auto i: m_myKeys) if (i.address() == h) myKeys.push_back(i); } m_myKeys = myKeys; } void Main::on_blocks_currentItemChanged() { ui->info->clear(); m_client->lock(); if (auto item = ui->blocks->currentItem()) { auto hba = item->data(Qt::UserRole).toByteArray(); assert(hba.size() == 32); auto h = h256((byte const*)hba.data(), h256::ConstructFromPointer); auto details = m_client->blockChain().details(h); auto blockData = m_client->blockChain().block(h); auto block = RLP(blockData); BlockInfo info(blockData); stringstream s; if (item->data(Qt::UserRole + 1).isNull()) { char timestamp[64]; time_t rawTime = (time_t)(uint64_t)info.timestamp; strftime(timestamp, 64, "%c", localtime(&rawTime)); s << "

" << h << "

"; s << "

#" << details.number; s << "   " << timestamp << "

"; s << "
D/TD: 2^" << log2((double)info.difficulty) << "/2^" << log2((double)details.totalDifficulty) << ""; s << "   Children: " << details.children.size() << ""; s << "
Coinbase: " << pretty(info.coinbaseAddress).toStdString() << " " << info.coinbaseAddress; s << "
State: " << info.stateRoot << ""; s << "
Nonce: " << info.nonce << ""; s << "
Transactions: " << block[1].itemCount() << " @" << info.sha3Transactions << ""; s << "
Uncles: " << block[2].itemCount() << " @" << info.sha3Uncles << ""; } else { unsigned txi = item->data(Qt::UserRole + 1).toInt(); Transaction tx(block[1][txi].data()); auto ss = tx.safeSender(); h256 th = sha3(rlpList(ss, tx.nonce)); s << "

" << th << "

"; s << "

" << h << "[" << txi << "]

"; s << "
From: " << pretty(ss).toStdString() << " " << ss; if (tx.isCreation()) s << "
Creates: " << pretty(right160(th)).toStdString() << " " << right160(th); else s << "
To: " << pretty(tx.receiveAddress).toStdString() << " " << tx.receiveAddress; s << "
Value: " << formatBalance(tx.value) << ""; s << "   #" << tx.nonce << ""; s << "
Gas price: " << formatBalance(tx.gasPrice) << ""; s << "
Gas: " << tx.gas << ""; if (tx.isCreation()) { if (tx.init.size()) s << "

Init

" << disassemble(tx.init); if (tx.data.size()) s << "

Body

" << disassemble(tx.data); } else { if (tx.data.size()) s << htmlDump(tx.data, 16); } } ui->info->appendHtml(QString::fromStdString(s.str())); } m_client->unlock(); } void Main::on_contracts_currentItemChanged() { ui->contractInfo->clear(); m_client->lock(); if (auto item = ui->contracts->currentItem()) { auto hba = item->data(Qt::UserRole).toByteArray(); assert(hba.size() == 20); auto h = h160((byte const*)hba.data(), h160::ConstructFromPointer); stringstream s; auto storage = state().contractStorage(h); for (auto const& i: storage) s << "@" << showbase << hex << i.first << "    " << showbase << hex << i.second << "
"; s << "

Body Code

" << disassemble(state().contractCode(h)); ui->contractInfo->appendHtml(QString::fromStdString(s.str())); } m_client->unlock(); } void Main::on_idealPeers_valueChanged() { if (m_client->peerServer()) m_client->peerServer()->setIdealPeerCount(ui->idealPeers->value()); } void Main::on_ourAccounts_doubleClicked() { auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); } void Main::on_log_doubleClicked() { qApp->clipboard()->setText(ui->log->currentItem()->text()); } void Main::on_accounts_doubleClicked() { auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); } void Main::on_contracts_doubleClicked() { auto hba = ui->contracts->currentItem()->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); } void Main::on_destination_textChanged() { if (ui->destination->text().size()) if (Address a = fromString(ui->destination->text())) ui->calculatedName->setText(render(a)); else ui->calculatedName->setText("Unknown Address"); else ui->calculatedName->setText("Create Contract"); on_data_textChanged(); // updateFee(); } void Main::on_data_textChanged() { if (isCreation()) { string code = ui->data->toPlainText().toStdString(); m_init.clear(); m_data = compileLisp(code, true, m_init); ui->code->setHtml((m_init.size() ? "

Init

" + QString::fromStdString(disassemble(m_init)).toHtmlEscaped() : "") + "

Body

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped()); ui->gas->setMinimum((qint64)state().createGas(m_data.size() + m_init.size(), 0)); if (!ui->gas->isEnabled()) ui->gas->setValue(m_backupGas); ui->gas->setEnabled(true); } else { m_data.clear(); QString s = ui->data->toPlainText(); while (s.size()) { QRegExp r("@?\"(.*)\"(.*)"); QRegExp h("@?(0x)?(([a-fA-F0-9][a-fA-F0-9])+)(.*)"); if (r.exactMatch(s)) { for (auto i: r.cap(1)) m_data.push_back((byte)i.toLatin1()); if (s[0] == '@') for (int i = r.cap(1).size(); i < 32; ++i) m_data.push_back(0); else m_data.push_back(0); s = r.cap(2); } else if (h.exactMatch(s)) { bytes bs = fromHex(h.cap(2).toStdString()); if (s[0] == '@') for (auto i = bs.size(); i < 32; ++i) m_data.push_back(0); for (auto b: bs) m_data.push_back(b); s = h.cap(4); } else s = s.mid(1); } ui->code->setHtml(QString::fromStdString(htmlDump(m_data))); if (m_client->postState().isContractAddress(fromString(ui->destination->text()))) { ui->gas->setMinimum((qint64)state().callGas(m_data.size(), 1)); if (!ui->gas->isEnabled()) ui->gas->setValue(m_backupGas); ui->gas->setEnabled(true); } else { if (ui->gas->isEnabled()) m_backupGas = ui->gas->value(); ui->gas->setValue((qint64)state().callGas(m_data.size())); ui->gas->setEnabled(false); } } updateFee(); } bool Main::isCreation() const { return ui->destination->text().isEmpty()/* || !ui->destination->text().toInt()*/; } u256 Main::fee() const { return ui->gas->value() * gasPrice(); } u256 Main::value() const { if (ui->valueUnits->currentIndex() == -1) return 0; return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; } u256 Main::gasPrice() const { if (ui->gasPriceUnits->currentIndex() == -1) return 0; return ui->gasPrice->value() * units()[units().size() - 1 - ui->gasPriceUnits->currentIndex()].first; } u256 Main::total() const { return value() + fee(); } void Main::updateFee() { ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str())); auto totalReq = total(); ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); bool ok = false; for (auto i: m_myKeys) if (state().balance(i.address()) >= totalReq) { ok = true; break; } ui->send->setEnabled(ok); QPalette p = ui->total->palette(); p.setColor(QPalette::WindowText, QColor(ok ? 0x00 : 0x80, 0x00, 0x00)); ui->total->setPalette(p); } void Main::on_net_triggered() { ui->port->setEnabled(!ui->net->isChecked()); ui->clientName->setEnabled(!ui->net->isChecked()); string n = "AlethZero/v" ETH_QUOTED(ETH_VERSION); if (ui->clientName->text().size()) n += "/" + ui->clientName->text().toStdString(); n += "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM); m_client->setClientVersion(n); if (ui->net->isChecked()) { m_client->startNetwork(ui->port->value(), string(), 0, NodeMode::Full, ui->idealPeers->value(), std::string(), ui->upnp->isChecked()); if (m_peers.size()) m_client->peerServer()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); } else m_client->stopNetwork(); } void Main::on_connect_triggered() { if (!ui->net->isChecked()) { ui->net->setChecked(true); on_net_triggered(); } bool ok = false; QString s = QInputDialog::getItem(this, "Connect to a Network Peer", "Enter a peer to which a connection may be made:", m_servers, m_servers.count() ? rand() % m_servers.count() : 0, true, &ok); if (ok && s.contains(":")) { string host = s.section(":", 0, 0).toStdString(); unsigned short port = s.section(":", 1).toInt(); m_client->connect(host, port); } } void Main::on_verbosity_sliderMoved() { g_logVerbosity = ui->verbosity->value(); } void Main::on_mine_triggered() { if (ui->mine->isChecked()) { m_client->setAddress(m_myKeys.last().address()); m_client->startMining(); } else m_client->stopMining(); } void Main::on_send_clicked() { u256 totalReq = value() + fee(); m_client->lock(); for (auto i: m_myKeys) if (m_client->state().balance(i.address()) >= totalReq) { m_client->unlock(); Secret s = i.secret(); if (ui->enableDebug->isChecked()) { m_client->lock(); m_executiveState = state(); m_client->unlock(); m_currentExecution = unique_ptr(new Executive(m_executiveState)); Transaction t; t.nonce = m_executiveState.transactionsFrom(toAddress(s)); t.value = value(); t.gasPrice = gasPrice(); t.gas = ui->gas->value(); t.data = m_data; if (isCreation()) { t.receiveAddress = Address(); t.init = m_init; } else { t.receiveAddress = fromString(ui->destination->text()); t.data = m_data; } t.sign(s); auto r = t.rlp(); m_currentExecution->setup(&r); initDebugger(); updateDebugger(); } else { if (isCreation()) m_client->transact(s, value(), m_data, m_init, ui->gas->value(), gasPrice()); else m_client->transact(s, value(), fromString(ui->destination->text()), m_data, ui->gas->value(), gasPrice()); refresh(); } return; } m_client->unlock(); statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount."); } void Main::on_create_triggered() { m_myKeys.append(KeyPair::create()); m_keysChanged = true; } void Main::on_enableDebug_triggered() { ui->debugPanel->setEnabled(ui->enableDebug->isChecked()); ui->send->setText(ui->enableDebug->isChecked() ? "D&ebug" : "&Execute"); } void Main::on_debugStep_triggered() { if (!m_currentExecution) return; if (m_currentExecution->go(1)) debugFinished(); else updateDebugger(); } void Main::on_debugContinue_triggered() { if (!m_currentExecution) return; if (m_currentExecution->go()) debugFinished(); else updateDebugger(); } void Main::debugFinished() { m_currentExecution.reset(); ui->debugCode->clear(); ui->debugStack->clear(); ui->debugMemory->setHtml(""); ui->debugStorage->setHtml(""); ui->debugStateInfo->setText(""); ui->send->setEnabled(true); ui->enableDebug->setEnabled(true); ui->debugStep->setEnabled(false); ui->debugContinue->setEnabled(false); ui->debugPanel->setEnabled(false); } void Main::initDebugger() { ui->send->setEnabled(false); ui->enableDebug->setEnabled(false); ui->debugStep->setEnabled(true); ui->debugContinue->setEnabled(true); ui->debugPanel->setEnabled(true); ui->debugCode->setEnabled(false); QListWidget* dc = ui->debugCode; dc->clear(); if (m_currentExecution) { for (unsigned i = 0; i <= m_currentExecution->ext().code.size(); ++i) { byte b = i < m_currentExecution->ext().code.size() ? m_currentExecution->ext().code[i] : 0; QString s = c_instructionInfo.at((Instruction)b).name; m_pcWarp[i] = dc->count(); ostringstream out; out << hex << setw(4) << setfill('0') << i; if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32) { unsigned bc = b - (byte)Instruction::PUSH1 + 1; s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&m_currentExecution->ext().code[i + 1], bc))); i += bc; } dc->addItem(QString::fromStdString(out.str()) + " " + s); } } } void Main::updateDebugger() { QListWidget* ds = ui->debugStack; ds->clear(); if (m_currentExecution) { eth::VM const& vm = m_currentExecution->vm(); for (auto i: vm.stack()) ds->insertItem(0, QString::fromStdString(toHex(((h256)i).asArray()))); ui->debugMemory->setHtml(QString::fromStdString(htmlDump(vm.memory(), 16))); ui->debugCode->setCurrentRow(m_pcWarp[(unsigned)vm.curPC()]); ostringstream ss; ss << hex << "PC: 0x" << vm.curPC() << " | GAS: 0x" << vm.gas(); ui->debugStateInfo->setText(QString::fromStdString(ss.str())); stringstream s; auto storage = m_currentExecution->state().contractStorage(m_currentExecution->ext().myAddress); for (auto const& i: storage) s << "@" << showbase << hex << i.first << "    " << showbase << hex << i.second << "
"; ui->debugStorage->setHtml(QString::fromStdString(s.str())); } } // extra bits needed to link on VS #ifdef _MSC_VER // include moc file, ofuscated to hide from automoc #include\ "moc_MainWin.cpp" // specify library dependencies, it's easier to do here than in the project since we can control the "d" debug suffix #ifdef _DEBUG #define QTLIB(x) x"d.lib" #else #define QTLIB(x) x".lib" #endif #pragma comment(lib, QTLIB("Qt5PlatformSupport")) #pragma comment(lib, QTLIB("Qt5Core")) #pragma comment(lib, QTLIB("Qt5GUI")) #pragma comment(lib, QTLIB("Qt5Widgets")) #pragma comment(lib, QTLIB("Qt5Network")) #pragma comment(lib, QTLIB("qwindows")) #pragma comment(lib, "Imm32.lib") #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "winmm.lib") #endif