diff --git a/torba/orchstr8/cli.py b/torba/orchstr8/cli.py
index 0dc7343e6..523e0c1dd 100644
--- a/torba/orchstr8/cli.py
+++ b/torba/orchstr8/cli.py
@@ -3,8 +3,8 @@ import argparse
import asyncio
import aiohttp
-from .node import Conductor, get_ledger_from_environment, get_blockchain_node_from_ledger
-from .service import ConductorService
+from torba.orchstr8.node import Conductor, get_ledger_from_environment, get_blockchain_node_from_ledger
+from torba.orchstr8.service import ConductorService
def get_argument_parser():
@@ -46,6 +46,7 @@ def main():
return start_app()
loop = asyncio.get_event_loop()
+ asyncio.set_event_loop(loop)
ledger = get_ledger_from_environment()
if command == 'download':
diff --git a/torba/orchstr8/node.py b/torba/orchstr8/node.py
index 991ea1241..7e5b834a1 100644
--- a/torba/orchstr8/node.py
+++ b/torba/orchstr8/node.py
@@ -50,13 +50,19 @@ def get_blockchain_node_from_ledger(ledger_module):
)
-def set_logging(ledger_module, level):
- logging.getLogger('torba').setLevel(level)
- logging.getLogger('torba.client').setLevel(level)
- logging.getLogger('torba.server').setLevel(level)
- #logging.getLogger('asyncio').setLevel(level)
- logging.getLogger('blockchain').setLevel(level)
- logging.getLogger(ledger_module.__name__).setLevel(level)
+def set_logging(ledger_module, level, handler=None):
+ modules = [
+ 'torba',
+ 'torba.client',
+ 'torba.server',
+ 'blockchain',
+ ledger_module.__name__
+ ]
+ for module_name in modules:
+ module = logging.getLogger(module_name)
+ module.setLevel(level)
+ if handler is not None:
+ module.addHandler(handler)
class Conductor:
@@ -184,6 +190,7 @@ class SPVNode:
self.controller = None
self.data_path = None
self.server = None
+ self.port = 1984
async def start(self):
self.data_path = tempfile.mkdtemp()
@@ -191,7 +198,7 @@ class SPVNode:
'DB_DIRECTORY': self.data_path,
'DAEMON_URL': 'http://rpcuser:rpcpassword@localhost:50001/',
'REORG_LIMIT': '100',
- 'TCP_PORT': '1984'
+ 'TCP_PORT': str(self.port)
}
os.environ.update(conf)
self.server = Server(Env(self.coin_class))
@@ -250,6 +257,7 @@ class BlockchainNode:
self.protocol = None
self.transport = None
self._block_expected = 0
+ self.port = 50001
def is_expected_block(self, e: BlockHeightEvent):
return self._block_expected == e.height
@@ -303,7 +311,7 @@ class BlockchainNode:
self.daemon_bin,
'-datadir={}'.format(self.data_path),
'-printtoconsole', '-regtest', '-server', '-txindex',
- '-rpcuser=rpcuser', '-rpcpassword=rpcpassword', '-rpcport=50001'
+ '-rpcuser=rpcuser', '-rpcpassword=rpcpassword', f'-rpcport={self.port}'
)
self.log.info(' '.join(command))
self.transport, self.protocol = await loop.subprocess_exec(
diff --git a/torba/orchstr8/service.py b/torba/orchstr8/service.py
index 23a7ad3f8..d57bf5a26 100644
--- a/torba/orchstr8/service.py
+++ b/torba/orchstr8/service.py
@@ -2,7 +2,9 @@ import asyncio
import logging
from aiohttp.web import Application, WebSocketResponse, json_response
from aiohttp.http_websocket import WSMsgType, WSCloseCode
-from .node import Conductor
+
+from torba.client.util import satoshis_to_coins
+from .node import Conductor, set_logging
PORT = 7954
@@ -56,30 +58,15 @@ class ConductorService:
await self.app.cleanup()
async def start_stack(self, _):
- handler = WebSocketLogHandler(self.send_message)
- logging.getLogger('blockchain').setLevel(logging.DEBUG)
- logging.getLogger('blockchain').addHandler(handler)
- logging.getLogger('electrumx').setLevel(logging.DEBUG)
- logging.getLogger('electrumx').addHandler(handler)
- logging.getLogger('Controller').setLevel(logging.DEBUG)
- logging.getLogger('Controller').addHandler(handler)
- logging.getLogger('LBRYBlockProcessor').setLevel(logging.DEBUG)
- logging.getLogger('LBRYBlockProcessor').addHandler(handler)
- logging.getLogger('LBCDaemon').setLevel(logging.DEBUG)
- logging.getLogger('LBCDaemon').addHandler(handler)
- logging.getLogger('torba').setLevel(logging.DEBUG)
- logging.getLogger('torba').addHandler(handler)
- logging.getLogger(self.stack.ledger_module.__name__).setLevel(logging.DEBUG)
- logging.getLogger(self.stack.ledger_module.__name__).addHandler(handler)
- logging.getLogger(self.stack.ledger_module.__electrumx__.split('.')[0]).setLevel(logging.DEBUG)
- logging.getLogger(self.stack.ledger_module.__electrumx__.split('.')[0]).addHandler(handler)
- #await self.stack.start()
+ set_logging(
+ self.stack.ledger_module, logging.DEBUG, WebSocketLogHandler(self.send_message)
+ )
self.stack.blockchain_started or await self.stack.start_blockchain()
- self.send_message({'type': 'service', 'name': 'blockchain'})
+ self.send_message({'type': 'service', 'name': 'blockchain', 'port': self.stack.blockchain_node.port})
self.stack.spv_started or await self.stack.start_spv()
- self.send_message({'type': 'service', 'name': 'spv'})
+ self.send_message({'type': 'service', 'name': 'spv', 'port': self.stack.spv_node.port})
self.stack.wallet_started or await self.stack.start_wallet()
- self.send_message({'type': 'service', 'name': 'wallet'})
+ self.send_message({'type': 'service', 'name': 'wallet', 'port': ''})
self.stack.wallet_node.ledger.on_header.listen(self.on_status)
self.stack.wallet_node.ledger.on_transaction.listen(self.on_status)
return json_response({'started': True})
@@ -138,10 +125,10 @@ class ConductorService:
self.send_message({
'type': 'status',
'height': self.stack.wallet_node.ledger.headers.height,
- 'balance': await self.stack.wallet_node.account.get_balance(),
+ 'balance': satoshis_to_coins(await self.stack.wallet_node.account.get_balance()),
'miner': await self.stack.blockchain_node.get_balance()
})
def send_message(self, msg):
for web_socket in self.app['websockets']:
- asyncio.ensure_future(web_socket.send_json(msg))
+ self.loop.create_task(web_socket.send_json(msg))
diff --git a/torba/workbench/Makefile b/torba/workbench/Makefile
new file mode 100644
index 000000000..524c22557
--- /dev/null
+++ b/torba/workbench/Makefile
@@ -0,0 +1,5 @@
+all: _blockchain_dock.py _output_dock.py
+_blockchain_dock.py: blockchain_dock.ui
+ pyside2-uic -d blockchain_dock.ui -o _blockchain_dock.py
+_output_dock.py: output_dock.ui
+ pyside2-uic -d output_dock.ui -o _output_dock.py
diff --git a/torba/workbench/__init__.py b/torba/workbench/__init__.py
new file mode 100644
index 000000000..3449276fd
--- /dev/null
+++ b/torba/workbench/__init__.py
@@ -0,0 +1 @@
+from .application import main
diff --git a/torba/workbench/_blockchain_dock.py b/torba/workbench/_blockchain_dock.py
new file mode 100644
index 000000000..2a7cc11d8
--- /dev/null
+++ b/torba/workbench/_blockchain_dock.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'blockchain_dock.ui',
+# licensing of 'blockchain_dock.ui' applies.
+#
+# Created: Sun Jan 13 02:56:21 2019
+# by: pyside2-uic running on PySide2 5.12.0
+#
+# WARNING! All changes made in this file will be lost!
+
+from PySide2 import QtCore, QtGui, QtWidgets
+
+class Ui_BlockchainDock(object):
+ def setupUi(self, BlockchainDock):
+ BlockchainDock.setObjectName("BlockchainDock")
+ BlockchainDock.resize(416, 167)
+ BlockchainDock.setFloating(False)
+ BlockchainDock.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures)
+ self.dockWidgetContents = QtWidgets.QWidget()
+ self.dockWidgetContents.setObjectName("dockWidgetContents")
+ self.formLayout = QtWidgets.QFormLayout(self.dockWidgetContents)
+ self.formLayout.setObjectName("formLayout")
+ self.generate = QtWidgets.QPushButton(self.dockWidgetContents)
+ self.generate.setObjectName("generate")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.generate)
+ self.blocks = QtWidgets.QSpinBox(self.dockWidgetContents)
+ self.blocks.setMinimum(1)
+ self.blocks.setMaximum(9999)
+ self.blocks.setProperty("value", 1)
+ self.blocks.setObjectName("blocks")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.blocks)
+ self.transfer = QtWidgets.QPushButton(self.dockWidgetContents)
+ self.transfer.setObjectName("transfer")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.transfer)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.amount = QtWidgets.QDoubleSpinBox(self.dockWidgetContents)
+ self.amount.setSuffix("")
+ self.amount.setMaximum(9999.99)
+ self.amount.setProperty("value", 10.0)
+ self.amount.setObjectName("amount")
+ self.horizontalLayout.addWidget(self.amount)
+ self.to_label = QtWidgets.QLabel(self.dockWidgetContents)
+ self.to_label.setObjectName("to_label")
+ self.horizontalLayout.addWidget(self.to_label)
+ self.address = QtWidgets.QLineEdit(self.dockWidgetContents)
+ self.address.setObjectName("address")
+ self.horizontalLayout.addWidget(self.address)
+ self.formLayout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout)
+ self.invalidate = QtWidgets.QPushButton(self.dockWidgetContents)
+ self.invalidate.setObjectName("invalidate")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.invalidate)
+ self.block_hash = QtWidgets.QLineEdit(self.dockWidgetContents)
+ self.block_hash.setObjectName("block_hash")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.block_hash)
+ BlockchainDock.setWidget(self.dockWidgetContents)
+
+ self.retranslateUi(BlockchainDock)
+ QtCore.QMetaObject.connectSlotsByName(BlockchainDock)
+
+ def retranslateUi(self, BlockchainDock):
+ BlockchainDock.setWindowTitle(QtWidgets.QApplication.translate("BlockchainDock", "Blockchain", None, -1))
+ self.generate.setText(QtWidgets.QApplication.translate("BlockchainDock", "generate", None, -1))
+ self.blocks.setSuffix(QtWidgets.QApplication.translate("BlockchainDock", " block(s)", None, -1))
+ self.transfer.setText(QtWidgets.QApplication.translate("BlockchainDock", "transfer", None, -1))
+ self.to_label.setText(QtWidgets.QApplication.translate("BlockchainDock", "to", None, -1))
+ self.address.setPlaceholderText(QtWidgets.QApplication.translate("BlockchainDock", "recipient address", None, -1))
+ self.invalidate.setText(QtWidgets.QApplication.translate("BlockchainDock", "invalidate", None, -1))
+ self.block_hash.setPlaceholderText(QtWidgets.QApplication.translate("BlockchainDock", "block hash", None, -1))
+
diff --git a/torba/workbench/_output_dock.py b/torba/workbench/_output_dock.py
new file mode 100644
index 000000000..980343735
--- /dev/null
+++ b/torba/workbench/_output_dock.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'output_dock.ui',
+# licensing of 'output_dock.ui' applies.
+#
+# Created: Sat Oct 27 16:41:03 2018
+# by: pyside2-uic running on PySide2 5.11.2
+#
+# WARNING! All changes made in this file will be lost!
+
+from PySide2 import QtCore, QtGui, QtWidgets
+
+class Ui_OutputDock(object):
+ def setupUi(self, OutputDock):
+ OutputDock.setObjectName("OutputDock")
+ OutputDock.resize(700, 397)
+ OutputDock.setFloating(False)
+ OutputDock.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures)
+ self.dockWidgetContents = QtWidgets.QWidget()
+ self.dockWidgetContents.setObjectName("dockWidgetContents")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.dockWidgetContents)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.textEdit = QtWidgets.QTextEdit(self.dockWidgetContents)
+ self.textEdit.setReadOnly(True)
+ self.textEdit.setObjectName("textEdit")
+ self.horizontalLayout.addWidget(self.textEdit)
+ OutputDock.setWidget(self.dockWidgetContents)
+
+ self.retranslateUi(OutputDock)
+ QtCore.QMetaObject.connectSlotsByName(OutputDock)
+
+ def retranslateUi(self, OutputDock):
+ OutputDock.setWindowTitle(QtWidgets.QApplication.translate("OutputDock", "Output", None, -1))
+
diff --git a/torba/workbench/application.py b/torba/workbench/application.py
new file mode 100644
index 000000000..56b282844
--- /dev/null
+++ b/torba/workbench/application.py
@@ -0,0 +1,401 @@
+import sys
+import json
+import math
+
+from PySide2 import QtCore, QtGui, QtWidgets, QtNetwork, QtWebSockets, QtSvg
+
+from torba.workbench._output_dock import Ui_OutputDock as OutputDock
+from torba.workbench._blockchain_dock import Ui_BlockchainDock as BlockchainDock
+
+
+def dict_to_post_data(d):
+ query = QtCore.QUrlQuery()
+ for key, value in d.items():
+ query.addQueryItem(str(key), str(value))
+ return QtCore.QByteArray(query.toString().encode())
+
+
+class LoggingOutput(QtWidgets.QDockWidget, OutputDock):
+
+ def __init__(self, title, parent):
+ super().__init__(parent)
+ self.setupUi(self)
+ self.setWindowTitle(title)
+
+
+class BlockchainControls(QtWidgets.QDockWidget, BlockchainDock):
+
+ def __init__(self, parent):
+ super().__init__(parent)
+ self.setupUi(self)
+ self.generate.clicked.connect(self.on_generate)
+ self.transfer.clicked.connect(self.on_transfer)
+
+ def on_generate(self):
+ print('generating')
+ self.parent().run_command('generate', blocks=self.blocks.value())
+
+ def on_transfer(self):
+ print('transfering')
+ self.parent().run_command('transfer', amount=self.amount.value())
+
+
+class Arrow(QtWidgets.QGraphicsLineItem):
+
+ def __init__(self, start_node, end_node, parent=None, scene=None):
+ super().__init__(parent, scene)
+ self.start_node = start_node
+ self.start_node.connect_arrow(self)
+ self.end_node = end_node
+ self.end_node.connect_arrow(self)
+ self.arrow_head = QtGui.QPolygonF()
+ self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
+ self.setZValue(-1000.0)
+ self.arrow_color = QtCore.Qt.black
+ self.setPen(QtGui.QPen(
+ self.arrow_color, 2, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin
+ ))
+
+ def boundingRect(self):
+ extra = (self.pen().width() + 20) / 2.0
+ p1 = self.line().p1()
+ p2 = self.line().p2()
+ size = QtCore.QSizeF(p2.x() - p1.x(), p2.y() - p1.y())
+ return QtCore.QRectF(p1, size).normalized().adjusted(-extra, -extra, extra, extra)
+
+ def shape(self):
+ path = super().shape()
+ path.addPolygon(self.arrow_head)
+ return path
+
+ def update_position(self):
+ line = QtCore.QLineF(
+ self.mapFromItem(self.start_node, 0, 0),
+ self.mapFromItem(self.end_node, 0, 0)
+ )
+ self.setLine(line)
+
+ def paint(self, painter, option, widget=None):
+ if self.start_node.collidesWithItem(self.end_node):
+ return
+
+ start_node = self.start_node
+ end_node = self.end_node
+ color = self.arrow_color
+ pen = self.pen()
+ pen.setColor(self.arrow_color)
+ arrow_size = 20.0
+ painter.setPen(pen)
+ painter.setBrush(self.arrow_color)
+
+ end_rectangle = end_node.sceneBoundingRect()
+ start_center = start_node.sceneBoundingRect().center()
+ end_center = end_rectangle.center()
+ center_line = QtCore.QLineF(start_center, end_center)
+ end_polygon = QtGui.QPolygonF(end_rectangle)
+ p1 = end_polygon.at(0)
+
+ intersect_point = QtCore.QPointF()
+ for p2 in end_polygon:
+ poly_line = QtCore.QLineF(p1, p2)
+ intersect_type, intersect_point = poly_line.intersect(center_line)
+ if intersect_type == QtCore.QLineF.BoundedIntersection:
+ break
+ p1 = p2
+
+ self.setLine(QtCore.QLineF(intersect_point, start_center))
+ line = self.line()
+
+ angle = math.acos(line.dx() / line.length())
+ if line.dy() >= 0:
+ angle = (math.pi * 2.0) - angle
+
+ arrow_p1 = line.p1() + QtCore.QPointF(
+ math.sin(angle + math.pi / 3.0) * arrow_size,
+ math.cos(angle + math.pi / 3.0) * arrow_size
+ )
+ arrow_p2 = line.p1() + QtCore.QPointF(
+ math.sin(angle + math.pi - math.pi / 3.0) * arrow_size,
+ math.cos(angle + math.pi - math.pi / 3.0) * arrow_size
+ )
+
+ self.arrow_head.clear()
+ for point in [line.p1(), arrow_p1, arrow_p2]:
+ self.arrow_head.append(point)
+
+ painter.drawLine(line)
+ painter.drawPolygon(self.arrow_head)
+ if self.isSelected():
+ painter.setPen(QtGui.QPen(color, 1, QtCore.Qt.DashLine))
+ line = QtCore.QLineF(line)
+ line.translate(0, 4.0)
+ painter.drawLine(line)
+ line.translate(0, -8.0)
+ painter.drawLine(line)
+
+
+ONLINE_COLOR = "limegreen"
+OFFLINE_COLOR = "lightsteelblue"
+
+
+class NodeItem(QtSvg.QGraphicsSvgItem):
+
+ def __init__(self, context_menu):
+ super().__init__()
+ self._port = ''
+ self._color = OFFLINE_COLOR
+ self.context_menu = context_menu
+ self.arrows = set()
+ self.renderer = QtSvg.QSvgRenderer()
+ self.update_svg()
+ self.setSharedRenderer(self.renderer)
+ #self.setScale(2.0)
+ #self.setTransformOriginPoint(24, 24)
+ self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
+ self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
+
+ def get_svg(self):
+ return self.SVG.format(
+ port=self.port,
+ color=self._color
+ )
+
+ def update_svg(self):
+ self.renderer.load(QtCore.QByteArray(self.get_svg().encode()))
+ self.update()
+
+ @property
+ def port(self):
+ return self._port
+
+ @port.setter
+ def port(self, port):
+ self._port = port
+ self.update_svg()
+
+ @property
+ def online(self):
+ return self._color == ONLINE_COLOR
+
+ @online.setter
+ def online(self, online):
+ if online:
+ self._color = ONLINE_COLOR
+ else:
+ self._color = OFFLINE_COLOR
+ self.update_svg()
+
+ def connect_arrow(self, arrow):
+ self.arrows.add(arrow)
+
+ def disconnect_arrow(self, arrow):
+ self.arrows.discard(arrow)
+
+ def contextMenuEvent(self, event):
+ self.scene().clearSelection()
+ self.setSelected(True)
+ self.myContextMenu.exec_(event.screenPos())
+
+ def itemChange(self, change, value):
+ if change == QtWidgets.QGraphicsItem.ItemPositionChange:
+ for arrow in self.arrows:
+ arrow.update_position()
+ return value
+
+
+class BlockchainNode(NodeItem):
+ SVG = """
+
+ """
+
+ def __init__(self, *args):
+ self._block_height = ''
+ super().__init__(*args)
+
+ @property
+ def block_height(self):
+ return self._block_height
+
+ @block_height.setter
+ def block_height(self, block_height):
+ self._block_height = block_height
+ self.update_svg()
+
+ def get_svg(self):
+ return self.SVG.format(
+ port=self.port,
+ block=self.block_height,
+ color=self._color
+ )
+
+
+class SPVNode(NodeItem):
+ SVG = """
+
+ """
+
+ def __init__(self, *args):
+ super().__init__(*args)
+
+
+class WalletNode(NodeItem):
+ SVG = """
+
+ """
+
+ def __init__(self, *args):
+ self._coins = '--'
+ super().__init__(*args)
+
+ @property
+ def coins(self):
+ return self._coins
+
+ @coins.setter
+ def coins(self, coins):
+ self._coins = coins
+ self.update_svg()
+
+ def get_svg(self):
+ return self.SVG.format(
+ coins=self.coins,
+ color=self._color
+ )
+
+
+class Stage(QtWidgets.QGraphicsScene):
+
+ def __init__(self, parent):
+ super().__init__(parent)
+ self.blockchain = b = BlockchainNode(None)
+ b.port = ''
+ b.block_height = ''
+ b.setZValue(0)
+ b.setPos(-25, -100)
+ self.addItem(b)
+ self.spv = s = SPVNode(None)
+ s.port = ''
+ s.setZValue(0)
+ self.addItem(s)
+ s.setPos(-10, -10)
+ self.wallet = w = WalletNode(None)
+ w.coins = ''
+ w.setZValue(0)
+ w.update_svg()
+ self.addItem(w)
+ w.setPos(0, 100)
+
+ self.addItem(Arrow(b, s))
+ self.addItem(Arrow(s, w))
+
+
+class Orchstr8Workbench(QtWidgets.QMainWindow):
+
+ def __init__(self):
+ super().__init__()
+ self.stage = Stage(self)
+ self.view = QtWidgets.QGraphicsView(self.stage)
+ self.status_bar = QtWidgets.QStatusBar(self)
+
+ self.setWindowTitle('Orchstr8 Workbench')
+ self.setCentralWidget(self.view)
+ self.setStatusBar(self.status_bar)
+
+ self.block_height = self.make_status_label('Height: -- ')
+ self.user_balance = self.make_status_label('User Balance: -- ')
+ self.mining_balance = self.make_status_label('Mining Balance: -- ')
+
+ self.wallet_log = LoggingOutput('Wallet', self)
+ self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.wallet_log)
+ self.spv_log = LoggingOutput('SPV Server', self)
+ self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.spv_log)
+ self.blockchain_log = LoggingOutput('Blockchain', self)
+ self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.blockchain_log)
+
+ self.blockchain_controls = BlockchainControls(self)
+ self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.blockchain_controls)
+
+ self.network = QtNetwork.QNetworkAccessManager(self)
+ self.socket = QtWebSockets.QWebSocket()
+ self.socket.connected.connect(lambda: self.run_command('start'))
+ self.socket.error.connect(lambda e: print(f'errored: {e}'))
+ self.socket.textMessageReceived.connect(self.on_message)
+ self.socket.open('ws://localhost:7954/log')
+
+ def make_status_label(self, text):
+ label = QtWidgets.QLabel(text)
+ label.setFrameStyle(QtWidgets.QLabel.Panel | QtWidgets.QLabel.Sunken)
+ self.status_bar.addPermanentWidget(label)
+ return label
+
+ def on_message(self, text):
+ msg = json.loads(text)
+ if msg['type'] == 'status':
+ self.stage.wallet.coins = msg['balance']
+ self.stage.blockchain.block_height = msg['height']
+ self.block_height.setText(f"Height: {msg['height']} ")
+ self.user_balance.setText(f"User Balance: {msg['balance']} ")
+ self.mining_balance.setText(f"Mining Balance: {msg['miner']} ")
+ elif msg['type'] == 'service':
+ node = {
+ 'blockchain': self.stage.blockchain,
+ 'spv': self.stage.spv,
+ 'wallet': self.stage.wallet
+ }[msg['name']]
+ node.online = True
+ node.port = f":{msg['port']}"
+ elif msg['type'] == 'log':
+ log = {
+ 'blockchain': self.blockchain_log,
+ 'electrumx': self.spv_log,
+ 'lbryumx': self.spv_log,
+ 'Controller': self.spv_log,
+ 'LBRYBlockProcessor': self.spv_log,
+ 'LBCDaemon': self.spv_log,
+ }.get(msg['name'].split('.')[-1], self.wallet_log)
+ log.textEdit.append(msg['message'])
+
+ def run_command(self, command, **kwargs):
+ request = QtNetwork.QNetworkRequest(QtCore.QUrl('http://localhost:7954/'+command))
+ request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/x-www-form-urlencoded")
+ reply = self.network.post(request, dict_to_post_data(kwargs))
+ # reply.finished.connect(cb)
+ reply.error.connect(self.on_command_error)
+
+ @staticmethod
+ def on_command_error(error):
+ print('failed executing command:')
+ print(error)
+
+
+def main():
+ app = QtWidgets.QApplication(sys.argv)
+ workbench = Orchstr8Workbench()
+ workbench.setGeometry(100, 100, 1200, 600)
+ workbench.show()
+ return app.exec_()
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/torba/workbench/blockchain_dock.ui b/torba/workbench/blockchain_dock.ui
new file mode 100644
index 000000000..2946b839d
--- /dev/null
+++ b/torba/workbench/blockchain_dock.ui
@@ -0,0 +1,104 @@
+
+
+ BlockchainDock
+
+
+
+ 0
+ 0
+ 416
+ 167
+
+
+
+ false
+
+
+ QDockWidget::AllDockWidgetFeatures
+
+
+ Blockchain
+
+
+
+ -
+
+
+ generate
+
+
+
+ -
+
+
+ block(s)
+
+
+ 1
+
+
+ 9999
+
+
+ 1
+
+
+
+ -
+
+
+ transfer
+
+
+
+ -
+
+
-
+
+
+
+
+
+ 9999.989999999999782
+
+
+ 10.000000000000000
+
+
+
+ -
+
+
+ to
+
+
+
+ -
+
+
+ recipient address
+
+
+
+
+
+ -
+
+
+ invalidate
+
+
+
+ -
+
+
+ block hash
+
+
+
+
+
+
+
+
+
diff --git a/torba/workbench/output_dock.ui b/torba/workbench/output_dock.ui
new file mode 100644
index 000000000..3e1136659
--- /dev/null
+++ b/torba/workbench/output_dock.ui
@@ -0,0 +1,36 @@
+
+
+ OutputDock
+
+
+
+ 0
+ 0
+ 700
+ 397
+
+
+
+ false
+
+
+ QDockWidget::AllDockWidgetFeatures
+
+
+ Output
+
+
+
+ -
+
+
+ true
+
+
+
+
+
+
+
+
+