From 7524128cf2b6f1e82324006816fd0bf4b47fc6e0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 9 Aug 2014 18:21:01 +0100 Subject: [PATCH] Better accounts. "Third" browser. --- CMakeLists.txt | 1 + alethzero/MainWin.cpp | 22 +- alethzero/MainWin.h | 7 +- libethcore/BlockInfo.cpp | 2 +- libethcore/CommonEth.cpp | 2 +- libethcore/Exceptions.h | 2 +- libethereum/Client.h | 8 +- libqethereum/QEthereum.cpp | 8 +- stdserv.js | 31 +- third/CMakeLists.txt | 100 ++++++ third/Main.ui | 480 ++++++++++++++++++++++++++++ third/MainWin.cpp | 629 +++++++++++++++++++++++++++++++++++++ third/MainWin.h | 129 ++++++++ third/main.cpp | 11 + 14 files changed, 1403 insertions(+), 29 deletions(-) create mode 100644 third/CMakeLists.txt create mode 100644 third/Main.ui create mode 100644 third/MainWin.cpp create mode 100644 third/MainWin.h create mode 100644 third/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ce9fb6d4..1ca9a9c37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -358,6 +358,7 @@ if (NOT LANGUAGES) add_subdirectory(libqethereum) add_subdirectory(alethzero) + add_subdirectory(third) if(QTQML) add_subdirectory(walleth) endif() diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index efa026b66..c9d8e22ca 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -663,10 +663,18 @@ void Main::refreshBalances() // update all the balance-dependent stuff. ui->ourAccounts->clear(); u256 totalBalance = 0; - map> altCoins; + map> 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); + { + auto n = m_client->stateAt(coinsAddr, i + 1); + auto addr = right160(m_client->stateAt(coinsAddr, n)); + auto denom = m_client->stateAt(coinsAddr, sha3(h256(n).asBytes())); + if (denom == 0) + denom = 1; + cdebug << n << addr << denom << sha3(h256(n).asBytes()); + altCoins[addr] = make_tuple(fromRaw(n), 0, denom); + } for (auto i: m_myKeys) { u256 b = m_client->balanceAt(i.address()); @@ -675,13 +683,17 @@ void Main::refreshBalances() totalBalance += b; for (auto& c: altCoins) - c.second.second += (u256)m_client->stateAt(c.first, (u160)i.address()); + get<1>(c.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() + " | "; + if (get<1>(c.second)) + { + stringstream s; + s << setw(toString(get<2>(c.second) - 1).size()) << setfill('0') << (get<1>(c.second) % get<2>(c.second)); + b += QString::fromStdString(toString(get<1>(c.second) / get<2>(c.second)) + "." + s.str() + " ") + get<0>(c.second).toUpper() + " | "; + } ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance))); } diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 825e05456..f54a64053 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -159,8 +159,7 @@ private: void alterDebugStateGroup(bool _enable) const; void updateFee(); - void readSettings(bool _skipGeometry); - void readSettings() { readSettings(false); } + void readSettings(bool _skipGeometry = false); void writeSettings(); bool isCreation() const; @@ -205,10 +204,6 @@ private: unsigned m_balancesFilter = (unsigned)-1; QByteArray m_peers; - QMutex m_guiLock; - QTimer* m_ticker; - QTimer* m_refreshNetwork; - QTimer* m_refreshMining; QStringList m_servers; QList m_myKeys; bool m_keysChanged = false; diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 172583271..2fdee90c7 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -176,7 +176,7 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const throw InvalidDifficulty(); if (gasLimit != calculateGasLimit(_parent)) - throw InvalidGasLimit(); + throw InvalidGasLimit(gasLimit, calculateGasLimit(_parent)); // Check timestamp is after previous timestamp. if (parentHash) diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 08d9d2de4..336829c2f 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -29,7 +29,7 @@ using namespace eth; //#define ETH_ADDRESS_DEBUG 1 -const unsigned eth::c_protocolVersion = 25; +const unsigned eth::c_protocolVersion = 26; const unsigned eth::c_databaseVersion = 1; static const vector> g_units = diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index 127a84450..475ab1f05 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -26,7 +26,7 @@ class InvalidStateRoot: public Exception {}; class InvalidTransactionsHash: public Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual std::string description() const { return "Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref()); } }; class InvalidTransaction: public Exception {}; class InvalidDifficulty: public Exception {}; -class InvalidGasLimit: public Exception {}; +class InvalidGasLimit: public Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual std::string description() const { return "Invalid gas limit (provided: " + toString(provided) + " valid:" + toString(valid) + ")"; } }; class InvalidMinGasPrice: public Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual std::string description() const { return "Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")"; } }; class InvalidTransactionGasUsed: public Exception {}; class InvalidTransactionStateRoot: public Exception {}; diff --git a/libethereum/Client.h b/libethereum/Client.h index 1d6b072ce..f20b3538f 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -152,11 +152,11 @@ struct ClientWatch unsigned changes = 1; }; -struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 6; }; +struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; #define cwatch eth::LogOutputStream() -struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 5; }; -struct WorkOutChannel: public LogChannel { static const char* name() { return "W>"; } static const int verbosity = 6; }; +struct WorkOutChannel: public LogChannel { static const char* name() { return "() #define cworkin eth::LogOutputStream() #define cworkout eth::LogOutputStream() diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index 06a6a485b..ecc312d07 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -409,7 +409,13 @@ void QEthereum::doTransact(QString _json) return; TransactionSkeleton t = toTransaction(_json); if (!t.from && m_accounts.size()) - t.from = m_accounts[0].secret(); + { + auto b = m_accounts.first(); + for (auto a: m_accounts) + if (client()->balanceAt(KeyPair(a).address()) > client()->balanceAt(KeyPair(b).address())) + b = a; + t.from = b.secret(); + } if (!t.gasPrice) t.gasPrice = 10 * eth::szabo; if (!t.gas) diff --git a/stdserv.js b/stdserv.js index b9006ae76..d0eefc709 100644 --- a/stdserv.js +++ b/stdserv.js @@ -54,9 +54,20 @@ env.note('NameReg at address ' + nameReg) env.note('Register NameReg...') eth.transact(eth.key, '0', config, "0".pad(32) + nameReg.pad(32), 10000, eth.gasPrice); -var coinsCode = eth.lll(" +var dnsRegCode = '0x60006000546000600053602001546000600053604001546020604060206020600073661005d2720d855f1d9976f88bb10c1a3398c77f6103e8f17f7265676973746572000000000000000000000000000000000000000000000000600053606001600060200201547f446e735265670000000000000000000000000000000000000000000000000000600053606001600160200201546000600060006000604060606000600053604001536103e8f1327f6f776e65720000000000000000000000000000000000000000000000000000005761011663000000e46000396101166000f20060006000547f72656769737465720000000000000000000000000000000000000000000000006000602002350e0f630000006d596000600160200235560e0f630000006c59600032560e0f0f6300000057596000325657600260200235600160200235576001602002353257007f64657265676973746572000000000000000000000000000000000000000000006000602002350e0f63000000b95960016020023532560e0f63000000b959600032576000600160200235577f6b696c6c000000000000000000000000000000000000000000000000000000006000602002350e0f630000011559327f6f776e6572000000000000000000000000000000000000000000000000000000560e0f63000001155932ff00'; + +var dnsReg; +env.note('Create DnsReg...') +eth.create(eth.key, '0', dnsRegCode, 10000, eth.gasPrice, function(a) { dnsReg = a; }) + +env.note('DnsReg at address ' + dnsReg) + +env.note('Register DnsReg...') +eth.transact(eth.key, '0', config, "4".pad(32) + dnsReg.pad(32), 10000, eth.gasPrice); + +var coinRegCode = eth.lll(" { -[0]'register [32]'Coins +[0]'register [32]'CoinReg (msg allgas " + nameReg + " 0 0 64) (returnlll { (def 'name $0) @@ -72,14 +83,14 @@ var coinsCode = eth.lll(" } "); -var coins; -env.note('Create Coins...') -eth.create(eth.key, '0', coinsCode, 10000, eth.gasPrice, function(a) { coins = a; }) +var coinReg; +env.note('Create CoinReg...') +eth.create(eth.key, '0', coinRegCode, 10000, eth.gasPrice, function(a) { coinReg = a; }) -env.note('Coins at address ' + coins) +env.note('CoinReg at address ' + coinReg) -env.note('Register Coins...') -eth.transact(eth.key, '0', config, "1".pad(32) + coins.pad(32), 10000, eth.gasPrice); +env.note('Register CoinReg...') +eth.transact(eth.key, '0', config, "1".pad(32) + coinReg.pad(32), 10000, eth.gasPrice); var gavCoinCode = eth.lll(" { @@ -89,8 +100,8 @@ var gavCoinCode = eth.lll(" [0]'register [32]'GavCoin (msg allgas " + nameReg + " 0 0 64) -[0]'GAV [32]'1000 -(msg allgas " + coins + " 0 0 64) +[0]'GAV [32] 1000 +(msg allgas " + coinReg + " 0 0 64) (returnlll { (when (&& (= $0 'kill) (= (caller) @@0x69)) (suicide (caller))) diff --git a/third/CMakeLists.txt b/third/CMakeLists.txt new file mode 100644 index 000000000..3385fc265 --- /dev/null +++ b/third/CMakeLists.txt @@ -0,0 +1,100 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) +aux_source_directory(. SRC_LIST) +include_directories(..) + +if (APPLE) + # Add homebrew path for qt5 + set(CMAKE_PREFIX_PATH /usr/local/opt/qt5) + include_directories(/usr/local/opt/qt5/include /usr/local/include) +elseif ("${TARGET_PLATFORM}" STREQUAL "w64") + set(SRC_LIST ${SRC_LIST} ../windows/qt_plugin_import.cpp) + include_directories(/usr/x86_64-w64-mingw32/include /usr/x86_64-w64-mingw32/include/QtCore /usr/x86_64-w64-mingw32/include/QtGui /usr/x86_64-w64-mingw32/include/QtQuick /usr/x86_64-w64-mingw32/include/QtQml /usr/x86_64-w64-mingw32/include/QtNetwork /usr/x86_64-w64-mingw32/include/QtWidgets /usr/x86_64-w64-mingw32/include/QtWebKit /usr/x86_64-w64-mingw32/include/QtWebKitWidgets) +elseif (UNIX) + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ";$ENV{QTDIR}/lib/cmake") +endif () + +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Quick REQUIRED) +find_package(Qt5Qml REQUIRED) +find_package(Qt5Network REQUIRED) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5WebKit REQUIRED) +find_package(Qt5WebKitWidgets REQUIRED) + +qt5_wrap_ui(ui_Main.h Main.ui) + +# Set name of binary and add_executable() +if (APPLE) + set(EXECUTEABLE Third) + set(BIN_INSTALL_DIR ".") + set(DOC_INSTALL_DIR ".") + + set(PROJECT_VERSION "${ETH_VERSION}") + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") + set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTEABLE}) + set(MACOSX_BUNDLE_ICON_FILE third) + include(BundleUtilities) + + add_executable(${EXECUTEABLE} MACOSX_BUNDLE third.icns Main.ui ${SRC_LIST}) + set_target_properties(${EXECUTEABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in") + SET_SOURCE_FILES_PROPERTIES(${EXECUTEABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) + SET_SOURCE_FILES_PROPERTIES(${MACOSX_BUNDLE_ICON_FILE}.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + +else () + set(EXECUTEABLE third) + add_executable(${EXECUTEABLE} Main.ui ${SRC_LIST}) +endif () + +qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets) +target_link_libraries(${EXECUTEABLE} qethereum ethereum evm ethcore secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface ethential) + +if (APPLE) + # First have qt5 install plugins and frameworks + add_custom_command(TARGET ${EXECUTEABLE} POST_BUILD + COMMAND /usr/local/opt/qt5/bin/macdeployqt ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTEABLE}.app + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # This tool and next will inspect linked libraries in order to determine which dependencies are required + if (${CMAKE_CFG_INTDIR} STREQUAL ".") + set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXECUTEABLE}.app") + else () + set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTEABLE}.app") + endif () + install(CODE " + include(BundleUtilities) + set(BU_CHMOD_BUNDLE_ITEMS 1) + fixup_bundle(\"${APP_BUNDLE_PATH}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\") + " COMPONENT RUNTIME ) + # Cleanup duplicate libs from macdeployqt + install(CODE " + file(GLOB LINGER_RM \"${APP_BUNDLE_PATH}/Contents/Frameworks/*.dylib\") + if (LINGER_RM) + file(REMOVE \${LINGER_RM}) + endif () + ") +elseif ("${TARGET_PLATFORM}" STREQUAL "w64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-keep-inline-dllexport -static-libgcc -static-libstdc++ -static") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-s -Wl,-subsystem,windows -mthreads -L/usr/x86_64-w64-mingw32/plugins/platforms") + target_link_libraries(${EXECUTEABLE} gcc) + target_link_libraries(${EXECUTEABLE} mingw32 qtmain mswsock iphlpapi qwindows shlwapi Qt5PlatformSupport opengl32 gdi32 comdlg32 oleaut32 imm32 winmm ole32 uuid ws2_32) + target_link_libraries(${EXECUTEABLE} boost_system-mt-s) + target_link_libraries(${EXECUTEABLE} boost_filesystem-mt-s) + target_link_libraries(${EXECUTEABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTEABLE} crypt32) + target_link_libraries(${EXECUTEABLE} Qt5PlatformSupport) + set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) +elseif (UNIX) +else () + target_link_libraries(${EXECUTEABLE} boost_system) + target_link_libraries(${EXECUTEABLE} boost_filesystem) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTEABLE} ${CMAKE_THREAD_LIBS_INIT}) + install( TARGETS ${EXECUTEABLE} RUNTIME DESTINATION bin ) +endif () + diff --git a/third/Main.ui b/third/Main.ui new file mode 100644 index 000000000..52ad30e7b --- /dev/null +++ b/third/Main.ui @@ -0,0 +1,480 @@ + + + Main + + + + 0 + 0 + 1711 + 1138 + + + + Third + + + true + + + QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs + + + true + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 0 wei + + + + + + + 0 peers + + + + + + + 1 block + + + + + + + + + + + + + + 0 + + + true + + + true + + + + Tab 1 + + + + 0 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + + + + about:blank + + + + + + + + + + + + + + + + 0 + 0 + 1711 + 25 + + + + + &File + + + + + + T&ools + + + + + + + + + + &Help + + + + + + &Network + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + QDockWidget::DockWidgetFeatureMask + + + Owned Accounts + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::NoFocus + + + QFrame::NoFrame + + + QAbstractItemView::InternalMove + + + true + + + + + + + + + &Quit + + + Ctrl+Q + + + + + true + + + true + + + Use &UPnP + + + + + &Connect to Peer... + + + + + true + + + Enable &Network + + + + + true + + + &Mine + + + + + &New Address + + + + + &About... + + + + + true + + + &Preview + + + + + false + + + &Step Over + + + F10 + + + + + true + + + Mining &Paranoia + + + + + &Kill Blockchain + + + + + &Import Key... + + + + + &Export Selected Key... + + + + + &Inject Transaction + + + + + true + + + Show Ancient &Blocks + + + + + true + + + Show Anonymous &Accounts + + + + + true + + + Use &Past Peers + + + + + &Load Javascript... + + + + + false + + + Step Over &Backwards + + + Ctrl+F10 + + + + + true + + + &Force Mining + + + + + false + + + Standard with &Storage... + + + + + false + + + Step &Into + + + F11 + + + + + false + + + Step &Out + + + Shift+F11 + + + + + false + + + S&tandard... + + + + + false + + + Step Into Backwards + + + Ctrl+F11 + + + + + false + + + Step Out Backwards + + + Ctrl+Shift+F11 + + + + + false + + + Debu&g Current Transaction + + + + + false + + + D&ump Current Transaction State (post) + + + + + false + + + D&ump Current Transaction State (pre) + + + + + false + + + &Pretty... + + + + + &Refresh + + + + + &New Tab + + + + + + + QWebView + QWidget +
QtWebKitWidgets/QWebView
+
+
+ + tabWidget + urlEdit + + + +
diff --git a/third/MainWin.cpp b/third/MainWin.cpp new file mode 100644 index 000000000..784752959 --- /dev/null +++ b/third/MainWin.cpp @@ -0,0 +1,629 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file MainWin.cpp + * @author Gav Wood + * @date 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#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::BlockChain; +using eth::PeerInfo; +using eth::RLP; +using eth::Secret; +using eth::Transaction; +using eth::Executive; + +// functions +using eth::toHex; +using eth::compileLLL; +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; +using eth::operator<<; + +// vars +using eth::g_logPost; +using eth::g_logVerbosity; +using eth::c_instructionInfo; + +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) : + QMainWindow(parent), + ui(new Ui::Main) +{ + setWindowFlags(Qt::Window); + ui->setupUi(this); + + cerr << "State root: " << BlockChain::genesis().stateRoot << endl; + cerr << "Block Hash: " << sha3(BlockChain::createGenesisBlock()) << endl; + cerr << "Block RLP: " << RLP(BlockChain::createGenesisBlock()) << endl; + cerr << "Block Hex: " << toHex(BlockChain::createGenesisBlock()) << endl; + cerr << "Network protocol version: " << eth::c_protocolVersion << endl; + cerr << "Client database version: " << eth::c_databaseVersion << endl; + + ui->ownedAccountsDock->hide(); + + statusBar()->addPermanentWidget(ui->balance); + statusBar()->addPermanentWidget(ui->peerCount); + statusBar()->addPermanentWidget(ui->mineStatus); + statusBar()->addPermanentWidget(ui->blockCount); + + connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); + + m_client.reset(new Client("Third")); + + connect(ui->webView, &QWebView::loadStarted, [this]() + { + // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. + m_ethereum = new QEthereum(this, m_client.get(), owned()); + + QWebFrame* f = ui->webView->page()->mainFrame(); + f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); + auto qeth = m_ethereum; + connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, qeth, this)); + }); + + connect(ui->webView, &QWebView::loadFinished, [=]() + { + m_ethereum->poll(); + }); + + connect(ui->webView, &QWebView::titleChanged, [=]() + { + ui->tabWidget->setTabText(0, ui->webView->title()); + }); + + readSettings(); + + installWatches(); + + startTimer(100); + + { + QSettings s("ethereum", "third"); + 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 is 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() +{ + // Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor) + // *after* the client is dead. + m_ethereum->clientDieing(); + + writeSettings(); +} + +void Main::onKeysChanged() +{ + installBalancesWatch(); +} + +unsigned Main::installWatch(eth::MessageFilter const& _tf, std::function const& _f) +{ + auto ret = m_client->installWatch(_tf); + m_handlers[ret] = _f; + return ret; +} + +unsigned Main::installWatch(eth::h256 _tf, std::function const& _f) +{ + auto ret = m_client->installWatch(_tf); + m_handlers[ret] = _f; + return ret; +} + +void Main::installWatches() +{ + installWatch(eth::MessageFilter().altered(c_config, 0), [=](){ installNameRegWatch(); }); + installWatch(eth::MessageFilter().altered(c_config, 1), [=](){ installCurrenciesWatch(); }); + installWatch(eth::ChainChangedFilter, [=](){ onNewBlock(); }); +} + +void Main::installNameRegWatch() +{ + m_client->uninstallWatch(m_nameRegFilter); + m_nameRegFilter = installWatch(eth::MessageFilter().altered((u160)m_client->stateAt(c_config, 0)), [=](){ onNameRegChange(); }); +} + +void Main::installCurrenciesWatch() +{ + m_client->uninstallWatch(m_currenciesFilter); + m_currenciesFilter = installWatch(eth::MessageFilter().altered((u160)m_client->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); }); +} + +void Main::installBalancesWatch() +{ + eth::MessageFilter tf; + + vector
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() +{ + cwatch << "NameReg changed!"; + + // update any namereg-dependent stuff - for now force a full update. + refreshAll(); +} + +void Main::onCurrenciesChange() +{ + cwatch << "Currencies changed!"; + installBalancesWatch(); + + // TODO: update any currency-dependent stuff? +} + +void Main::onBalancesChange() +{ + cwatch << "Our balances changed!"; + + refreshBalances(); +} + +void Main::onNewBlock() +{ + cwatch << "Blockchain changed!"; + + // update blockchain dependent views. + refreshBlockCount(); +} + +void Main::note(QString _s) +{ + cnote << _s.toStdString(); +} + +void Main::debug(QString _s) +{ + cdebug << _s.toStdString(); +} + +void Main::warn(QString _s) +{ + cwarn << _s.toStdString(); +} + +void Main::eval(QString const& _js) +{ + if (_js.trimmed().isEmpty()) + return; + ui->webView->page()->currentFrame()->evaluateJavaScript("___RET=(" + _js + ")"); +} + +QString Main::pretty(eth::Address _a) const +{ + h256 n; + + if (h160 nameReg = (u160)m_client->stateAt(c_config, 0)) + n = m_client->stateAt(nameReg, (u160)(_a)); + + return fromRaw(n); +} + +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 +{ + if (_a == "(Create Contract)") + return Address(); + + 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)m_client->stateAt(c_config, 0)) + if (h256 a = m_client->stateAt(nameReg, n)) + return right160(a); + } + if (_a.size() == 40) + return Address(fromHex(_a.toStdString())); + else + return Address(); +} + +QString Main::lookup(QString const& _a) const +{ + if (!_a.endsWith(".eth")) + return _a; + + string sn = _a.mid(0, _a.size() - 4).toStdString(); + if (sn.size() > 32) + sn = sha3(sn, false); + h256 n; + memcpy(n.data(), sn.data(), sn.size()); + +/* string sn2 = _a.toStdString(); + if (sn2.size() > 32) + sn2 = sha3(sn2, false); + h256 n2; + memcpy(n2.data(), sn2.data(), sn2.size()); +*/ + + h256 ret; + if (h160 dnsReg = (u160)m_client->stateAt(c_config, 4, 0)) + ret = m_client->stateAt(dnsReg, n); +/* if (!ret) + if (h160 nameReg = (u160)m_client->stateAt(c_config, 0, 0)) + ret = m_client->stateAt(nameReg, n2); +*/ + if (ret && !((u256)ret >> 32)) + return QString("%1.%2.%3.%4").arg((int)ret[28]).arg((int)ret[29]).arg((int)ret[30]).arg((int)ret[31]); + // TODO: support IPv6. + else if (ret) + return fromRaw(ret); + else + return _a; +} + +void Main::on_about_triggered() +{ + QMessageBox::about(this, "About Third PoC-" + QString(eth::EthVersion).section('.', 1, 1), QString("Third/v") + eth::EthVersion + "/" 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\nThanks to the various contributors including: Alex Leverington, Tim Hughes, caktux, Eric Lombrozo, Marko Simovic."); +} + +void Main::writeSettings() +{ + QSettings s("ethereum", "third"); + 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("url", ui->urlEdit->text()); + + bytes d = m_client->savePeers(); + if (d.size()) + m_peers = QByteArray((char*)d.data(), (int)d.size()); + s.setValue("peers", m_peers); + + s.setValue("geometry", saveGeometry()); + s.setValue("windowState", saveState()); +} + +void Main::readSettings(bool _skipGeometry) +{ + QSettings s("ethereum", "third"); + + if (!_skipGeometry) + restoreGeometry(s.value("geometry").toByteArray()); + restoreState(s.value("windowState").toByteArray()); + + m_myKeys.clear(); + 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)); + if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k))) + m_myKeys.append(KeyPair(k)); + } + } + m_client->setAddress(m_myKeys.back().address()); + m_peers = s.value("peers").toByteArray(); + ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html + on_urlEdit_returnPressed(); +} + +void Main::on_importKey_triggered() +{ + QString s = QInputDialog::getText(this, "Import Account Key", "Enter account's secret key"); + bytes b = fromHex(s.toStdString()); + if (b.size() == 32) + { + auto k = KeyPair(h256(b)); + if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end()) + { + m_myKeys.append(k); + refreshBalances(); + } + else + QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); + } + else + QMessageBox::warning(this, "Invalid Entry", "Could not import the secret key; invalid key entered. Make sure it is 64 hex characters (0-9 or A-F)."); +} + +void Main::on_exportKey_triggered() +{ + if (ui->ourAccounts->currentRow() >= 0 && ui->ourAccounts->currentRow() < m_myKeys.size()) + { + auto k = m_myKeys[ui->ourAccounts->currentRow()]; + QMessageBox::information(this, "Export Account Key", "Secret key to account " + render(k.address()) + " is:\n" + QString::fromStdString(toHex(k.sec().ref()))); + } +} + +void Main::on_urlEdit_returnPressed() +{ + QString s = ui->urlEdit->text(); + QRegExp r("([a-z]+://)?([^/]*)(.*)"); + if (r.exactMatch(s)) + if (r.cap(2).isEmpty()) + s = (r.cap(1).isEmpty() ? "file://" : r.cap(1)) + r.cap(3); + else + s = (r.cap(1).isEmpty() ? "http://" : r.cap(1)) + lookup(r.cap(2)) + r.cap(3); + else{} + qDebug() << s; + ui->webView->setUrl(s); +} + +void Main::refreshMining() +{ + eth::MineProgress p = m_client->miningProgress(); + ui->mineStatus->setText(m_client->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); +} + +void Main::refreshBalances() +{ + cwatch << "refreshBalances()"; + // update all the balance-dependent stuff. + ui->ourAccounts->clear(); + u256 totalBalance = 0; + map> 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(); + + ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); +} + +void Main::refreshAll() +{ + refreshBlockCount(); + refreshBalances(); +} + +void Main::refreshBlockCount() +{ + cwatch << "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)); +} + +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) + refreshMining(); + + // refresh peer list every 1000ms, reset counter + if (interval == 1000) + { + interval = 0; + ensureNetwork(); + refreshNetwork(); + } + else + interval += 100; + + if (m_ethereum) + m_ethereum->poll(); + + for (auto const& i: m_handlers) + if (m_client->checkWatch(i.first)) + i.second(); +} + +void Main::ourAccountsRowsMoved() +{ + QList myKeys; + for (int i = 0; i < ui->ourAccounts->count(); ++i) + { + auto hba = ui->ourAccounts->item(i)->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + for (auto i: m_myKeys) + if (i.address() == h) + myKeys.push_back(i); + } + m_myKeys = myKeys; + if (m_ethereum) + m_ethereum->setAccounts(myKeys); +} + +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::ensureNetwork() +{ + string n = string("Third/v") + eth::EthVersion; + n += "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM); + m_client->setClientVersion(n); + + int pocnumber = QString(eth::EthVersion).section('.', 1, 1).toInt(); + string defPeer; + if (pocnumber == 5) + defPeer = "54.72.69.180"; + else if (pocnumber == 6) + defPeer = "54.76.56.74"; + + if (!m_client->haveNetwork()) + m_client->startNetwork(30303, defPeer); + else + if (!m_client->peerCount()) + m_client->connect(defPeer); + if (m_peers.size()) + m_client->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); +} + +void Main::on_connect_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_mine_triggered() +{ + if (ui->mine->isChecked()) + { + m_client->setAddress(m_myKeys.last().address()); + m_client->startMining(); + } + else + m_client->stopMining(); +} + +// extra bits needed to link on VS +#ifdef _MSC_VER + +// include moc file, ofuscated to hide from automoc +#include\ +"moc_MainWin.cpp" + +#include\ +"moc_MiningView.cpp" + +#endif diff --git a/third/MainWin.h b/third/MainWin.h new file mode 100644 index 000000000..4706b5158 --- /dev/null +++ b/third/MainWin.h @@ -0,0 +1,129 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file MainWin.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { +class Main; +} + +namespace eth { +class Client; +class State; +class MessageFilter; +} + +class QQuickView; + +class Main : public QMainWindow +{ + Q_OBJECT + +public: + explicit Main(QWidget *parent = 0); + ~Main(); + + eth::Client* client() { return m_client.get(); } + + QList const& owned() const { return m_myKeys; } + +public slots: + void note(QString _entry); + void debug(QString _entry); + void warn(QString _entry); + void eval(QString const& _js); + + void onKeysChanged(); + +private slots: + void on_mine_triggered(); + void on_ourAccounts_doubleClicked(); + void ourAccountsRowsMoved(); + void on_about_triggered(); + void on_connect_triggered(); + void on_quit_triggered() { close(); } + void on_urlEdit_returnPressed(); + void on_importKey_triggered(); + void on_exportKey_triggered(); + +signals: + void poll(); + +private: + QString pretty(eth::Address _a) const; + QString render(eth::Address _a) const; + eth::Address fromString(QString const& _a) const; + QString lookup(QString const& _n) const; + + void readSettings(bool _skipGeometry = false); + void writeSettings(); + + unsigned installWatch(eth::MessageFilter const& _tf, std::function const& _f); + unsigned installWatch(eth::h256 _tf, std::function const& _f); + + void onNewBlock(); + void onNameRegChange(); + void onCurrenciesChange(); + void onBalancesChange(); + + void installWatches(); + void installCurrenciesWatch(); + void installNameRegWatch(); + void installBalancesWatch(); + + virtual void timerEvent(QTimerEvent*); + void ensureNetwork(); + + void refreshAll(); + void refreshBlockCount(); + void refreshBalances(); + void refreshNetwork(); + void refreshMining(); + + std::unique_ptr ui; + + std::unique_ptr m_client; + + QList m_myKeys; + + std::map> m_handlers; + unsigned m_nameRegFilter = (unsigned)-1; + unsigned m_currenciesFilter = (unsigned)-1; + unsigned m_balancesFilter = (unsigned)-1; + + QByteArray m_peers; + QStringList m_servers; + + QNetworkAccessManager m_webCtrl; + + QEthereum* m_ethereum = nullptr; +}; diff --git a/third/main.cpp b/third/main.cpp new file mode 100644 index 000000000..42afd5e66 --- /dev/null +++ b/third/main.cpp @@ -0,0 +1,11 @@ +#include "MainWin.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + Main w; + w.show(); + + return a.exec(); +}