Compare commits

...

3 Commits

Author SHA1 Message Date
b759fc1954 network/gui: add unified reset for SSL certificates and known servers
- Network.clear_pinned_server_certs(): remove cached certs and reconnect interfaces
- Network.clear_recent_servers(): clear the list of recently used servers
- QML bridge: expose clearPinnedServerCertificates() and clearRecentServers()
- QML: move reset actions to ServerConfig with a dropdown menu (SSL / Known servers)
- Qt: add Reset SSL certificates and Reset known servers buttons in Server tab
2026-02-19 08:48:45 +01:00
e15826b645 Merge branch 'arm64': add ARM64/aarch64 QML support 2026-02-18 15:42:58 +01:00
84df4b7cb8 feat(arm64): add ARM64/aarch64 QML support with pinned PyQt6 dependencies
- Pin qml_gui extras in setup.py to PyQt6>=6.7.0,<6.8.0, PyQt6-Qt6>=6.7.0,<6.8.0
  and PyQt6-sip==13.10.2 for compatibility on both x86_64 and aarch64 architectures
- Add ARM64/aarch64 QML GUI installation instructions in README.md with the exact
  pinned package versions required for correct operation on Apple Silicon and
  Linux ARM64 systems
- Update README.md
2026-02-18 15:42:29 +01:00
7 changed files with 238 additions and 39 deletions

View File

