class Window(QMainWindow): # pylint: disable=too-many-instance-attributes,too-many-locals,too-many-statements def __init__(self): super().__init__() self.node_id = str(uuid4()) self.wallet = Wallet() self.blockchain = Blockchain(self.wallet.address, self.node_id) self.threadCount = QThreadPool.globalInstance().maxThreadCount() self.pool = QThreadPool.globalInstance() self.setWindowTitle("To Be Named Coin") self.setGeometry(1300, 1000, 1300, 1000) self.setupUi() self.registerAndSyncNode() def format_label(self) -> None: balance = (0.0 if self.blockchain.get_balance() is None else self.blockchain.get_balance()) self.label.setText("Address: {}\nBalance: {:6.2f}".format( self.wallet.address, balance)) def configure_menu_bar(self) -> None: """ Setup the menu bar """ exitAction = QAction(QIcon("exit.png"), "&Exit", self) exitAction.setShortcut("Ctrl+Q") exitAction.setStatusTip("Exit application") exitAction.triggered.connect(app.quit) # type: ignore menubar = self.menuBar() fileMenu = menubar.addMenu("&File") fileMenu.addAction(exitAction) walletActionCreate = QAction("&Create", self) walletActionCreate.setStatusTip("Create new wallet") walletActionCreate.triggered.connect( # type: ignore lambda: self.setupWalletUi("create")) walletActionLogin = QAction("&Login", self) walletActionLogin.setStatusTip("Login to wallet") walletActionLogin.triggered.connect( # type: ignore lambda: self.setupWalletUi("login")) walletActionLogout = QAction("&Logout", self) walletActionLogout.setStatusTip("Logout of wallet") walletActionLogout.triggered.connect( # type: ignore lambda: self.setupWalletUi("logout")) walletMenu = menubar.addMenu("&Wallet") walletMenu.addAction(walletActionCreate) walletMenu.addAction(walletActionLogin) walletMenu.addAction(walletActionLogout) chainActionRegister = QAction("&Register and sync", self) chainActionRegister.setStatusTip( "Register and sync node to blockchain") chainActionRegister.triggered.connect( self.registerAndSyncNode) # type: ignore chainActionShow = QAction("&Show chain", self) chainActionShow.setStatusTip("Show blockchain") chainActionShow.triggered.connect( # type: ignore lambda: self.mainDisplay.setText( json.dumps(self.blockchain.pretty_chain(), indent=2))) chainActionClear = QAction("&Clear", self) chainActionClear.setStatusTip("Clear visible blockchain") chainActionClear.triggered.connect( # type: ignore lambda: self.mainDisplay.setText("")) chainMenu = menubar.addMenu("&Chain") chainMenu.addAction(chainActionRegister) chainMenu.addAction(chainActionShow) chainMenu.addAction(chainActionClear) transactionNew = QAction("&New", self) transactionNew.setStatusTip("Create new transaction") transactionNew.triggered.connect( self.setupTransactionUi) # type: ignore transactionPending = QAction("&Pending", self) transactionPending.setStatusTip("View pending transaction(s)") transactionPending.triggered.connect( # type: ignore lambda: self.mainDisplay.setText( json.dumps( [{ "sender": t.signed_transaction.details.sender, "recipient": t.signed_transaction.details.recipient, "amount": t.signed_transaction.details.amount, "nonce": t.signed_transaction.details.nonce, "signature": t.signed_transaction.details.signature, } for t in self.blockchain.get_open_transactions], indent=2, ))) transactionMenu = menubar.addMenu("&Transaction") transactionMenu.addAction(transactionNew) transactionMenu.addAction(transactionPending) mineNewBlock = QAction("&Mine", self) mineNewBlock.setStatusTip("Mine a new block") mineNewBlock.triggered.connect(self.mineBlock) # type: ignore mineMenu = menubar.addMenu("&Mining") mineMenu.addAction(mineNewBlock) def setupUi(self): self.scroll = QScrollArea() self.widget = QWidget() # Set the layout self.grid = QGridLayout() # Initialize Status Bar self.statusBar() # Create and add top label self.label = QLabel("Welcome!!") self.label.setAlignment(Qt.AlignTop) self.grid.addWidget(self.label, 1, 0) # Create and connect main display self.mainDisplay = QLabel("") self.mainDisplay.setAlignment(Qt.AlignVCenter) self.grid.addWidget(self.mainDisplay, 2, 0) self.widget.setLayout(self.grid) self.widget.setFixedWidth(self.width()) # Scroll Area Properties (Since our wallet addresses are so long right now) self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scroll.setWidgetResizable(True) self.scroll.setWidget(self.widget) self.setCentralWidget(self.scroll) self.configure_menu_bar() def setupWalletUi(self, action) -> None: self.mainDisplay.setText("") walletLayout = QGridLayout() password = QInputDialog() def refresh_chain() -> None: self.blockchain = Blockchain(self.wallet.address, self.node_id) self.registerAndSyncNode() self.format_label() def login() -> None: if self.wallet.login(password.textValue()): refresh_chain() def create() -> None: if self.wallet.create_login(password.textValue()): refresh_chain() if action == "logout": self.wallet = Wallet() refresh_chain() else: password.setTextEchoMode(QLineEdit.Password) if action == "create": password.accepted.connect(create) # type: ignore elif action == "login": password.accepted.connect(login) # type: ignore walletLayout.addWidget(password, 1, 0) self.grid.addLayout(walletLayout, 2, 0) def setupTransactionUi(self) -> None: self.mainDisplay.setText("") transactionLayout = QGridLayout() recipientLabel = QLabel("Recipient: ") recipient = QLineEdit() amountLabel = QLabel("Amount: ") amount = QLineEdit() submit = QPushButton("Submit Transaction") def clear_transaction(): # Theres got to be a better way to handle these Widgets... recipient.deleteLater() recipientLabel.deleteLater() amount.deleteLater() amountLabel.deleteLater() submit.deleteLater() def submit_transaction(): if not self.wallet.logged_in: logging.error( "No Wallet is currently logged in. Unable to create a transaction" "without one") clear_transaction() else: tx_details = Details( sender=self.wallet.address, recipient=recipient.text(), amount=float(amount.text()), nonce=self.wallet.get_nonce(), timestamp=datetime.utcnow(), public_key=self.wallet.public_key.hex(), ) transaction = self.wallet.sign_transaction(tx_details) if self.blockchain.add_transaction(transaction, "open"): logging.info("Added transaction!") clear_transaction() else: logging.info("Transaction failed!") submit.clicked.connect(submit_transaction) # type: ignore transactionLayout.addWidget(recipientLabel, 1, 0) transactionLayout.addWidget(recipient, 1, 1) transactionLayout.addWidget(amountLabel, 2, 0) transactionLayout.addWidget(amount, 2, 1) transactionLayout.addWidget(submit, 3, 0) self.grid.addLayout(transactionLayout, 3, 0) def registerAndSyncNode(self): def log_result(s): self.statusBar().showMessage(s) def finished(): self.format_label() def progress(progress): progress, message = progress logging.debug("%d%% done %s", progress, message) def register(progress_callback): logging.info("Registering to masternode....") progress_callback.emit((25, "Registering to masternode")) self.blockchain.register_node("https://sedrik.life/blockchain") progress_callback.emit((50, "Registered to masternode")) logging.info("Syncing with masternode....") progress_callback.emit((75, "Syncing to masternode...")) self.blockchain.resolve_conflicts() progress_callback.emit(( 100, f"Synced {self.blockchain.chain_length} blocks with masternode" )) # Pass the function to execute worker = Worker( register) # Any other args, kwargs are passed to the run function worker.signals.result.connect(log_result) worker.signals.finished.connect(finished) worker.signals.progress.connect(progress) # Execute self.pool.start(worker) def mineBlock(self): def log_result(s): self.statusBar().showMessage(s) def finished(): self.format_label() logging.info("Finished") def progress(progress): progress, message = progress logging.debug("%d%% done %s", progress, message) def register(progress_callback): logging.info("Mining new Block...") progress_callback.emit((50, "Mining new block...")) self.blockchain.mine_block() progress_callback.emit((100, "Block Mined!")) # Pass the function to execute worker = Worker( register) # Any other args, kwargs are passed to the run function worker.signals.result.connect(log_result) worker.signals.finished.connect(finished) worker.signals.progress.connect(progress) # Execute self.pool.start(worker)