Browse Source

Walleth initial commit.

cl-refactor
Gav Wood 11 years ago
parent
commit
ac8f044669
  1. 1
      CMakeLists.txt
  2. 4
      TODO
  3. 107
      walleth/CMakeLists.txt
  4. 186
      walleth/Main.ui
  5. 316
      walleth/MainWin.cpp
  6. 76
      walleth/MainWin.h
  7. 11
      walleth/main.cpp

1
CMakeLists.txt

@ -149,6 +149,7 @@ add_subdirectory(test)
add_subdirectory(eth)
if (NOT HEADLESS)
add_subdirectory(alethzero)
add_subdirectory(walleth)
endif ()
unset(HEADLESS CACHE)

4
TODO

@ -1,5 +1,7 @@
### UP FOR GRABS
BUG: Nonce sometimes jumps one.
Tests
- Use standard tests.
@ -10,6 +12,8 @@ Crypto stuff:
Network:
- *** Exponential backoff on bad connection.
- *** Handle exception when no network.
- *** Only download blocks from one peer at once.
- Download in parallel, but stripe.
- NotInChain will be very bad for new peers - it'll run through until the genesis.
- Check how many it has first.
- Crypto on network - use id as public key?

107
walleth/CMakeLists.txt

@ -0,0 +1,107 @@
cmake_policy(SET CMP0015 NEW)
if ("${TARGET_PLATFORM}" STREQUAL "w64")
cmake_policy(SET CMP0020 NEW)
endif ()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST)
include_directories(..)
link_directories(../libethereum)
# Find Qt5 for Apple and update src_list for windows
if (APPLE)
# homebrew defaults to qt4 and installs qt5 as 'keg-only'
# which places it into /usr/local/opt insteadof /usr/local.
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)
endif ()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Quick REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5Network REQUIRED)
qt5_wrap_ui(ui_Main.h Main.ui)
# Set name of binary and add_executable()
if (APPLE)
set(EXECUTEABLE Walleth)
set(CMAKE_INSTALL_PREFIX ./)
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})
include(BundleUtilities)
add_executable(${EXECUTEABLE} MACOSX_BUNDLE Main.ui ${SRC_LIST})
else ()
set(EXECUTEABLE walleth)
add_executable(${EXECUTEABLE} Main.ui ${SRC_LIST})
endif ()
qt5_use_modules(${EXECUTEABLE} Core Gui Widgets Network Quick Qml)
target_link_libraries(${EXECUTEABLE} ethereum secp256k1 ${CRYPTOPP_LIBRARIES})
if (APPLE)
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)
# This is a workaround for when the build-type defaults to Debug, and when a multi-config generator like xcode is used, where the type
# will not be set but defaults to release.
set(generator_lowercase "${CMAKE_GENERATOR}")
string(TOLOWER "${CMAKE_GENERATOR}" generator_lowercase)
if (generator_lowercase STREQUAL "xcode")
# TODO: Not sure how to resolve this. Possibly \${TARGET_BUILD_DIR}
set(binary_build_dir "${CMAKE_CURRENT_BINARY_DIR}/Debug")
else ()
set(binary_build_dir "${CMAKE_CURRENT_BINARY_DIR}")
endif ()
set(APPS ${binary_build_dir}/${EXECUTEABLE}.app)
# This tool and the next will automatically looked at the linked libraries in order to determine what dependencies are required. Thus, target_link_libaries only needs to add ethereum and secp256k1 (above)
install(CODE "
include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS 1)
fixup_bundle(\"${APPS}\" \"${BUNDLELIBS}\" \"../libethereum ../secp256k1\")
" COMPONENT RUNTIME )
add_custom_target(addframeworks ALL
COMMAND /usr/local/opt/qt5/bin/macdeployqt ${binary_build_dir}/${EXECUTEABLE}.app
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
DEPENDS ${PROJECT_NAME}
)
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 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} Qt5PlatformSupport)
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)
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 ()