@@ -49,82 +49,108 @@ The easiest way to run Pallectrum is to download the pre-built binaries:
### Running from Source
If you want to run Pallectrum from source, you'll need Python 3.10 or higher.
Running from source requires **Python 3.10 or higher**. Follow the steps for your platform below.
---
#### Windows
**Prerequisites:**
- Python 3.10+ installed from [python.org](https://www.python.org/downloads/)
**Setup with Virtual Environment:**
**Prerequisite:** Python 3.10+ from [python.org](https://www.python.org/downloads/)
```powershell
# Clone repository
# Clone the repository
git clone https://github.com/palladium-coin/pallectrum.git
cd pallectrum
# Create and activate virtual environment
# Create and activate a virtual environment
python -m venv env
env\Scripts\Activate.ps1
# Upgrade pip and install dependencies
# Install dependencies
python -m pip install --upgrade pip
pip install -e ".[gui,crypto]"
# Copy libsecp256k1 DLL (if needed)
# Copy the secp256k1 library (required on Windows)
copy libsecp256k1*.dll env\Lib\site-packages\electrum_ecc\
# Run with Qt GUI
# Launch
python run_electrum
```
##### Optional: QML GUI on Windows (simulates Android interface)
> **Optional QML GUI** (simulates the Android interface):
> ```powershell
> pip install -e ".[qml_gui]"
> python run_electrum -g qml
> ```
```powershell
pip install ".[qml_gui]"
python run_electrum gui -g qml
```
---
#### Linux (Ubuntu/Debian)
#### Linux (Ubuntu / Debian)
**Install Prerequisites:**
##### 1. Install system prerequisites
```bash
# System packages
sudo apt update
sudo apt install -y python3-venv python3-pip build-essential python3-dev
sudo apt install -y libffi-dev libssl-dev libsecp256k1-dev
sudo apt install -y \
python3-venv python3-pip python3-dev build-essential \
libffi-dev libssl-dev libsecp256k1-dev libpulse0
```
**Setup with Virtual Environment:**
##### 2. Clone the repository and set up the virtual environment
```bash
# Clone repository
git clone https://github.com/palladium-coin/pallectrum.git
cd pallectrum
# Create and activate virtual environment
python3 -m venv env
source env/bin/activate
# Upgrade pip and install dependencies
python3 -m pip install --upgrade pip
pip install -e ".[gui,crypto]"
```
# Run with Qt GUI
##### 3. Install dependencies
###### 3.1. x86_64 (Intel / AMD)
```bash
pip install -e ".[gui,crypto]"
```
###### 3.2. ARM64 / aarch64
> PyQt6 ≥ 6.9 wheels are not published for ARM64 on PyPI — pinned versions must be used.
```bash
pip install -r contrib/requirements/requirements.txt
pip install "cryptography>=2.6"
pip install --only-binary PyQt6,PyQt6-Qt6,PyQt6-sip \
"PyQt6>=6.7.0,<6.8.0" \
"PyQt6-Qt6>=6.7.0,<6.8.0" \
"PyQt6-sip==13.10.2"
pip install -e .
```
##### 4. Launch
```bash
python run_electrum
```
##### Optional: QML GUI on Linux (simulates Android interface)
###### 4.1. Optional QML GUI (Android-like interface)
*x86_64*
```bash
pip install ".[qml_gui]"
python run_electrum gui -g qml
pip install -e ".[qml_gui]"
python run_electrum -g qml
```
For detailed installation instructions, including dependencies and platform-specific notes, see the [original Electrum documentation](README-ELECTRUM.md#getting-started).
*ARM64* — the pinned PyQt6 packages installed in step 3 already include QML support:
```bash
python run_electrum -g qml
```
---
For the full list of dependencies and advanced configuration options, see [README-ELECTRUM.md#getting-started](README-ELECTRUM.md#getting-started).
## User Guide
@@ -168,10 +194,10 @@ Pallectrum stores wallet data and configuration in:
Pallectrum includes specific adaptations for the Palladium blockchain:
- BIP21 URI scheme: `palladium:` (instead of `bitcoin:`)
- BIP21 URI scheme: `palladium:`
- BIP44 coin type: 746
- Default block explorer: https://explorer.palladium-coin.com/
- Currency unit: PLM (Palladium) instead of BTC
- Currency unit: PLM (Palladium)
- Checkpoint-based validation (compatible with LWMA difficulty algorithm)

View File

@@ -296,6 +296,7 @@ Pane {
dialog.open()
}
}
}
}

View File

@@ -50,9 +50,73 @@ Item {
}
}
Label {
text: qsTr("Server")
enabled: address_tf.enabled
RowLayout {
Layout.fillWidth: true
Label {
text: qsTr("Server")
enabled: address_tf.enabled
Layout.fillWidth: true
}
ToolButton {
icon.source: '../../../icons/delete.png'
ToolTip.text: qsTr('Reset network data')
ToolTip.visible: hovered
onClicked: resetMenu.open()
Menu {
id: resetMenu
MenuItem {
text: qsTr('SSL certificates')
onTriggered: {
var dialog = app.messageDialog.createObject(app, {
title: qsTr('Are you sure?'),
text: qsTr('This will remove cached SSL certificates for servers and reconnect to fetch them again.'),
yesno: true
})
dialog.accepted.connect(function() {
var removed = Network.clearPinnedServerCertificates()
var msg = removed < 0
? qsTr('Failed to reset SSL certificates.')
: removed > 0
? qsTr('%1 certificate files were removed.').arg(removed)
: qsTr('No cached certificate files were found.')
app.messageDialog.createObject(app, {
title: qsTr('Reset SSL certificates'),
text: msg
}).open()
})
dialog.open()
}
}
MenuItem {
text: qsTr('Known servers')
onTriggered: {
var dialog = app.messageDialog.createObject(app, {
title: qsTr('Are you sure?'),
text: qsTr('This will remove the list of known servers.'),
yesno: true
})
dialog.accepted.connect(function() {
var removed = Network.clearRecentServers()
var msg = removed < 0
? qsTr('Failed to reset known servers.')
: removed > 0
? qsTr('%1 server(s) were removed.').arg(removed)
: qsTr('No known servers were found.')
app.messageDialog.createObject(app, {
title: qsTr('Reset known servers'),
text: msg
}).open()
})
dialog.open()
}
}
}
}
}
TextHighlightPane {

View File

@@ -306,3 +306,19 @@ class QENetwork(QObject, QtEventListener):
@pyqtSlot()
def probeTor(self):
ProxySettings.probe_tor(self.torProbeFinished.emit) # via signal
@pyqtSlot(result=int)
def clearPinnedServerCertificates(self):
try:
return self.network.run_from_another_thread(self.network.clear_pinned_server_certs())
except Exception:
self._logger.exception("failed to clear pinned server certificates")
return -1
@pyqtSlot(result=int)
def clearRecentServers(self):
try:
return self.network.clear_recent_servers()
except Exception:
self._logger.exception("failed to clear recent servers")
return -1

View File

@@ -29,7 +29,7 @@ from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QMenu, QGridLayout, QComboBox, QLineEdit, QDialog, QVBoxLayout, QHeaderView,
QCheckBox, QTabWidget, QWidget, QLabel, QPushButton, QHBoxLayout,
QListWidget, QListWidgetItem,
QListWidget, QListWidgetItem, QMessageBox,
)
from PyQt6.QtGui import QIntValidator
@@ -444,6 +444,16 @@ class ServerWidget(QWidget, QtEventListener):
self.layout().addWidget(self.nodes_list_widget)
self.nodes_list_widget.update()
self.clear_certs_button = QPushButton(_('Reset SSL certificates'))
self.clear_certs_button.clicked.connect(self.clear_pinned_server_certs)
self.clear_servers_button = QPushButton(_('Reset known servers'))
self.clear_servers_button.clicked.connect(self.clear_recent_servers)
buttons = QHBoxLayout()
buttons.addStretch(1)
buttons.addWidget(self.clear_certs_button)
buttons.addWidget(self.clear_servers_button)
self.layout().addLayout(buttons)
self.register_callbacks()
self.destroyed.connect(lambda: self.unregister_callbacks())
@@ -584,6 +594,54 @@ class ServerWidget(QWidget, QtEventListener):
_logger.debug(f"set_server: {new_net_params=}")
self.network.run_from_another_thread(self.network.set_parameters(new_net_params))
def clear_pinned_server_certs(self):
result = QMessageBox.question(
self,
_('Reset SSL certificates'),
_('This will remove cached SSL certificates for servers and reconnect to fetch them again.')
+ '\n\n'
+ _('Do you want to continue?'),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if result != QMessageBox.StandardButton.Yes:
return
try:
removed = self.network.run_from_another_thread(self.network.clear_pinned_server_certs())
except Exception as e:
_logger.exception("failed to clear pinned server certificates")
QMessageBox.critical(self, _('Reset SSL certificates'), str(e))
return
if removed > 0:
msg = _('{} certificate files were removed.').format(removed)
else:
msg = _('No cached certificate files were found.')
QMessageBox.information(self, _('Reset SSL certificates'), msg)
def clear_recent_servers(self):
result = QMessageBox.question(
self,
_('Reset known servers'),
_('This will remove the list of known servers.')
+ '\n\n'
+ _('Do you want to continue?'),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if result != QMessageBox.StandardButton.Yes:
return
try:
removed = self.network.clear_recent_servers()
except Exception as e:
_logger.exception("failed to clear recent servers")
QMessageBox.critical(self, _('Reset known servers'), str(e))
return
if removed > 0:
msg = _('{} server(s) were removed.').format(removed)
else:
msg = _('No known servers were found.')
QMessageBox.information(self, _('Reset known servers'), msg)
class NostrWidget(QWidget, QtEventListener):

View File

@@ -408,6 +408,36 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
"""Our guess whether the device has Internet-connectivity."""
return self._has_ever_managed_to_connect_to_server
def clear_recent_servers(self) -> int:
"""Clear the list of recently used servers."""
with self.recent_servers_lock:
count = len(self._recent_servers)
self._recent_servers = []
self._save_recent_servers()
self.logger.info(f"cleared {count} recent server(s)")
return count
async def clear_pinned_server_certs(self) -> int:
"""Delete cached server SSL certs and reconnect interfaces."""
certs_dir = os.path.join(self.config.path, 'certs')
util.make_dir(certs_dir)
removed = 0
for entry in os.scandir(certs_dir):
if not entry.is_file():
continue
try:
os.unlink(entry.path)
removed += 1
except OSError as e:
self.logger.warning(f"failed to delete cert file {entry.path!r}: {e!r}")
self.logger.info(f"removed {removed} cached server cert(s)")
if removed:
with self.interfaces_lock:
interfaces = list(self.interfaces.values())
for iface in interfaces:
await self._close_interface(iface)
return removed
def has_channel_db(self):
return self.channel_db is not None

View File

@@ -45,7 +45,11 @@ extras_require = {
'gui': ['pyqt6'],
'crypto': ['cryptography>=2.6'],
'tests': ['pycryptodomex>=3.7', 'cryptography>=2.6', 'pyaes>=0.1a1'],
'qml_gui': ['pyqt6<6.6', 'pyqt6-qt6<6.6']
'qml_gui': [
'pyqt6>=6.7.0,<6.8.0',
'pyqt6-qt6>=6.7.0,<6.8.0',
'pyqt6-sip==13.10.2',
],
}
# 'full' extra that tries to grab everything an enduser would need (except for libsecp256k1...)
extras_require['full'] = [pkg for sublist in