diff --git a/socket_py/credentials.py b/socket_py/credentials.py new file mode 100644 index 0000000..63d6f7f --- /dev/null +++ b/socket_py/credentials.py @@ -0,0 +1,3 @@ +nodeAddr = "Address of node" +privateKey = "Ethereum private key" +address = "Ethereum address belonging to private key" \ No newline at end of file diff --git a/socket_py/qt/Socket_python.pro b/socket_py/qt/Socket_python.pro new file mode 100644 index 0000000..7dfd315 --- /dev/null +++ b/socket_py/qt/Socket_python.pro @@ -0,0 +1,40 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2019-08-20T08:38:25 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = Socket_python +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +CONFIG += c++11 + +SOURCES += \ + main.cpp \ + mainwindow.cpp + +HEADERS += \ + mainwindow.h + +FORMS += \ + mainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/socket_py/qt/Socket_python.pro.user b/socket_py/qt/Socket_python.pro.user new file mode 100644 index 0000000..02a9aa3 --- /dev/null +++ b/socket_py/qt/Socket_python.pro.user @@ -0,0 +1,336 @@ + + + + + + EnvironmentId + {92870ea4-37a7-43bd-990a-109b15ee3aaf} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.SocketinSettings + + + true + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.13.0 clang 64bit + Desktop Qt 5.13.0 clang 64bit + qt.qt5.5130.clang_64_kit + 0 + 0 + 0 + + /Users/Daniel/Documents/Uni/Bachelor/Absolviert/Bachelorarbeit/git/Socket_py/build-Socket_python-Desktop_Qt_5_13_0_clang_64bit-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + /Users/Daniel/Documents/Uni/Bachelor/Absolviert/Bachelorarbeit/git/Socket_py/build-Socket_python-Desktop_Qt_5_13_0_clang_64bit-Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + /Users/Daniel/Documents/Uni/Bachelor/Absolviert/Bachelorarbeit/git/Socket_py/build-Socket_python-Desktop_Qt_5_13_0_clang_64bit-Profile + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 3 + + + 0 + Deployment + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deployment-Konfiguration + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + Socket_python + + Qt4ProjectManager.Qt4RunConfiguration:/Users/Daniel/Documents/Uni/Bachelor/Absolviert/Bachelorarbeit/git/Socket_py/qt/Socket_python.pro + + 3768 + false + true + false + true + false + false + true + + /Users/Daniel/Documents/Uni/Bachelor/Absolviert/Bachelorarbeit/git/Socket_py/build-Socket_python-Desktop_Qt_5_13_0_clang_64bit-Debug/Socket_python.app/Contents/MacOS + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 21 + + + Version + 21 + + diff --git a/socket_py/qt/main.cpp b/socket_py/qt/main.cpp new file mode 100644 index 0000000..b48f94e --- /dev/null +++ b/socket_py/qt/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/socket_py/qt/mainwindow.cpp b/socket_py/qt/mainwindow.cpp new file mode 100644 index 0000000..49d64fc --- /dev/null +++ b/socket_py/qt/mainwindow.cpp @@ -0,0 +1,14 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/socket_py/qt/mainwindow.h b/socket_py/qt/mainwindow.h new file mode 100644 index 0000000..9353441 --- /dev/null +++ b/socket_py/qt/mainwindow.h @@ -0,0 +1,22 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H diff --git a/socket_py/qt/mainwindow.ui b/socket_py/qt/mainwindow.ui new file mode 100644 index 0000000..b372619 --- /dev/null +++ b/socket_py/qt/mainwindow.ui @@ -0,0 +1,79 @@ + + + MainWindow + + + + 0 + 0 + 800 + 480 + + + + MainWindow + + + background-color: rgb(50, 50, 50); + + + + + + 435 + 439 + 351 + 31 + + + + color: white; font-size:18px + + + Currently not connected to node + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 720 + 10 + 64 + 64 + + + + color: transparent; border: none + + + X + + + + + + 85 + 20 + 630 + 90 + + + + color:white; font-size:36px + + + <html><head/><body><p><span style=" font-size:40pt; font-weight:600; font-style:italic;">Blockchain Smart Outlet</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + diff --git a/socket_py/socket.py b/socket_py/socket.py new file mode 100644 index 0000000..bad10fd --- /dev/null +++ b/socket_py/socket.py @@ -0,0 +1,175 @@ +# ui libraries +from PyQt5.QtGui import QPalette, QFont +from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot, Qt, QEvent, QSize +from PyQt5.QtWidgets import QApplication, QMainWindow, QScrollerProperties, QScroller, QListWidgetItem + +# ethereum library +from eth_account.messages import encode_defunct +from web3.middleware import geth_poa_middleware +from web3.exceptions import TransactionNotFound +from web3 import Web3 +from ui_mainwindow import Ui_MainWindow + +# websockets +import websockets +import asyncio +from socket import socket, AF_INET, SOCK_DGRAM + +# utility libraries +import operator +import sys +from enum import Enum +import json +from json import JSONDecodeError +import credentials + +DEBUG = False + +# credentials +nodeAddr = credentials.nodeAddr +contract_abi = '[{"constant":false,"inputs":[{"name":"_newPrice","type":"uint256"}],"name":"changePrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"},{"name":"_signature","type":"bytes"}],"name":"closeChannel","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"initializePaymentChannel","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"timeOutChannel","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_pricePerSecond","type":"uint256"},{"name":"_expirationDuration","type":"uint256"},{"name":"_minDeposit","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"customer","type":"address"},{"indexed":true,"name":"start","type":"uint256"},{"indexed":true,"name":"maxValue","type":"uint256"},{"indexed":false,"name":"end","type":"uint256"}],"name":"InitializedPaymentChannel","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"value","type":"uint256"},{"indexed":true,"name":"expired","type":"bool"},{"indexed":false,"name":"duration","type":"uint256"}],"name":"ClosedPaymentChannel","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"oldPrice","type":"uint256"},{"indexed":true,"name":"newPrice","type":"uint256"}],"name":"PriceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"channelActive","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"channelCustomer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"channelExpired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"creationTimeStamp","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"customerNonces","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"expirationDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"expirationDuration","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minDeposit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pricePerSecond","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_value","type":"uint256"},{"name":"_signature","type":"bytes"}],"name":"verifySignature","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]' + +etherPriceEUR = 300 +web3 = Web3(Web3.HTTPProvider(nodeAddr)) +# inject the poa compatibility middleware to the innermost layer +web3.middleware_onion.inject(geth_poa_middleware, layer=0) + +privateKey = bytes.fromhex(credentials.privateKey) +address = web3.toChecksumAddress(credentials.address) + +# global thread and ui variables +window = None +app = None + + +class State(Enum): + disconnected = 0 + connected_P = 1 + connected_S = 2 + initialized_P = 3 + initialized_S = 4 + active_P = 5 + active_S = 6 + closed = 7 + + +# Main window class +class MainWindow(QMainWindow): + def __init__(self, parent=None): + super(MainWindow, self).__init__(parent) + + # load ui file + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + + # start main thread + self.node_thread = Node() + self.node_thread.start() + + # connect button handlers + self.ui.closeButton.clicked.connect(self.close) + + def closeEvent(self, event): + # cleanup and end threads + try: + self.node_thread.threadActive = False + self.node_thread.wait() + except: + pass + try: + self.ws_thread.threadActive = False + self.ws_thread.wait() + except: + pass + # GPIO.cleanup() + event.accept() # let the window close + + def terminateThread(self, process): + try: + thread = operator.attrgetter(process)(self) + if thread.threadActive: + thread.threadActive = False + thread.quit() + if (thread.wait(1000) == False): + thread.terminate() + thread.wait() + except AttributeError: + # thread was not created yet + pass + + def connectWS(self): + self.updateInfoCenter('') + self.updateInfo('Searching for smart sockets...') + + # start websocket + self.ws_thread = WSConnection() + self.ws_thread.start() + + +# ethereum node functionality +class Node(QThread): + def __init__(self, parent=window): + super(Node, self).__init__(parent) + global window + self.threadActive = False + + if web3.isConnected(): + self.threadActive = True + else: + window.ui.nodeInfoLabel.setText("Not connected to Node") + + def run(self): + global window + i = 0 + while self.threadActive: + i += 1 + # check if node is up to date once per second + syncing = web3.eth.syncing + if syncing is False: + blockNumber = web3.eth.blockNumber + # TODO remove str(i) + window.ui.nodeInfoLabel.setText( + "Blocknumber: " + str(blockNumber) + ) + + else: + window.ui.nodeInfoLabel.setText( + "Syncing: " + str(syncing.currentBlock) + + "/" + str(syncing.highestBlock) + ) + + QThread().sleep(5) + + +# websocket thread +class WSConnection(QThread): + def __init__(self, parent=window): + super(WSConnection, self).__init__(parent) + self.threadActive = False + + def run(self): + pass + + +def main(): + global window + global app + + # start window and ui + app = QApplication(sys.argv) + window = MainWindow() + + # show ui + # window.showFullScreen() + if DEBUG: + window.show() + else: + # show app in fullscreen and hide cursor on RPi + window.showFullScreen() + app.setOverrideCursor(Qt.BlankCursor) + + sys.exit(app.exec_()) + + +if __name__ == "__main__": + main() diff --git a/socket_py/ui_mainwindow.py b/socket_py/ui_mainwindow.py new file mode 100644 index 0000000..96f6204 --- /dev/null +++ b/socket_py/ui_mainwindow.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'qt/mainwindow.ui', +# licensing of 'qt/mainwindow.ui' applies. +# +# Created: Mon Nov 4 22:01:21 2019 +# by: pyside2-uic running on PySide2 5.13.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(800, 480) + MainWindow.setStyleSheet("background-color: rgb(50, 50, 50);") + self.centralWidget = QtWidgets.QWidget(MainWindow) + self.centralWidget.setObjectName("centralWidget") + self.nodeInfoLabel = QtWidgets.QLabel(self.centralWidget) + self.nodeInfoLabel.setGeometry(QtCore.QRect(435, 439, 351, 31)) + self.nodeInfoLabel.setStyleSheet("color: white; font-size:18px") + self.nodeInfoLabel.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.nodeInfoLabel.setObjectName("nodeInfoLabel") + self.closeButton = QtWidgets.QPushButton(self.centralWidget) + self.closeButton.setGeometry(QtCore.QRect(720, 10, 64, 64)) + self.closeButton.setStyleSheet("color: transparent; border: none") + self.closeButton.setObjectName("closeButton") + self.infoLabel = QtWidgets.QLabel(self.centralWidget) + self.infoLabel.setGeometry(QtCore.QRect(85, 20, 630, 90)) + self.infoLabel.setStyleSheet("color:white; font-size:36px") + self.infoLabel.setAlignment(QtCore.Qt.AlignCenter) + self.infoLabel.setObjectName("infoLabel") + MainWindow.setCentralWidget(self.centralWidget) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + MainWindow.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "MainWindow", None, -1)) + self.nodeInfoLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Currently not connected to node", None, -1)) + self.closeButton.setText(QtWidgets.QApplication.translate("MainWindow", "X", None, -1)) + self.infoLabel.setText(QtWidgets.QApplication.translate("MainWindow", "

Blockchain Smart Outlet

", None, -1)) +