186
walleth/Main.ui

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Main</class>
<widget class="QMainWindow" name="Main">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>562</width>
<height>488</height>
</rect>
</property>
<property name="windowTitle">
<string>AlethZero Ethereum Client</string>
</property>
<property name="dockNestingEnabled">
<bool>true</bool>
</property>
<property name="dockOptions">
<set>QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs</set>
</property>
<property name="sizeGripEnabled" stdset="0">
<bool>true</bool>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="fullDisplay">
<item>
<widget class="QLabel" name="balance">
<property name="text">
<string>0 wei</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="peerCount">
<property name="text">
<string>0 peers</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="blockCount">
<property name="text">
<string>1 block</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>562</width>
<height>20</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
<property name="title">
<string>&amp;File</string>
</property>
<addaction name="quit"/>
</widget>
<widget class="QMenu" name="menu_Network">
<property name="title">
<string>&amp;Network</string>
</property>
<addaction name="upnp"/>
<addaction name="net"/>
<addaction name="connect"/>
</widget>
<widget class="QMenu" name="menu_Tools">
<property name="title">
<string>T&amp;ools</string>
</property>
<addaction name="mine"/>
<addaction name="create"/>
<addaction name="preview"/>
</widget>
<widget class="QMenu" name="menu_Help">
<property name="title">
<string>&amp;Help</string>
</property>
<addaction name="about"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Network"/>
<addaction name="menu_Tools"/>
<addaction name="menu_Help"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="quit">
<property name="text">
<string>&amp;Quit</string>
</property>
</action>
<action name="upnp">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Use &amp;UPnP</string>
</property>
</action>
<action name="connect">
<property name="text">
<string>&amp;Connect to Peer...</string>
</property>
</action>
<action name="net">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Enable &amp;Network</string>
</property>
</action>
<action name="mine">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Mine</string>
</property>
</action>
<action name="create">
<property name="text">
<string>&amp;New Address</string>
</property>
</action>
<action name="about">
<property name="text">
<string>&amp;About...</string>
</property>
</action>
<action name="preview">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Preview</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>destination</tabstop>
<tabstop>calculatedName</tabstop>
<tabstop>value</tabstop>
<tabstop>valueUnits</tabstop>
<tabstop>data</tabstop>
<tabstop>code</tabstop>
<tabstop>send</tabstop>
<tabstop>idealPeers</tabstop>
<tabstop>port</tabstop>
<tabstop>clientName</tabstop>
<tabstop>verbosity</tabstop>
<tabstop>transactionQueue</tabstop>
<tabstop>accounts</tabstop>
<tabstop>peers</tabstop>
<tabstop>log</tabstop>
<tabstop>ourAccounts</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

316
walleth/MainWin.cpp

