#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; // functions using eth::toHex; using eth::assemble; using eth::compileLisp; 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; static void initUnits(QComboBox* _b) { for (auto n = (::uint)units().size(); n-- != 0; ) _b->addItem(QString::fromStdString(units()[n].second), n); } Main::Main(QWidget *parent) : QMainWindow(parent), ui(new Ui::Main) { 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")); readSettings(); refresh(); 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 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); } Main::~Main() { g_logPost = simpleDebugOut; writeSettings(); } QString Main::pretty(eth::Address _a) const { if (h256 n = state().contractMemory(m_nameReg, (h256)(u256)(u160)_a)) { 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 (h256 a = state().contractMemory(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()); if (s.value("nameReg").toString() == "11f62328e131dbb05ce4c73a3de3c7ab1c84a163") s.remove("nameReg"); ui->nameReg->setText(s.value("nameReg", "8ff91e5b145a23ab1afef34f12587c18bd42aec0").toString()); } void Main::on_nameReg_textChanged() { string s = ui->nameReg->text().toStdString(); if (s.size() == 40) { m_nameReg = Address(fromHex(s)); refresh(true); } } 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) { 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(t.sha3()))) .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(t.sha3()))) .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; 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; } ui->balance->setText(QString::fromStdString(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()); h256 th = tx.sha3(); s << "

" << th << "

"; s << "

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

"; auto ss = tx.safeSender(); 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) << ""; if (tx.isCreation) { s << "
Storage:   "; s << "
" << disassemble(tx.storage); } else { s << "
Gas: " << tx.gas << ""; s << "
Data:    0x..." << setw(2) << setfill('0') << hex; unsigned c = 0; for (auto i: tx.data) s << i << (c % 8 ? "" : " "); } } 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 mem = state().contractMemory(h); u256 next = 0; unsigned numerics = 0; bool unexpectedNumeric = false; for (auto const& i: mem) { if (next < i.first) { unsigned j; for (j = 0; j <= numerics && next + j < i.first; ++j) s << (j < numerics || unexpectedNumeric ? " 0" : " STOP"); unexpectedNumeric = false; numerics -= min(numerics, j); if (next + j < i.first) s << " ...
@" << showbase << hex << i.first << "    "; } else if (!next) s << "@" << showbase << hex << i.first << "    "; auto iit = c_instructionInfo.find((Instruction)(unsigned)i.second); if (numerics || iit == c_instructionInfo.end() || (u256)(unsigned)iit->first != i.second) // not an instruction or expecting an argument... { if (numerics) numerics--; else unexpectedNumeric = true; s << " " << showbase << hex << i.second; } else { auto const& ii = iit->second; s << " " << ii.name << ""; numerics = ii.additional; } next = i.first + 1; } 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_storage = code[0] == '(' ? compileLisp(code, true) : assemble(code, true); ui->code->setPlainText(QString::fromStdString(disassemble(m_storage))); ui->gas->setValue((qint64)state().createGas(m_storage.size())); ui->gas->setEnabled(false); } else { string code = ui->data->toPlainText().replace(" ", "").toStdString(); try { m_data = fromHex(code); } catch (...) {} ui->code->setPlainText(QString::fromStdString(toHex(m_data))); if (m_client->postState().isContractAddress(fromString(ui->destination->text()))) { ui->gas->setMinimum((qint64)state().callGas(m_data.size(), 1)); ui->gas->setEnabled(true); } else { 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 { return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; } u256 Main::gasPrice() const { 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 (isCreation()) m_client->transact(s, value(), gasPrice(), m_storage); else m_client->transact(s, value(), gasPrice(), fromString(ui->destination->text()), ui->gas->value(), m_data); 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; } // 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