|
|
|
@ -114,6 +114,21 @@ log.addHandler(handler) |
|
|
|
from jmqtui import Ui_OpenWalletDialog |
|
|
|
from jmqtui import Ui_OpenWalletDialog |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JMFileDialog(QFileDialog): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent=None): |
|
|
|
|
|
|
|
super().__init__(parent=parent) |
|
|
|
|
|
|
|
self.result_fut = asyncio.get_event_loop().create_future() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@QtCore.Slot(QMessageBox.StandardButton) |
|
|
|
|
|
|
|
def on_finished(self, result): |
|
|
|
|
|
|
|
self.result_fut.set_result(result) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def result(self): |
|
|
|
|
|
|
|
await self.result_fut |
|
|
|
|
|
|
|
return self.result_fut.result() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JMOpenWalletDialog(QDialog, Ui_OpenWalletDialog): |
|
|
|
class JMOpenWalletDialog(QDialog, Ui_OpenWalletDialog): |
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_WALLET_FILE_TEXT = "wallet.jmdat" |
|
|
|
DEFAULT_WALLET_FILE_TEXT = "wallet.jmdat" |
|
|
|
@ -124,17 +139,25 @@ class JMOpenWalletDialog(QDialog, Ui_OpenWalletDialog): |
|
|
|
self.setupUi(self) |
|
|
|
self.setupUi(self) |
|
|
|
self.errorMessageLabel.setWordWrap(True); |
|
|
|
self.errorMessageLabel.setWordWrap(True); |
|
|
|
self.passphraseEdit.setFocus() |
|
|
|
self.passphraseEdit.setFocus() |
|
|
|
self.chooseWalletButton.clicked.connect(self.chooseWalletFile) |
|
|
|
self.chooseWalletButton.clicked.connect(lambda: |
|
|
|
|
|
|
|
asyncio.ensure_future(self.chooseWalletFile())) |
|
|
|
def chooseWalletFile(self, error_text: str = ""): |
|
|
|
|
|
|
|
(filename, _) = QFileDialog.getOpenFileName( |
|
|
|
async def chooseWalletFile(self, error_text: str = ""): |
|
|
|
self, |
|
|
|
d = JMFileDialog(self) |
|
|
|
"Choose Wallet File", |
|
|
|
d.setOptions(QFileDialog.DontUseNativeDialog) |
|
|
|
self._get_wallets_path(), |
|
|
|
d.setWindowTitle('Choose Wallet File') |
|
|
|
options=QFileDialog.DontUseNativeDialog, |
|
|
|
d.setDirectory(self._get_wallets_path()) |
|
|
|
) |
|
|
|
d.finished.connect(d.on_finished) |
|
|
|
if filename: |
|
|
|
d.open() |
|
|
|
self.walletFileEdit.setText(filename) |
|
|
|
await d.result_fut |
|
|
|
|
|
|
|
result = d.result_fut.result() |
|
|
|
|
|
|
|
if result != QDialog.Accepted: |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
filenames = d.selectedFiles() |
|
|
|
|
|
|
|
if not filenames or len(filenames) != 1: |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
filename = filenames[0] |
|
|
|
|
|
|
|
self.walletFileEdit.setText(os.path.basename(filename)) |
|
|
|
self.passphraseEdit.setFocus() |
|
|
|
self.passphraseEdit.setFocus() |
|
|
|
self.errorMessageLabel.setText(self.verify_lock(filename)) |
|
|
|
self.errorMessageLabel.setText(self.verify_lock(filename)) |
|
|
|
|
|
|
|
|
|
|
|
@ -486,21 +509,31 @@ class SpendTab(QWidget): |
|
|
|
return |
|
|
|
return |
|
|
|
self.sch_startButton.setEnabled(True) |
|
|
|
self.sch_startButton.setEnabled(True) |
|
|
|
|
|
|
|
|
|
|
|
def selectSchedule(self): |
|
|
|
async def selectSchedule(self): |
|
|
|
current_path = os.path.dirname(os.path.realpath(__file__)) |
|
|
|
current_path = os.path.dirname( |
|
|
|
firstarg = QFileDialog.getOpenFileName(self, |
|
|
|
os.path.dirname(os.path.realpath(__file__))) |
|
|
|
'Choose Schedule File', |
|
|
|
d = JMFileDialog(self) |
|
|
|
dir=current_path, |
|
|
|
d.setOptions(QFileDialog.DontUseNativeDialog) |
|
|
|
options=QFileDialog.DontUseNativeDialog) |
|
|
|
d.setWindowTitle('Choose Schedule File') |
|
|
|
#TODO validate the schedule |
|
|
|
d.setDirectory(current_path) |
|
|
|
log.debug('Looking for schedule in: ' + str(firstarg)) |
|
|
|
d.finished.connect(d.on_finished) |
|
|
|
if not firstarg: |
|
|
|
d.open() |
|
|
|
|
|
|
|
await d.result_fut |
|
|
|
|
|
|
|
result = d.result_fut.result() |
|
|
|
|
|
|
|
if result != QDialog.Accepted: |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
filenames = d.selectedFiles() |
|
|
|
|
|
|
|
if not filenames or len(filenames) != 1: |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
filename = filenames[0] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO validate the schedule |
|
|
|
|
|
|
|
log.debug('Looking for schedule in: ' + str(filename)) |
|
|
|
#extract raw text before processing |
|
|
|
#extract raw text before processing |
|
|
|
with open(firstarg[0], 'rb') as f: |
|
|
|
with open(filename, 'rb') as f: |
|
|
|
rawsched = f.read() |
|
|
|
rawsched = f.read() |
|
|
|
|
|
|
|
|
|
|
|
res, schedule = get_schedule(firstarg[0]) |
|
|
|
res, schedule = get_schedule(filename) |
|
|
|
if not res: |
|
|
|
if not res: |
|
|
|
asyncio.ensure_future( |
|
|
|
asyncio.ensure_future( |
|
|
|
JMQtMessageBox( |
|
|
|
JMQtMessageBox( |
|
|
|
@ -509,7 +542,7 @@ class SpendTab(QWidget): |
|
|
|
else: |
|
|
|
else: |
|
|
|
mainWindow.statusBar().showMessage("Schedule loaded OK.") |
|
|
|
mainWindow.statusBar().showMessage("Schedule loaded OK.") |
|
|
|
self.spendstate.loaded_schedule = schedule |
|
|
|
self.spendstate.loaded_schedule = schedule |
|
|
|
self.spendstate.schedule_name = os.path.basename(str(firstarg[0])) |
|
|
|
self.spendstate.schedule_name = os.path.basename(filename) |
|
|
|
self.updateSchedView() |
|
|
|
self.updateSchedView() |
|
|
|
if self.spendstate.schedule_name == "TUMBLE.schedule": |
|
|
|
if self.spendstate.schedule_name == "TUMBLE.schedule": |
|
|
|
|
|
|
|
|
|
|
|
@ -596,7 +629,8 @@ class SpendTab(QWidget): |
|
|
|
current_schedule_layout.addWidget(self.sched_view) |
|
|
|
current_schedule_layout.addWidget(self.sched_view) |
|
|
|
sch_layout.addLayout(current_schedule_layout, 0, 0, 1, 1) |
|
|
|
sch_layout.addLayout(current_schedule_layout, 0, 0, 1, 1) |
|
|
|
self.schedule_set_button = QPushButton('Choose schedule file') |
|
|
|
self.schedule_set_button = QPushButton('Choose schedule file') |
|
|
|
self.schedule_set_button.clicked.connect(self.selectSchedule) |
|
|
|
self.schedule_set_button.clicked.connect( |
|
|
|
|
|
|
|
lambda: asyncio.ensure_future(self.selectSchedule())) |
|
|
|
self.schedule_generate_button = QPushButton('Generate tumble schedule') |
|
|
|
self.schedule_generate_button = QPushButton('Generate tumble schedule') |
|
|
|
self.schedule_generate_button.clicked.connect( |
|
|
|
self.schedule_generate_button.clicked.connect( |
|
|
|
lambda: asyncio.ensure_future(self.generateTumbleSchedule())) |
|
|
|
lambda: asyncio.ensure_future(self.generateTumbleSchedule())) |
|
|
|
@ -2148,14 +2182,22 @@ class JMMainWindow(QMainWindow): |
|
|
|
if jm_single().config.get("BLOCKCHAIN", "blockchain_source") != "regtest": |
|
|
|
if jm_single().config.get("BLOCKCHAIN", "blockchain_source") != "regtest": |
|
|
|
# guaranteed to exist as load_program_config was called on startup: |
|
|
|
# guaranteed to exist as load_program_config was called on startup: |
|
|
|
wallets_path = os.path.join(jm_single().datadir, 'wallets') |
|
|
|
wallets_path = os.path.join(jm_single().datadir, 'wallets') |
|
|
|
firstarg = QFileDialog.getOpenFileName(self, |
|
|
|
d = JMFileDialog(self) |
|
|
|
'Choose Wallet File', |
|
|
|
d.setOptions(QFileDialog.DontUseNativeDialog) |
|
|
|
wallets_path, |
|
|
|
d.setWindowTitle('Choose Wallet File') |
|
|
|
options=QFileDialog.DontUseNativeDialog) |
|
|
|
d.setDirectory(wallets_path) |
|
|
|
#TODO validate the file looks vaguely like a wallet file |
|
|
|
d.finished.connect(d.on_finished) |
|
|
|
log.debug('Looking for wallet in: ' + str(firstarg)) |
|
|
|
d.open() |
|
|
|
if not firstarg or not firstarg[0]: |
|
|
|
await d.result_fut |
|
|
|
|
|
|
|
result = d.result_fut.result() |
|
|
|
|
|
|
|
if result != QDialog.Accepted: |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
filenames = d.selectedFiles() |
|
|
|
|
|
|
|
if not filenames or len(filenames) != 1: |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
filename = filenames[0] |
|
|
|
|
|
|
|
#TODO validate the file looks vaguely like a wallet file |
|
|
|
|
|
|
|
log.debug('Looking for wallet in: ' + str(filename)) |
|
|
|
decrypted = False |
|
|
|
decrypted = False |
|
|
|
while not decrypted: |
|
|
|
while not decrypted: |
|
|
|
text, ok = QInputDialog.getText(self, |
|
|
|
text, ok = QInputDialog.getText(self, |
|
|
|
@ -2167,7 +2209,7 @@ class JMMainWindow(QMainWindow): |
|
|
|
pwd = str(text).strip() |
|
|
|
pwd = str(text).strip() |
|
|
|
try: |
|
|
|
try: |
|
|
|
decrypted = await self.loadWalletFromBlockchain( |
|
|
|
decrypted = await self.loadWalletFromBlockchain( |
|
|
|
firstarg[0], pwd) |
|
|
|
filename, pwd) |
|
|
|
except Exception as e: |
|
|
|
except Exception as e: |
|
|
|
await JMQtMessageBox(self, str(e), |
|
|
|
await JMQtMessageBox(self, str(e), |
|
|
|
mbtype='warn', title="Error") |
|
|
|
mbtype='warn', title="Error") |
|
|
|
@ -2182,20 +2224,23 @@ class JMMainWindow(QMainWindow): |
|
|
|
'Load Testnet wallet', |
|
|
|
'Load Testnet wallet', |
|
|
|
'Enter a testnet seed:', |
|
|
|
'Enter a testnet seed:', |
|
|
|
QLineEdit.Normal) |
|
|
|
QLineEdit.Normal) |
|
|
|
if not ok: |
|
|
|
testnet_seed = testnet_seed.strip() |
|
|
|
|
|
|
|
if not ok or not testnet_seed: |
|
|
|
|
|
|
|
await JMQtMessageBox(self, "No seed entered, aborting", |
|
|
|
|
|
|
|
mbtype='warn', title="Error") |
|
|
|
return |
|
|
|
return |
|
|
|
firstarg = str(testnet_seed) |
|
|
|
filename = testnet_seed |
|
|
|
pwd = None |
|
|
|
pwd = None |
|
|
|
#ignore return value as there is no decryption failure possible |
|
|
|
#ignore return value as there is no decryption failure possible |
|
|
|
await self.loadWalletFromBlockchain(firstarg, pwd) |
|
|
|
await self.loadWalletFromBlockchain(filename, pwd) |
|
|
|
|
|
|
|
|
|
|
|
async def loadWalletFromBlockchain(self, firstarg=None, |
|
|
|
async def loadWalletFromBlockchain(self, filename=None, |
|
|
|
pwd=None, rethrow=False): |
|
|
|
pwd=None, rethrow=False): |
|
|
|
if firstarg: |
|
|
|
if filename: |
|
|
|
wallet_path = get_wallet_path(str(firstarg), None) |
|
|
|
wallet_path = get_wallet_path(str(filename), None) |
|
|
|
try: |
|
|
|
try: |
|
|
|
wallet = await open_test_wallet_maybe( |
|
|
|
wallet = await open_test_wallet_maybe( |
|
|
|
wallet_path, str(firstarg), None, ask_for_password=False, |
|
|
|
wallet_path, str(filename), None, ask_for_password=False, |
|
|
|
password=pwd.encode('utf-8') if pwd else None, |
|
|
|
password=pwd.encode('utf-8') if pwd else None, |
|
|
|
gap_limit=jm_single().config.getint("GUI", "gaplimit")) |
|
|
|
gap_limit=jm_single().config.getint("GUI", "gaplimit")) |
|
|
|
except RetryableStorageError as e: |
|
|
|
except RetryableStorageError as e: |
|
|
|
@ -2206,7 +2251,7 @@ class JMMainWindow(QMainWindow): |
|
|
|
mbtype='warn', title="Error") |
|
|
|
mbtype='warn', title="Error") |
|
|
|
return False |
|
|
|
return False |
|
|
|
# only used for GUI display on regtest: |
|
|
|
# only used for GUI display on regtest: |
|
|
|
self.testwalletname = wallet.seed = str(firstarg) |
|
|
|
self.testwalletname = wallet.seed = str(filename) |
|
|
|
if 'listunspent_args' not in jm_single().config.options('POLICY'): |
|
|
|
if 'listunspent_args' not in jm_single().config.options('POLICY'): |
|
|
|
jm_single().config.set('POLICY', 'listunspent_args', '[0]') |
|
|
|
jm_single().config.set('POLICY', 'listunspent_args', '[0]') |
|
|
|
assert wallet, "No wallet loaded" |
|
|
|
assert wallet, "No wallet loaded" |
|
|
|
@ -2293,6 +2338,7 @@ class JMMainWindow(QMainWindow): |
|
|
|
log.debug('generating wallet') |
|
|
|
log.debug('generating wallet') |
|
|
|
if jm_single().config.get("BLOCKCHAIN", "blockchain_source") == "regtest": |
|
|
|
if jm_single().config.get("BLOCKCHAIN", "blockchain_source") == "regtest": |
|
|
|
seed = await self.getTestnetSeed() |
|
|
|
seed = await self.getTestnetSeed() |
|
|
|
|
|
|
|
if seed: |
|
|
|
await self.selectWallet(testnet_seed=seed) |
|
|
|
await self.selectWallet(testnet_seed=seed) |
|
|
|
else: |
|
|
|
else: |
|
|
|
await self.initWallet() |
|
|
|
await self.initWallet() |
|
|
|
@ -2334,11 +2380,12 @@ class JMMainWindow(QMainWindow): |
|
|
|
async def getTestnetSeed(self): |
|
|
|
async def getTestnetSeed(self): |
|
|
|
text, ok = QInputDialog.getText( |
|
|
|
text, ok = QInputDialog.getText( |
|
|
|
self, 'Testnet seed', 'Enter a 32 char hex string as seed:') |
|
|
|
self, 'Testnet seed', 'Enter a 32 char hex string as seed:') |
|
|
|
|
|
|
|
text = text.strip() |
|
|
|
if not ok or not text: |
|
|
|
if not ok or not text: |
|
|
|
await JMQtMessageBox(self, "No seed entered, aborting", |
|
|
|
await JMQtMessageBox(self, "No seed entered, aborting", |
|
|
|
mbtype='warn', title="Error") |
|
|
|
mbtype='warn', title="Error") |
|
|
|
return |
|
|
|
return |
|
|
|
return str(text).strip() |
|
|
|
return text |
|
|
|
|
|
|
|
|
|
|
|
def showSeedDialog(self): |
|
|
|
def showSeedDialog(self): |
|
|
|
if not self.wallet_service: |
|
|
|
if not self.wallet_service: |
|
|
|
@ -2440,6 +2487,7 @@ class JMMainWindow(QMainWindow): |
|
|
|
await self.loadWalletFromBlockchain( |
|
|
|
await self.loadWalletFromBlockchain( |
|
|
|
self.walletname, pwd=self.textpassword) |
|
|
|
self.walletname, pwd=self.textpassword) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_wallet_printout(wallet_service): |
|
|
|
async def get_wallet_printout(wallet_service): |
|
|
|
"""Given a WalletService object, retrieve the list of |
|
|
|
"""Given a WalletService object, retrieve the list of |
|
|
|
addresses and corresponding balances to be displayed. |
|
|
|
addresses and corresponding balances to be displayed. |
|
|
|
|