@ -0,0 +1,316 @@
#include <QtNetwork/QNetworkReply>
#include <QtQuick/QQuickView>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QInputDialog>
#include <QtGui/QClipboard>
#include <QtCore/QtCore>
#include <libethereum/Dagger.h>
#include <libethereum/Client.h>
#include <libethereum/Instruction.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::asHex;
using eth::assemble;
using eth::compileLisp;
using eth::disassemble;
using eth::formatBalance;
using eth::fromUserHex;
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;
#define ADD_QUOTES_HELPER(s) #s
#define ADD_QUOTES(s) ADD_QUOTES_HELPER(s)
Main::Main(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Main)
{
setWindowFlags(Qt::Window);
ui->setupUi(this);
m_client.reset(new Client("Walleth"));
/*
ui->librariesView->setModel(m_libraryMan);
ui->graphsView->setModel(m_graphMan);
setWindowIcon(QIcon(":/Noted.png"));
qmlRegisterSingletonType<TimeHelper>("com.llr", 1, 0, "Time", TimelineItem::constructTimeHelper);
qmlRegisterType<GraphItem>("com.llr", 1, 0, "Graph");
qmlRegisterType<CursorGraphItem>("com.llr", 1, 0, "CursorGraph");
qmlRegisterType<IntervalItem>("com.llr", 1, 0, "Interval");
qmlRegisterType<CursorItem>("com.llr", 1, 0, "Cursor");
qmlRegisterType<TimelinesItem>("com.llr", 1, 0, "Timelines");
qmlRegisterType<TimeLabelsItem>("com.llr", 1, 0, "TimeLabels");
qmlRegisterType<XLabelsItem>("com.llr", 1, 0, "XLabels");
qmlRegisterType<XScaleItem>("com.llr", 1, 0, "XScale");
qmlRegisterType<YLabelsItem>("com.llr", 1, 0, "YLabels");
qmlRegisterType<YScaleItem>("com.llr", 1, 0, "YScale");
*/
m_view = new QQuickView();
QQmlContext* context = m_view->rootContext();
/* context->setContextProperty("libs", libs());
context->setContextProperty("compute", compute());
context->setContextProperty("data", data());
context->setContextProperty("graphs", graphs());
context->setContextProperty("audio", audio());
context->setContextProperty("view", view());
m_view->setSource(QUrl("qrc:/Noted.qml"));
*/
QWidget* w = QWidget::createWindowContainer(m_view);
w->setAcceptDrops(true);
m_view->setResizeMode(QQuickView::SizeRootObjectToView);
ui->fullDisplay->insertWidget(0, w);
m_view->create();
/*
m_timelinesItem = m_view->rootObject()->findChild<TimelinesItem*>("timelines");
qDebug() << m_view->rootObject();
*/
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(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r)
{
m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts);
if (m_servers.size())
{
ui->net->setChecked(true);
on_net_triggered(true);
}
});
QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc3.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));
statusBar()->addPermanentWidget(ui->balance);
statusBar()->addPermanentWidget(ui->peerCount);
statusBar()->addPermanentWidget(ui->blockCount);
}
Main::~Main()
{
writeSettings();
}
void Main::on_about_triggered()
{
QMessageBox::about(this, "About Walleth PoC-" + QString(ADD_QUOTES(ETH_VERSION)).section('.', 1, 1), "AlethZero/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Tim Hughes, Eric Lombrozo, Marko Simovic, Alex Leverington and several others.");
}
void Main::writeSettings()
{
QSettings s("ethereum", "walleth");
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", m_clientName);
s.setValue("idealPeers", m_idealPeers);
s.setValue("port", m_port);
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("geometry", saveGeometry());
s.setValue("windowState", saveState());
}
void Main::readSettings()
{
QSettings s("ethereum", "walleth");
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());
m_clientName = s.value("clientName", "").toString();
m_idealPeers = s.value("idealPeers", 5).toInt();
m_port = s.value("port", 30303).toInt();
}
void Main::refreshNetwork()
{
auto ps = m_client->peers();
ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)");
}
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)));
}
if (c || m_keysChanged || _override)
{
m_keysChanged = false;
u256 totalBalance = 0;
for (auto i: m_myKeys)
{
u256 b = st.balance(i.address());
totalBalance += b;
}
ui->balance->setText(QString::fromStdString(formatBalance(totalBalance)));
}
m_client->unlock();
}
void Main::on_net_triggered(bool _auto)
{
string n = "Walleth/v" ADD_QUOTES(ETH_VERSION);
if (m_clientName.size())
n += "/" + m_clientName.toStdString();
n += "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM);
m_client->setClientVersion(n);
if (ui->net->isChecked())
{
if (_auto)
{
QString s = m_servers[rand() % m_servers.size()];
m_client->startNetwork(m_port, s.section(':', 0, 0).toStdString(), s.section(':', 1).toInt(), NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked());
}
else
m_client->startNetwork(m_port, string(), 0, NodeMode::Full, m_idealPeers, 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_mine_triggered()
{
if (ui->mine->isChecked())
{
m_client->setAddress(m_myKeys.last().address());
m_client->startMining();
}
else
m_client->stopMining();
}
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

76
walleth/MainWin.h

@ -0,0 +1,76 @@
#ifndef MAIN_H
#define MAIN_H
#include <QtNetwork/QNetworkAccessManager>
#include <QtCore/QAbstractListModel>
#include <QtCore/QMutex>
#include <QtWidgets/QMainWindow>
#include <libethereum/Common.h>
namespace Ui {
class Main;
}
namespace eth {
class Client;
class State;
}
class QQuickView;
class Main : public QMainWindow
{
Q_OBJECT
public:
explicit Main(QWidget *parent = 0);
~Main();
private slots:
void on_connect_triggered();
void on_mine_triggered();
void on_create_triggered();
void on_net_triggered(bool _auto = false);
void on_about_triggered();
void on_preview_triggered() { refresh(true); }
void on_quit_triggered() { close(); }
void refresh(bool _override = false);
void refreshNetwork();
private:
/* QString pretty(eth::Address _a) const;
QString render(eth::Address _a) const;
eth::Address fromString(QString const& _a) const;
*/
eth::State const& state() const;
void updateFee();
void readSettings();
void writeSettings();
eth::u256 fee() const;
eth::u256 total() const;
eth::u256 value() const;
std::unique_ptr<Ui::Main> ui;
std::unique_ptr<eth::Client> m_client;
QByteArray m_peers;
QMutex m_guiLock;
QTimer* m_refresh;
QTimer* m_refreshNetwork;
QVector<eth::KeyPair> m_myKeys;
bool m_keysChanged = false;
int m_port;
int m_idealPeers;
QString m_clientName;
QStringList m_servers;
QQuickView* m_view;
QNetworkAccessManager m_webCtrl;
};
#endif // MAIN_H

11
walleth/main.cpp

@ -0,0 +1,11 @@
#include "MainWin.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Main w;
w.show();
return a.exec();
}
Loading…
Cancel
Save