From 12cc65abbd4ccc12dfbc83426d65b0fa29faec86 Mon Sep 17 00:00:00 2001 From: "Eagle[TM]" Date: Wed, 19 Dec 2012 09:41:23 +0100 Subject: [PATCH 001/124] add sockettimeout to header download by HTTP --- lib/verifier.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/verifier.py b/lib/verifier.py index 6e35a9a9..fbe1667f 100644 --- a/lib/verifier.py +++ b/lib/verifier.py @@ -298,7 +298,8 @@ class WalletVerifier(threading.Thread): return try: - import urllib + import urllib, socket + socket.setdefaulttimeout(30) print_error("downloading ", self.headers_url ) urllib.urlretrieve(self.headers_url, filename) except: From 9f4797c870a4fba6423342bb213e4c79fabe1707 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 20 Dec 2012 14:39:33 +0100 Subject: [PATCH 002/124] fix: restore timer in deseeded wallets --- lib/gui_qt.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 661f7be4..ddd7bc45 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -346,9 +346,8 @@ class ElectrumWindow(QMainWindow): self.qr_window = None def connect_slots(self, sender): - if self.wallet.seed: - self.connect(sender, QtCore.SIGNAL('timersignal'), self.timer_actions) - self.previous_payto_e='' + self.connect(sender, QtCore.SIGNAL('timersignal'), self.timer_actions) + self.previous_payto_e='' def timer_actions(self): if self.qr_window: From df1d7152fd7d5476a32cd4e5541d021e3e0b2900 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 28 Dec 2012 17:57:33 +0100 Subject: [PATCH 003/124] fix address balance (received coins) --- lib/wallet.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/wallet.py b/lib/wallet.py index 0953b033..31b50269 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -485,15 +485,16 @@ class Wallet: h = self.history.get(address,[]) if h == ['*']: return 0,0 c = u = 0 - received_coins = [] - + received_coins = [] # list of coins received at address + for tx_hash, tx_height in h: d = self.transactions.get(tx_hash) if not d: continue for item in d.get('outputs'): addr = item.get('address') - key = tx_hash + ':%d'%item['index'] - received_coins.append(key) + if addr == address: + key = tx_hash + ':%d'%item['index'] + received_coins.append(key) for tx_hash, tx_height in h: d = self.transactions.get(tx_hash) From 2666929a449e3bde6cc2da5ca8c6626771cc9bd9 Mon Sep 17 00:00:00 2001 From: Maran Date: Fri, 28 Dec 2012 18:16:28 +0100 Subject: [PATCH 004/124] Fix contacts auto complete in lite gui --- lib/gui_lite.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 5d267372..7bae9a89 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -209,12 +209,12 @@ class MiniWindow(QDialog): # Bitcoin address code self.address_input = QLineEdit() - self.address_input.setPlaceholderText(_("Enter a Bitcoin address...")) + self.address_input.setPlaceholderText(_("Enter a Bitcoin address or contact")) self.address_input.setObjectName("address_input") self.address_input.setFocusPolicy(Qt.ClickFocus) - self.address_input.textEdited.connect(self.address_field_changed) + self.address_input.textChanged.connect(self.address_field_changed) resize_line_edit_width(self.address_input, "1BtaFUr3qVvAmwrsuDuu5zk6e4s2rxd2Gy") @@ -499,6 +499,13 @@ class MiniWindow(QDialog): self.send_button.setDisabled(True) def address_field_changed(self, address): + # label or alias, with address in brackets + match2 = re.match("(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>", + address) + if match2: + address = match2.group(2) + self.address_input.setText(address) + if self.actuator.is_valid(address): self.check_button_status() self.address_input.setProperty("isValid", True) @@ -879,6 +886,7 @@ class MiniActuator: def is_valid(self, address): """Check if bitcoin address is valid.""" + return self.wallet.is_valid(address) def copy_master_public_key(self): From d48d7aeea66108e3bfd84c8b26153458b0bae732 Mon Sep 17 00:00:00 2001 From: Maran Date: Fri, 28 Dec 2012 18:17:45 +0100 Subject: [PATCH 005/124] Add bugfix to release file --- RELEASE-NOTES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 9cc7fb39..67f8180a 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,3 +1,7 @@ +# Release 1.5.8 (Not released) +== Lite GUI +* (Bug) Sending to auto-completed contacts works again + # Release 1.5.7 (18-12-2012) == Core From c4f1acade69ea972dde66947252b197d5c18dcf1 Mon Sep 17 00:00:00 2001 From: Maran Date: Fri, 28 Dec 2012 18:27:31 +0100 Subject: [PATCH 006/124] Added version to lite gui tile --- RELEASE-NOTES | 1 + lib/gui_lite.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 67f8180a..bbe25be0 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,6 +1,7 @@ # Release 1.5.8 (Not released) == Lite GUI * (Bug) Sending to auto-completed contacts works again +* (Chore) Added version number to title bar # Release 1.5.7 (18-12-2012) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 7bae9a89..a1f576e0 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -31,6 +31,7 @@ import util import csv import datetime +from version import ELECTRUM_VERSION as electrum_version from wallet import format_satoshis import gui_qt import shutil @@ -447,7 +448,7 @@ class MiniWindow(QDialog): quote_text = "(%s)" % quote_text btc_balance = "%.2f" % (btc_balance / bitcoin(1)) self.balance_label.set_balance_text(btc_balance, quote_text) - self.setWindowTitle("Electrum - %s BTC" % btc_balance) + self.setWindowTitle("Electrum %s - %s BTC" % (electrum_version, btc_balance)) def amount_input_changed(self, amount_text): """Update the number of bitcoins displayed.""" From 4978eff7f63de3f2aad97360a3d14460c97a15eb Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 2 Jan 2013 10:35:35 +0100 Subject: [PATCH 007/124] fix for command line history (EagleTM) --- lib/wallet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wallet.py b/lib/wallet.py index 31b50269..a7b89c29 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -700,7 +700,7 @@ class Wallet: balance = c + u - balance for tx in history: tx_hash = tx['tx_hash'] - conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else None + conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else None, None is_mine, value, fee = self.get_tx_value(tx_hash) if value is not None: balance += value From 4f0f187e5660c1f22f3a8826296ef2a153ac1f84 Mon Sep 17 00:00:00 2001 From: "Eagle[TM]" Date: Wed, 2 Jan 2013 02:18:13 +0100 Subject: [PATCH 008/124] Update default servers with pruning flag --- lib/interface.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/lib/interface.py b/lib/interface.py index d7235a0a..635afcd1 100644 --- a/lib/interface.py +++ b/lib/interface.py @@ -26,17 +26,13 @@ from util import print_error DEFAULT_TIMEOUT = 5 DEFAULT_SERVERS = [ - #'electrum.bitcoins.sk:50001:t', - #'uncle-enzo.info:50001:t', - #'electrum.bitfoo.org:50001:t', - #'webbtc.net:50001:t', - 'electrum.bitcoin.cz:50001:t', - 'electrum.novit.ro:50001:t', - 'electrum.be:50001:t', - 'electrum.bysh.me:50001:t', - 'electrum.pdmc.net:50001:t', - 'electrum.no-ip.org:50001:t', - 'ecdsa.org:50001:t' + 'electrum.bitcoin.cz:p:50001:t', + 'electrum.novit.ro:p:50001:t', + 'electrum.be::50001:t', + 'electrum.bysh.me::50001:t', + 'electrum.pdmc.net:p:50001:t', + 'electrum.no-ip.org::50001:t', + 'ecdsa.org:p:50001:t' ] proxy_modes = ['socks4', 'socks5', 'http'] @@ -507,8 +503,8 @@ class Interface(threading.Thread): if not self.servers: servers_list = {} for x in DEFAULT_SERVERS: - h,port,protocol = x.split(':') - servers_list[h] = {'ports':[(protocol,port)]} + h,pruning,port,protocol = x.split(':') + servers_list[h] = {'ports':[(protocol,port)], 'pruning':pruning} else: servers_list = self.servers From c64fc4f6a69ac9432670e3a41a8d83807b101f20 Mon Sep 17 00:00:00 2001 From: "Eagle[TM]" Date: Wed, 2 Jan 2013 02:28:25 +0100 Subject: [PATCH 009/124] updating release notes for 1.5.8 core --- RELEASE-NOTES | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index bbe25be0..236339ca 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,4 +1,11 @@ -# Release 1.5.8 (Not released) +# Release 1.5.8 (02-01-2013) + +== Core +* (Bug) Fix pending address balance on received coins for pruning servers +* (Chore) Restore timer for deseeded wallets +* (Chore) Update built-in default server with pruning flag +* (Chore) Add timeout to blockchain headers file download by HTTP + == Lite GUI * (Bug) Sending to auto-completed contacts works again * (Chore) Added version number to title bar From c3b3ecb016aa7787d323321cb260b1485754031c Mon Sep 17 00:00:00 2001 From: "Eagle[TM]" Date: Wed, 2 Jan 2013 02:51:27 +0100 Subject: [PATCH 010/124] add updates to release notes for 1.5.8 --- RELEASE-NOTES | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 236339ca..46951b31 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -2,6 +2,7 @@ == Core * (Bug) Fix pending address balance on received coins for pruning servers +* (Bug) Fix history command line option to show output again (regression by SPV) * (Chore) Restore timer for deseeded wallets * (Chore) Update built-in default server with pruning flag * (Chore) Add timeout to blockchain headers file download by HTTP From 777f423b7a85b98018a8dcab96f18f60f9f6e82d Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 2 Jan 2013 11:39:44 +0100 Subject: [PATCH 011/124] parentheses --- lib/wallet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wallet.py b/lib/wallet.py index a7b89c29..5b9837cc 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -700,7 +700,7 @@ class Wallet: balance = c + u - balance for tx in history: tx_hash = tx['tx_hash'] - conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else None, None + conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None) is_mine, value, fee = self.get_tx_value(tx_hash) if value is not None: balance += value From 9bf31f4d71ebb88000d2034a8deaf70a208f334c Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 2 Jan 2013 11:42:09 +0100 Subject: [PATCH 012/124] Made the bug reporting link clickable in lib/gui_lite.py --- lib/gui_lite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index a1f576e0..021e2694 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -556,7 +556,7 @@ class MiniWindow(QDialog): def show_report_bug(self): QMessageBox.information(self, "Electrum - " + _("Reporting Bugs"), - _("Please report any bugs as issues on github: https://github.com/spesmilo/electrum/issues")) + _("Please report any bugs as issues on github: https://github.com/spesmilo/electrum/issues")) def show_history(self, toggle_state): if toggle_state: From accbf7ea8244e7d22171d060c982f70759879979 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 2 Jan 2013 13:39:50 +0100 Subject: [PATCH 013/124] fix: exit if no password is provided when needed --- electrum | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/electrum b/electrum index be0a944a..132c5931 100755 --- a/electrum +++ b/electrum @@ -378,13 +378,19 @@ if __name__ == '__main__': # commands needing password if cmd in protected_commands or ( cmd=='addresses' and options.show_keys): - password = prompt_password('Password:', False) if wallet.use_encryption and not is_temporary else None - # check password - try: - wallet.pw_decode( wallet.seed, password) - except: - print_msg("Error: This password does not decode this wallet.") - exit(1) + if wallet.use_encryption and not is_temporary: + password = prompt_password('Password:', False) + if not password: + print_msg("Error: Password required") + exit(1) + # check password + try: + wallet.pw_decode( wallet.seed, password) + except: + print_msg("Error: This password does not decode this wallet.") + exit(1) + else: + password = None if cmd == 'import': # See if they specificed a key on the cmd line, if not prompt From 83c008b6f8d73c1a3e5c3503a160958eee8623bc Mon Sep 17 00:00:00 2001 From: "Eagle[TM]" Date: Wed, 2 Jan 2013 14:50:52 +0100 Subject: [PATCH 014/124] revert pruning flag for default servers (too much complexity) --- RELEASE-NOTES | 1 - lib/interface.py | 22 +++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 46951b31..0ed56779 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -4,7 +4,6 @@ * (Bug) Fix pending address balance on received coins for pruning servers * (Bug) Fix history command line option to show output again (regression by SPV) * (Chore) Restore timer for deseeded wallets -* (Chore) Update built-in default server with pruning flag * (Chore) Add timeout to blockchain headers file download by HTTP == Lite GUI diff --git a/lib/interface.py b/lib/interface.py index 635afcd1..d7235a0a 100644 --- a/lib/interface.py +++ b/lib/interface.py @@ -26,13 +26,17 @@ from util import print_error DEFAULT_TIMEOUT = 5 DEFAULT_SERVERS = [ - 'electrum.bitcoin.cz:p:50001:t', - 'electrum.novit.ro:p:50001:t', - 'electrum.be::50001:t', - 'electrum.bysh.me::50001:t', - 'electrum.pdmc.net:p:50001:t', - 'electrum.no-ip.org::50001:t', - 'ecdsa.org:p:50001:t' + #'electrum.bitcoins.sk:50001:t', + #'uncle-enzo.info:50001:t', + #'electrum.bitfoo.org:50001:t', + #'webbtc.net:50001:t', + 'electrum.bitcoin.cz:50001:t', + 'electrum.novit.ro:50001:t', + 'electrum.be:50001:t', + 'electrum.bysh.me:50001:t', + 'electrum.pdmc.net:50001:t', + 'electrum.no-ip.org:50001:t', + 'ecdsa.org:50001:t' ] proxy_modes = ['socks4', 'socks5', 'http'] @@ -503,8 +507,8 @@ class Interface(threading.Thread): if not self.servers: servers_list = {} for x in DEFAULT_SERVERS: - h,pruning,port,protocol = x.split(':') - servers_list[h] = {'ports':[(protocol,port)], 'pruning':pruning} + h,port,protocol = x.split(':') + servers_list[h] = {'ports':[(protocol,port)]} else: servers_list = self.servers From 66fa7b610bdfb2e07fe99fa656c93a08d2190e29 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 2 Jan 2013 16:03:54 +0100 Subject: [PATCH 015/124] option to set gui language from command line --- electrum | 2 ++ lib/__init__.py | 1 + lib/i18n.py | 21 +++++++++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/electrum b/electrum index 132c5931..c571258c 100755 --- a/electrum +++ b/electrum @@ -138,6 +138,7 @@ def arg_parser(): parser.add_option("-p", "--proxy", dest="proxy", default=None, help="set proxy [type:]host[:port], where type is socks4,socks5 or http") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="show debugging information") parser.add_option("-P", "--portable", action="store_true", dest="portable", default=False, help="portable wallet") + parser.add_option("-L", "--lang", dest="language", default=None, help="defaut language used in GUI") return parser @@ -146,6 +147,7 @@ if __name__ == '__main__': parser = arg_parser() options, args = parser.parse_args() set_verbosity(options.verbose) + set_language(options.language) # config is an object passed to the various constructors (wallet, interface, gui) if 'ANDROID_DATA' in os.environ: diff --git a/lib/__init__.py b/lib/__init__.py index abbc9553..1506f3e7 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,5 +1,6 @@ from version import ELECTRUM_VERSION from util import format_satoshis, print_msg, print_error, set_verbosity +from i18n import set_language from wallet import Wallet, WalletSynchronizer from verifier import WalletVerifier from interface import Interface, pick_random_server, DEFAULT_SERVERS diff --git a/lib/i18n.py b/lib/i18n.py index 2c0a6449..c3903167 100644 --- a/lib/i18n.py +++ b/lib/i18n.py @@ -16,10 +16,23 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import gettext +import gettext, os -LOCALE_DIR = '/usr/share/locale' -#LOCALE_DIR = './locale' +if os.path.exists('./locale'): + LOCALE_DIR = './locale' +else: + LOCALE_DIR = '/usr/share/locale' + +print LOCALE_DIR language = gettext.translation('electrum', LOCALE_DIR, fallback = True) -_ = language.ugettext + +def _(x): + global language + return language.ugettext(x) + +def set_language(x): + global language + if x: language = gettext.translation('electrum', LOCALE_DIR, fallback = True, languages=[x]) + + From 1f71e5e225d957c800ba83ba680596e90e77bc99 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 2 Jan 2013 16:12:00 +0100 Subject: [PATCH 016/124] call set_language only if gui is used. --- electrum | 2 +- lib/i18n.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/electrum b/electrum index c571258c..83a5869a 100755 --- a/electrum +++ b/electrum @@ -147,7 +147,6 @@ if __name__ == '__main__': parser = arg_parser() options, args = parser.parse_args() set_verbosity(options.verbose) - set_language(options.language) # config is an object passed to the various constructors (wallet, interface, gui) if 'ANDROID_DATA' in os.environ: @@ -208,6 +207,7 @@ if __name__ == '__main__': interface.start() interface.send([('server.peers.subscribe',[])]) + set_language(config.get('language')) gui = gui.ElectrumGui(wallet, config) found = config.wallet_file_exists diff --git a/lib/i18n.py b/lib/i18n.py index c3903167..7ef4458f 100644 --- a/lib/i18n.py +++ b/lib/i18n.py @@ -23,8 +23,6 @@ if os.path.exists('./locale'): else: LOCALE_DIR = '/usr/share/locale' -print LOCALE_DIR - language = gettext.translation('electrum', LOCALE_DIR, fallback = True) def _(x): From 5789707f35bf0bc423bbf6f2077fb0bb4a7d3d88 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 2 Jan 2013 16:57:18 +0100 Subject: [PATCH 017/124] select language in gui --- lib/gui_qt.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index ddd7bc45..6e37239b 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1562,6 +1562,21 @@ class ElectrumWindow(QMainWindow): if not self.config.is_modifiable('gui'): for w in [gui_combo, gui_label]: w.setEnabled(False) + lang_label=QLabel(_('Language') + ':') + grid.addWidget(lang_label , 8, 0) + lang_combo = QComboBox() + languages = ['', 'br', 'cs', 'de', 'eo', 'en', 'es', 'fr', 'it', 'lv', 'nl', 'ru', 'sl', 'vi', 'zh'] + lang_combo.addItems(languages) + try: + index = languages.index(self.config.get("language",'')) + except: + index = 0 + lang_combo.setCurrentIndex(index) + grid.addWidget(lang_combo, 8, 1) + grid.addWidget(HelpButton(_('Select which language is used in the GUI (after restart). ')), 8, 2) + if not self.config.is_modifiable('language'): + for w in [lang_combo, lang_label]: w.setEnabled(False) + vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) @@ -1612,6 +1627,7 @@ class ElectrumWindow(QMainWindow): QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK')) self.config.set_key("gui", str(gui_combo.currentText()).lower(), True) + self.config.set_key("language", languages[lang_combo.currentIndex()], True) From d29aeb890f2c736a826127f3712200ade9ddfb8a Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 2 Jan 2013 18:41:52 +0100 Subject: [PATCH 018/124] populate contacts with donation address --- lib/wallet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wallet.py b/lib/wallet.py index 5b9837cc..648b30d3 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -64,13 +64,13 @@ class Wallet: self.addresses = config.get('addresses', []) # receiving addresses visible for user self.change_addresses = config.get('change_addresses', []) # addresses used as change self.seed = config.get('seed', '') # encrypted - self.labels = config.get('labels',{}) # labels for addresses and transactions + self.labels = config.get('labels',{'1NmduGNyC5XejoysbuioodCN3jR3yf64xM':'Electrum donation address'}) self.aliases = config.get('aliases', {}) # aliases for addresses self.authorities = config.get('authorities', {}) # trusted addresses self.frozen_addresses = config.get('frozen_addresses',[]) self.prioritized_addresses = config.get('prioritized_addresses',[]) self.receipts = config.get('receipts',{}) # signed URIs - self.addressbook = config.get('contacts', []) # outgoing addresses, for payments + self.addressbook = config.get('contacts', ['1NmduGNyC5XejoysbuioodCN3jR3yf64xM']) self.imported_keys = config.get('imported_keys',{}) self.history = config.get('addr_history',{}) # address -> list(txid, height) self.transactions = config.get('transactions',{}) # txid -> deserialised From cefb1445de457d5ac0fd196f239a47a726823f92 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 2 Jan 2013 18:50:55 +0100 Subject: [PATCH 019/124] version 1.5.8 --- RELEASE-NOTES | 5 ++++- lib/version.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 0ed56779..8e75c6c6 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -3,13 +3,16 @@ == Core * (Bug) Fix pending address balance on received coins for pruning servers * (Bug) Fix history command line option to show output again (regression by SPV) -* (Chore) Restore timer for deseeded wallets * (Chore) Add timeout to blockchain headers file download by HTTP +* (Feature) new option: -L, --language: default language used in GUI. == Lite GUI * (Bug) Sending to auto-completed contacts works again * (Chore) Added version number to title bar +== Classic GUI +* (Feature) Language selector in options. + # Release 1.5.7 (18-12-2012) == Core diff --git a/lib/version.py b/lib/version.py index fbd2b240..269e80fe 100644 --- a/lib/version.py +++ b/lib/version.py @@ -1,4 +1,4 @@ -ELECTRUM_VERSION = "1.5.7" # version of the client package +ELECTRUM_VERSION = "1.5.8" # version of the client package PROTOCOL_VERSION = '0.6' # protocol version requested SEED_VERSION = 4 # bump this everytime the seed generation is modified -TRANSLATION_ID = 33853 # version of the wiki page +TRANSLATION_ID = 34259 # version of the wiki page From bceb8ae218c2bffa3c03b6566872c700042fae80 Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 2 Jan 2013 20:34:32 +0100 Subject: [PATCH 020/124] removed old donation address on lite gui --- lib/gui_lite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 021e2694..c9e06d6a 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -552,7 +552,7 @@ class MiniWindow(QDialog): def show_about(self): QMessageBox.about(self, "Electrum", - _("Electrum's focus is speed, with low resource usage and simplifying Bitcoin. You do not need to perform regular backups, because your wallet can be recovered from a secret phrase that you can memorize or write on paper. Startup times are instant because it operates in conjuction with high-performance servers that handle the most complicated parts of the Bitcoin system.\n\nSend donations to 1JwTMv4GWaPdf931N6LNPJeZBfZgZJ3zX1")) + _("Electrum's focus is speed, with low resource usage and simplifying Bitcoin. You do not need to perform regular backups, because your wallet can be recovered from a secret phrase that you can memorize or write on paper. Startup times are instant because it operates in conjuction with high-performance servers that handle the most complicated parts of the Bitcoin system.")) def show_report_bug(self): QMessageBox.information(self, "Electrum - " + _("Reporting Bugs"), From 6a82123e6e6592acf307ef6248df7a1cab9acd80 Mon Sep 17 00:00:00 2001 From: Charles Lehner Date: Wed, 2 Jan 2013 18:05:42 -0500 Subject: [PATCH 021/124] Use find instead of mdfind in Mac build script. This is for compatibility with Mac OS X 10.5 Leopard, which does not have mdfind -name. --- setup-release.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup-release.py b/setup-release.py index 33314de8..608ad4d0 100644 --- a/setup-release.py +++ b/setup-release.py @@ -60,7 +60,7 @@ if sys.platform == 'darwin': qt_menu_location = "/opt/local/lib/Resources/qt_menu.nib" else: # No dice? Then let's try the brew version - qt_menu_location = os.popen("mdfind -name qt_menu.nib | grep Cellar | head").read() + qt_menu_location = os.popen("find /usr/local/Cellar -name qt_menu.nib | head").read() qt_menu_location = re.sub('\n','', qt_menu_location) if(len(qt_menu_location) == 0): From b0f543826fcde3fd8ec227d993c32a94b39f4a69 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 12:23:59 +0100 Subject: [PATCH 022/124] improved settings dialog using tabs --- lib/gui_qt.py | 66 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 6e37239b..676f1235 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1489,6 +1489,7 @@ class ElectrumWindow(QMainWindow): def settings_dialog(self): d = QDialog(self) + d.setWindowTitle(_('Electrum Settings')) d.setModal(1) vbox = QVBoxLayout() msg = _('Here are the settings of your wallet.') + '\n'\ @@ -1498,46 +1499,60 @@ class ElectrumWindow(QMainWindow): label.setFixedWidth(250) label.setWordWrap(True) label.setAlignment(Qt.AlignJustify) + + tabs = QTabWidget(self) + vbox.addWidget(tabs) + vbox.addWidget(label) - grid = QGridLayout() - grid.setSpacing(8) - vbox.addLayout(grid) + tab = QWidget() + grid_wallet = QGridLayout(tab) + grid_wallet.setColumnStretch(0,1) + tabs.addTab(tab, _('Wallet') ) + + tab2 = QWidget() + grid_ui = QGridLayout(tab2) + grid_ui.setColumnStretch(0,1) + tabs.addTab(tab2, _('Display') ) fee_label = QLabel(_('Transaction fee')) - grid.addWidget(fee_label, 2, 0) + grid_wallet.addWidget(fee_label, 2, 0) fee_e = QLineEdit() fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) ) - grid.addWidget(fee_e, 2, 1) + grid_wallet.addWidget(fee_e, 2, 1) msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \ + _('Recommended value') + ': 0.001' - grid.addWidget(HelpButton(msg), 2, 2) + grid_wallet.addWidget(HelpButton(msg), 2, 2) fee_e.textChanged.connect(lambda: numbify(fee_e,False)) if not self.config.is_modifiable('fee'): for w in [fee_e, fee_label]: w.setEnabled(False) nz_label = QLabel(_('Display zeros')) - grid.addWidget(nz_label, 3, 0) + grid_ui.addWidget(nz_label, 3, 0) nz_e = QLineEdit() nz_e.setText("%d"% self.wallet.num_zeros) - grid.addWidget(nz_e, 3, 1) + grid_ui.addWidget(nz_e, 3, 1) msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"') - grid.addWidget(HelpButton(msg), 3, 2) + grid_ui.addWidget(HelpButton(msg), 3, 2) nz_e.textChanged.connect(lambda: numbify(nz_e,True)) if not self.config.is_modifiable('num_zeros'): for w in [nz_e, nz_label]: w.setEnabled(False) - usechange_cb = QCheckBox(_('Use change addresses')) - grid.addWidget(usechange_cb, 5, 0) - usechange_cb.setChecked(self.wallet.use_change) - grid.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 5, 2) - if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False) + + usechange_label = QLabel(_('Use change addresses')) + grid_wallet.addWidget(usechange_label, 5, 0) + usechange_combo = QComboBox() + usechange_combo.addItems(['Yes', 'No']) + usechange_combo.setCurrentIndex(not self.wallet.use_change) + grid_wallet.addWidget(usechange_combo, 5, 1) + grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 5, 2) + if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False) gap_label = QLabel(_('Gap limit')) - grid.addWidget(gap_label, 6, 0) + grid_wallet.addWidget(gap_label, 6, 0) gap_e = QLineEdit() gap_e.setText("%d"% self.wallet.gap_limit) - grid.addWidget(gap_e, 6, 1) + grid_wallet.addWidget(gap_e, 6, 1) msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \ + _('You may increase it if you need more receiving addresses.') + '\n\n' \ + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \ @@ -1545,25 +1560,25 @@ class ElectrumWindow(QMainWindow): + _('Warning') + ': ' \ + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \ + _('Do not modify it if you do not understand what you are doing, or if you expect to recover your wallet without knowing it!') + '\n\n' - grid.addWidget(HelpButton(msg), 6, 2) + grid_wallet.addWidget(HelpButton(msg), 6, 2) gap_e.textChanged.connect(lambda: numbify(nz_e,True)) if not self.config.is_modifiable('gap_limit'): for w in [gap_e, gap_label]: w.setEnabled(False) gui_label=QLabel(_('Default GUI') + ':') - grid.addWidget(gui_label , 7, 0) + grid_ui.addWidget(gui_label , 7, 0) gui_combo = QComboBox() gui_combo.addItems(['Lite', 'Classic', 'Gtk', 'Text']) index = gui_combo.findText(self.config.get("gui","classic").capitalize()) if index==-1: index = 1 gui_combo.setCurrentIndex(index) - grid.addWidget(gui_combo, 7, 1) - grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2) + grid_ui.addWidget(gui_combo, 7, 1) + grid_ui.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2) if not self.config.is_modifiable('gui'): for w in [gui_combo, gui_label]: w.setEnabled(False) lang_label=QLabel(_('Language') + ':') - grid.addWidget(lang_label , 8, 0) + grid_ui.addWidget(lang_label , 8, 0) lang_combo = QComboBox() languages = ['', 'br', 'cs', 'de', 'eo', 'en', 'es', 'fr', 'it', 'lv', 'nl', 'ru', 'sl', 'vi', 'zh'] lang_combo.addItems(languages) @@ -1572,8 +1587,8 @@ class ElectrumWindow(QMainWindow): except: index = 0 lang_combo.setCurrentIndex(index) - grid.addWidget(lang_combo, 8, 1) - grid.addWidget(HelpButton(_('Select which language is used in the GUI (after restart). ')), 8, 2) + grid_ui.addWidget(lang_combo, 8, 1) + grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart). ')), 8, 2) if not self.config.is_modifiable('language'): for w in [lang_combo, lang_label]: w.setEnabled(False) @@ -1608,8 +1623,9 @@ class ElectrumWindow(QMainWindow): self.update_history_tab() self.update_receive_tab() - if self.wallet.use_change != usechange_cb.isChecked(): - self.wallet.use_change = usechange_cb.isChecked() + usechange_result = usechange_combo.currentIndex() == 0 + if self.wallet.use_change != usechange_result: + self.wallet.use_change = usechange_result self.config.set_key('use_change', self.wallet.use_change, True) try: From ff24fda88ec971e0e569bfcb8901cb7cf2867ebd Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 14:18:45 +0100 Subject: [PATCH 023/124] mode Receive View selector into Settings dialog --- lib/gui_qt.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 676f1235..946aa015 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -877,11 +877,6 @@ class ElectrumWindow(QMainWindow): self.connect(l, SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)'), lambda a,b: self.recv_changed(a)) self.receive_list = l self.receive_buttons_hbox = hbox - view_combo = QComboBox() - view_combo.addItems([_('Simple View'), _('Detailed View'), _('Point of Sale')]) - view_combo.setCurrentIndex(self.receive_tab_mode) - hbox.addWidget(view_combo) - view_combo.currentIndexChanged.connect(self.receive_tab_set_mode) hbox.addStretch(1) return w @@ -1592,6 +1587,15 @@ class ElectrumWindow(QMainWindow): if not self.config.is_modifiable('language'): for w in [lang_combo, lang_label]: w.setEnabled(False) + + view_label=QLabel(_('Receive mode') + ':') + grid_ui.addWidget(view_label , 9, 0) + view_combo = QComboBox() + view_combo.addItems([_('Simple View'), _('Detailed View'), _('Point of Sale')]) + view_combo.setCurrentIndex(self.receive_tab_mode) + grid_ui.addWidget(view_combo, 9, 1) + grid_ui.addWidget(HelpButton(_('View mode for your "Receive" tab. ')), 9, 2) + vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) @@ -1645,6 +1649,7 @@ class ElectrumWindow(QMainWindow): self.config.set_key("gui", str(gui_combo.currentText()).lower(), True) self.config.set_key("language", languages[lang_combo.currentIndex()], True) + self.receive_tab_set_mode(view_combo.currentIndex()) @staticmethod From 702659a4c0e874954d2d40bc06b8ceb7c9e5dc1f Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 14:44:48 +0100 Subject: [PATCH 024/124] language names, and message box to instruct the user that they need to restart their client --- lib/gui_qt.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 946aa015..c2c6baf5 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1575,10 +1575,14 @@ class ElectrumWindow(QMainWindow): lang_label=QLabel(_('Language') + ':') grid_ui.addWidget(lang_label , 8, 0) lang_combo = QComboBox() - languages = ['', 'br', 'cs', 'de', 'eo', 'en', 'es', 'fr', 'it', 'lv', 'nl', 'ru', 'sl', 'vi', 'zh'] - lang_combo.addItems(languages) + languages = {'':_('Default'), 'br':_('Brasilian'), 'cs':_('Czech'), 'de':_('German'), + 'eo':_('Esperanto'), 'en':_('English'), 'es':_('Spanish'), 'fr':_('French'), + 'it':_('Italian'), 'lv':_('Latvian'), 'nl':_('Dutch'), 'ru':_('Russian'), + 'sl':_('Slovenian'), 'vi':_('Vietnamese'), 'zh':_('Chinese') + } + lang_combo.addItems(languages.values()) try: - index = languages.index(self.config.get("language",'')) + index = languages.keys().index(self.config.get("language",'')) except: index = 0 lang_combo.setCurrentIndex(index) @@ -1645,9 +1649,21 @@ class ElectrumWindow(QMainWindow): self.config.set_key('gap_limit', self.wallet.gap_limit, True) else: QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK')) - - self.config.set_key("gui", str(gui_combo.currentText()).lower(), True) - self.config.set_key("language", languages[lang_combo.currentIndex()], True) + + need_restart = False + + gui_request = str(gui_combo.currentText()).lower() + if gui_request != self.config.get('gui'): + self.config.set_key('gui', gui_request, True) + need_restart = True + + lang_request = languages.keys()[lang_combo.currentIndex()] + if lang_request != self.config.get('language'): + self.config.set_key("language", lang_request, True) + need_restart = True + + if need_restart: + QMessageBox.warning(self, _('Success'), _('Please restart Electrum to activate the new GUI settings'), _('OK')) self.receive_tab_set_mode(view_combo.currentIndex()) From b76f70a9f0308232dce4315871af5d7c2f3a9a45 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 15:00:04 +0100 Subject: [PATCH 025/124] hide meaning, in order to replace misplaced interpretations with a sentiment of helplessness --- lib/gui_qt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index c2c6baf5..d4fa4d15 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1742,7 +1742,7 @@ class ElectrumWindow(QMainWindow): servers_list_widget.setMaximumHeight(150) servers_list_widget.setColumnWidth(0, 240) for _host in servers_list.keys(): - _type = 'pruning' if servers_list[_host].get('pruning') else 'full' + _type = 'P' if servers_list[_host].get('pruning') else 'F' servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ _host, _type ] )) def change_server(host, protocol=None): From 4cede0c9c419f061b2d51c7fb532a0cdb02b7e85 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 15:13:12 +0100 Subject: [PATCH 026/124] simplify the 'simple' mode: hide prioritizations (they are not really useful if you don't see the balance of each address) --- lib/gui_qt.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index d4fa4d15..c7e2d8db 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -919,10 +919,12 @@ class ElectrumWindow(QMainWindow): menu.addAction(_("Edit label"), lambda: self.edit_label(True)) menu.addAction(_("Sign message"), lambda: self.sign_message(addr)) - t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze") - menu.addAction(t, lambda: self.toggle_freeze(addr)) - t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize") - menu.addAction(t, lambda: self.toggle_priority(addr)) + if self.receive_tab_mode == 1: + t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze") + menu.addAction(t, lambda: self.toggle_freeze(addr)) + t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize") + menu.addAction(t, lambda: self.toggle_priority(addr)) + menu.exec_(self.receive_list.viewport().mapToGlobal(position)) @@ -991,10 +993,11 @@ class ElectrumWindow(QMainWindow): balance = format_satoshis( c + u, False, self.wallet.num_zeros ) item.setData(4,0,balance) - if address in self.wallet.frozen_addresses: - item.setBackgroundColor(1, QColor('lightblue')) - elif address in self.wallet.prioritized_addresses: - item.setBackgroundColor(1, QColor('lightgreen')) + if self.receive_tab_mode == 1: + if address in self.wallet.frozen_addresses: + item.setBackgroundColor(1, QColor('lightblue')) + elif address in self.wallet.prioritized_addresses: + item.setBackgroundColor(1, QColor('lightgreen')) def update_receive_tab(self): From 6b5162880591f1c708d5d320e4c58cbf27cebdbd Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 15:27:39 +0100 Subject: [PATCH 027/124] text for help button of the receive tab mode --- lib/gui_qt.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index c7e2d8db..1be3f86b 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1595,13 +1595,18 @@ class ElectrumWindow(QMainWindow): for w in [lang_combo, lang_label]: w.setEnabled(False) - view_label=QLabel(_('Receive mode') + ':') + view_label=QLabel(_('Receive Tab') + ':') grid_ui.addWidget(view_label , 9, 0) view_combo = QComboBox() - view_combo.addItems([_('Simple View'), _('Detailed View'), _('Point of Sale')]) + view_combo.addItems([_('Simple'), _('Advanced'), _('Point of Sale')]) view_combo.setCurrentIndex(self.receive_tab_mode) grid_ui.addWidget(view_combo, 9, 1) - grid_ui.addWidget(HelpButton(_('View mode for your "Receive" tab. ')), 9, 2) + hh = _('This selects the interaction mode of the "Receive" tab. ') + '\n\n' \ + + _('Simple') + ': ' + _('Show only addresses and labels.') + '\n\n' \ + + _('Advanced') + ': ' + _('Show address balances and add extra menu items to freeze/prioritize addresses.') + '\n\n' \ + + _('Point of Sale') + ': ' + _('Show QR code window and amounts requested for each address. Add menu item to request amount.') + '\n\n' + + grid_ui.addWidget(HelpButton(hh), 9, 2) vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) From 4180a426c5bce92c0f9161d198737b6f66c968e3 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 16:05:38 +0100 Subject: [PATCH 028/124] auto-reconnect to random server if server is not there --- lib/interface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/interface.py b/lib/interface.py index d7235a0a..9caa88d3 100644 --- a/lib/interface.py +++ b/lib/interface.py @@ -382,7 +382,8 @@ class Interface(threading.Thread): def init_interface(self): if self.config.get('server'): self.init_with_server(self.config) - else: + + if not self.is_connected: print "Using random server..." servers = DEFAULT_SERVERS[:] while servers: From 2962c63001f50efbe04e2260512d2d25bec2072d Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 17:39:51 +0100 Subject: [PATCH 029/124] auto_cycle option --- lib/gui_qt.py | 8 +++++++- lib/interface.py | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 1be3f86b..9ed25d25 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1791,6 +1791,12 @@ class ElectrumWindow(QMainWindow): if not wallet.config.is_modifiable('server'): for w in [server_host, server_port, server_protocol, servers_list_widget]: w.setEnabled(False) + # auto cycle + autocycle_cb = QCheckBox('Try random servers if disconnected') + autocycle_cb.setChecked(wallet.config.get('auto_cycle', False)) + grid.addWidget(autocycle_cb, 3, 1, 3, 2) + if not wallet.config.is_modifiable('auto_cycle'): autocycle_cb.setEnabled(False) + # proxy setting proxy_mode = QComboBox() proxy_host = QLineEdit() @@ -1838,7 +1844,7 @@ class ElectrumWindow(QMainWindow): wallet.config.set_key("proxy", proxy, True) wallet.config.set_key("server", server, True) interface.set_server(server, proxy) - + wallet.config.set_key('auto_cycle', autocycle_cb.isChecked(), True) return True def closeEvent(self, event): diff --git a/lib/interface.py b/lib/interface.py index 9caa88d3..c79762b0 100644 --- a/lib/interface.py +++ b/lib/interface.py @@ -382,9 +382,12 @@ class Interface(threading.Thread): def init_interface(self): if self.config.get('server'): self.init_with_server(self.config) + else: + if self.config.get('auto_cycle') is None: + self.config.set_key('auto_cycle', True, False) - if not self.is_connected: - print "Using random server..." + if not self.is_connected and self.config.get('auto_cycle'): + print_msg("Using random server...") servers = DEFAULT_SERVERS[:] while servers: server = random.choice( servers ) From e3fc19c48c863ecafa88fed3caac4596a5c942bb Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 17:55:35 +0100 Subject: [PATCH 030/124] fix previous commit --- lib/interface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/interface.py b/lib/interface.py index c79762b0..32610221 100644 --- a/lib/interface.py +++ b/lib/interface.py @@ -21,7 +21,7 @@ import random, socket, ast, re, ssl import threading, traceback, sys, time, json, Queue from version import ELECTRUM_VERSION, PROTOCOL_VERSION -from util import print_error +from util import print_error, print_msg DEFAULT_TIMEOUT = 5 @@ -376,6 +376,7 @@ class Interface(threading.Thread): self.servers = {} # actual list from IRC self.rtime = 0 self.bytes_received = 0 + self.is_connected = False From 40509b7c613a99d5009a9c616b275e00b33130ec Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 3 Jan 2013 18:54:34 +0100 Subject: [PATCH 031/124] todolist --- TODOLIST | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 TODOLIST diff --git a/TODOLIST b/TODOLIST new file mode 100644 index 00000000..08b1d6f8 --- /dev/null +++ b/TODOLIST @@ -0,0 +1,28 @@ + +security: + - check that we are on the longest chain after a reorg + - check SSL cerfificates of servers + + +wallet, transactions : + - support compressed keys + - dust sweeping + - transactions with multiple outputs + - BIP 32 + + +code improvements: + - qrcode and bmp patches are on github (they are incompatible with android) + + +classic gui : + - in POS mode, request amount in USD, convert to BTC + + +android: + - kivy-based gui + + +protocol: + - add client authentication, to make paying servers possible + From 3bae4e9761dbca3454b6fafe4c5974eae2c23893 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 3 Jan 2013 20:13:23 +0100 Subject: [PATCH 032/124] remove unnecessary clutter --- lib/gui_qt.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 9ed25d25..51b66f11 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1490,19 +1490,10 @@ class ElectrumWindow(QMainWindow): d.setWindowTitle(_('Electrum Settings')) d.setModal(1) vbox = QVBoxLayout() - msg = _('Here are the settings of your wallet.') + '\n'\ - + _('For more explanations, click on the help buttons next to each field.') - - label = QLabel(msg) - label.setFixedWidth(250) - label.setWordWrap(True) - label.setAlignment(Qt.AlignJustify) tabs = QTabWidget(self) vbox.addWidget(tabs) - vbox.addWidget(label) - tab = QWidget() grid_wallet = QGridLayout(tab) grid_wallet.setColumnStretch(0,1) From 512a812872bf7e0183e416fbbdac9e330aec5371 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 3 Jan 2013 21:36:25 +0100 Subject: [PATCH 033/124] try http if tcp fails. --- electrum | 3 ++- lib/interface.py | 34 ++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/electrum b/electrum index 83a5869a..3f134d9d 100755 --- a/electrum +++ b/electrum @@ -205,7 +205,8 @@ if __name__ == '__main__': interface = Interface(config, True) wallet.interface = interface interface.start() - interface.send([('server.peers.subscribe',[])]) + if interface.is_connected: + interface.send([('server.peers.subscribe',[])]) set_language(config.get('language')) gui = gui.ElectrumGui(wallet, config) diff --git a/lib/interface.py b/lib/interface.py index 32610221..e9a37c8f 100644 --- a/lib/interface.py +++ b/lib/interface.py @@ -39,6 +39,11 @@ DEFAULT_SERVERS = [ 'ecdsa.org:50001:t' ] +# add only port 80 servers here +DEFAULT_HTTP_SERVERS = [ + 'electrum.no-ip.org:80:h' +] + proxy_modes = ['socks4', 'socks5', 'http'] @@ -174,7 +179,14 @@ class Interface(threading.Thread): self.init_server(host, port, proxy, use_ssl) self.session_id = None self.connection_msg = ('https' if self.use_ssl else 'http') + '://%s:%d'%( self.host, self.port ) - self.is_connected = True + try: + self.poll() + except: + return + + if self.session_id: + print_error('http session:',self.session_id) + self.is_connected = True def run_http(self): self.is_connected = True @@ -232,7 +244,7 @@ class Interface(threading.Thread): headers['cookie'] = 'SESSION=%s'%self.session_id req = urllib2.Request(self.connection_msg, data_json, headers) - response_stream = urllib2.urlopen(req) + response_stream = urllib2.urlopen(req, timeout=DEFAULT_TIMEOUT) for index, cookie in enumerate(cj): if cookie.name=='SESSION': @@ -389,17 +401,23 @@ class Interface(threading.Thread): if not self.is_connected and self.config.get('auto_cycle'): print_msg("Using random server...") - servers = DEFAULT_SERVERS[:] - while servers: - server = random.choice( servers ) - servers.remove(server) + servers_tcp = DEFAULT_SERVERS[:] + servers_http = DEFAULT_HTTP_SERVERS[:] + while servers_tcp or servers_http: + if servers_tcp: + server = random.choice( servers_tcp ) + servers_tcp.remove(server) + else: + # try HTTP if we can't get a TCP connection + server = random.choice( servers_http ) + servers_http.remove(server) + print server self.config.set_key('server', server, False) self.init_with_server(self.config) if self.is_connected: break - if not servers: + if not self.is_connected: print 'no server available' - self.is_connected = False self.connect_event.set() # to finish start self.server = 'ecdsa.org:50001:t' self.proxy = None From 34253a1ba908cfa5c98e83c9af1a65c8dfe5eae1 Mon Sep 17 00:00:00 2001 From: Pontius Date: Thu, 3 Jan 2013 21:56:48 +0100 Subject: [PATCH 034/124] Use blockchain.info instead of intersango.com for exchange rates (quick hack) --- lib/exchange_rate.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/exchange_rate.py b/lib/exchange_rate.py index 77cbf9b1..36e34fa6 100644 --- a/lib/exchange_rate.py +++ b/lib/exchange_rate.py @@ -28,21 +28,17 @@ class Exchanger(threading.Thread): self.discovery() def discovery(self): - connection = httplib.HTTPSConnection('intersango.com') - connection.request("GET", "/api/ticker.php") + connection = httplib.HTTPSConnection('blockchain.info') + connection.request("GET", "/ticker") response = connection.getresponse() if response.reason == httplib.responses[httplib.NOT_FOUND]: return response = json.loads(response.read()) - # 1 = BTC:GBP - # 2 = BTC:EUR - # 3 = BTC:USD - # 4 = BTC:PLN quote_currencies = {} try: - quote_currencies["GBP"] = self._lookup_rate(response, 1) - quote_currencies["EUR"] = self._lookup_rate(response, 2) - quote_currencies["USD"] = self._lookup_rate(response, 3) + quote_currencies["GBP"] = self._lookup_rate(response, "GBP") + quote_currencies["EUR"] = self._lookup_rate(response, "EUR") + quote_currencies["USD"] = self._lookup_rate(response, "USD") with self.lock: self.quote_currencies = quote_currencies self.parent.emit(SIGNAL("refresh_balance()")) @@ -50,7 +46,7 @@ class Exchanger(threading.Thread): pass def _lookup_rate(self, response, quote_id): - return decimal.Decimal(response[str(quote_id)]["last"]) + return decimal.Decimal(response[str(quote_id)]["24h"]) if __name__ == "__main__": exch = Exchanger(("EUR", "USD", "GBP")) From 2ae5f0940a3075e80a5e2d87804a1c325380860a Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 3 Jan 2013 23:59:43 +0100 Subject: [PATCH 035/124] update status bar when amount is modified --- lib/gui_qt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 51b66f11..077e8736 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -705,6 +705,7 @@ class ElectrumWindow(QMainWindow): self.funds_error = True self.amount_e.setPalette(palette) self.fee_e.setPalette(palette) + self.update_wallet() self.amount_e.textChanged.connect(lambda: entry_changed(False) ) self.fee_e.textChanged.connect(lambda: entry_changed(True) ) From 47145ce1a60ffdd41147667f1a9b38fb306a0486 Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Fri, 4 Jan 2013 12:25:29 +0700 Subject: [PATCH 036/124] show history debits in red --- data/cleanlook/style.css | 2 +- lib/gui_qt.py | 2 ++ lib/history_widget.py | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/data/cleanlook/style.css b/data/cleanlook/style.css index 66bda49b..eda21177 100644 --- a/data/cleanlook/style.css +++ b/data/cleanlook/style.css @@ -94,7 +94,7 @@ MiniWindow QPushButton { color: #333; } -#history::item +#history { color: #888; } diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 077e8736..3116c0a8 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -599,6 +599,8 @@ class ElectrumWindow(QMainWindow): item.setFont(2, QFont(MONOSPACE_FONT)) item.setFont(3, QFont(MONOSPACE_FONT)) item.setFont(4, QFont(MONOSPACE_FONT)) + if value < 0: + item.setForeground(3, QBrush(QColor("#BC1E1E"))) if tx_hash: item.setToolTip(0, tx_hash) if is_default_label: diff --git a/lib/history_widget.py b/lib/history_widget.py index f04d1e7e..2ca9a5dd 100644 --- a/lib/history_widget.py +++ b/lib/history_widget.py @@ -20,5 +20,7 @@ class HistoryWidget(QTreeWidget): if date is None: date = "Unknown" item = QTreeWidgetItem([amount, address, date]) + if float(amount) < 0: + item.setForeground(0, QBrush(QColor("#BC1E1E"))) self.insertTopLevelItem(0, item) From 251e4529b6c08e8fd0e3fa162395d5f16f66d6c1 Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Fri, 4 Jan 2013 12:33:57 +0700 Subject: [PATCH 037/124] updated indent spacing --- lib/gui_qt.py | 2 +- lib/history_widget.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 3116c0a8..dea7e1d0 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -600,7 +600,7 @@ class ElectrumWindow(QMainWindow): item.setFont(3, QFont(MONOSPACE_FONT)) item.setFont(4, QFont(MONOSPACE_FONT)) if value < 0: - item.setForeground(3, QBrush(QColor("#BC1E1E"))) + item.setForeground(3, QBrush(QColor("#BC1E1E"))) if tx_hash: item.setToolTip(0, tx_hash) if is_default_label: diff --git a/lib/history_widget.py b/lib/history_widget.py index 2ca9a5dd..3a6caf96 100644 --- a/lib/history_widget.py +++ b/lib/history_widget.py @@ -21,6 +21,6 @@ class HistoryWidget(QTreeWidget): date = "Unknown" item = QTreeWidgetItem([amount, address, date]) if float(amount) < 0: - item.setForeground(0, QBrush(QColor("#BC1E1E"))) + item.setForeground(0, QBrush(QColor("#BC1E1E"))) self.insertTopLevelItem(0, item) From c4ef9185abb337bae8b29e592409fdb2fcee45f5 Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Fri, 4 Jan 2013 13:07:33 +0700 Subject: [PATCH 038/124] added new sahara theme --- data/sahara/name.cfg | 1 + data/sahara/style.css | 102 ++++++++++++++++++++++++++++++++++++++++++ setup.py | 4 ++ 3 files changed, 107 insertions(+) create mode 100644 data/sahara/name.cfg create mode 100644 data/sahara/style.css diff --git a/data/sahara/name.cfg b/data/sahara/name.cfg new file mode 100644 index 00000000..da4844db --- /dev/null +++ b/data/sahara/name.cfg @@ -0,0 +1 @@ +Sahara diff --git a/data/sahara/style.css b/data/sahara/style.css new file mode 100644 index 00000000..cd8d7272 --- /dev/null +++ b/data/sahara/style.css @@ -0,0 +1,102 @@ +#main_window +{ + background: qlineargradient(x1: 0, y1: 0, x2:0,y2:1, stop: 0 white , stop: 1 #F2E3BE); +} + +MiniWindow QPushButton { + color: #C1A76D; + border: 1px solid #A7811C; + border-radius: 0px; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 white, stop: 1 #F2E3BE); + min-height: 25px; + min-width: 30px; +} + +#send_button{ + color: #FEEBA7; + border: 1px solid #AD8B35; + border-radius: 4px; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #E0A035, stop: 1 #AD8B35); + min-width: 80px; + min-height: 23px; + padding: 2px; +} + +#send_button:disabled{ + color: #FEEDD3; + border: 1px solid #F7D46D; + border-radius: 4px; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #FAEEA5, stop: 1 #DBC050); + min-width: 80px; + min-height: 23px; + padding: 2px; +} + +#receive_button +{ + color: #FEEBA7; + border: 1px solid #AD8B35; + border-radius: 4px; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #E0A035, stop: 1 #AD8B35); + min-height: 25px; + min-width: 30px; +} +#receive_button[isActive=true] +{ + color: #FEEBA7; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #E0A035, stop: 1 #987620); +} + +#address_input, #amount_input +{ + color: #000; + padding: 5px; + border-radius: 4px; + border: 1px solid #CBAE69; + width: 225px; +} + +#address_input[isValid=true] +{ + color: #4D9948; + padding: 5px; + border-radius: 4px; + border: 1px solid #CBAE69; + width: 225px; + margin-top: 4px; +} + +#address_input[isValid=false] +{ + color: #CE4141; + padding: 5px; + border-radius: 4px; + border: 1px solid #CBAE69; + width: 225px; + margin-top: 4px; +} + +#address_input[isValid=placeholder] +{ + color: #DEC58D; + padding: 5px; + border-radius: 4px; + border: 1px solid #CBAE69; + width: 225px; + margin-top: 4px; +} +#balance_label +{ + color: #7E5907; +} + +#history +{ + color: #8B6914; +} + diff --git a/setup.py b/setup.py index d1fb0279..a83642e6 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,10 @@ data_files += [ "data/cleanlook/name.cfg", "data/cleanlook/style.css" ]), + (os.path.join(util.appdata_dir(), "sahara"), [ + "data/sahara/name.cfg", + "data/sahara/style.css" + ]), (os.path.join(util.appdata_dir(), "dark"), [ "data/dark/background.png", "data/dark/name.cfg", From 902bc6f87a321dc0f5726f092c6b15ed8b9c0ebc Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Fri, 4 Jan 2013 13:57:10 +0700 Subject: [PATCH 039/124] make qr codes scalable and centered --- lib/gui_qt.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 077e8736..e11fc1d2 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -166,7 +166,6 @@ class QRCodeWidget(QWidget): black = QColor(0, 0, 0, 255) white = QColor(255, 255, 255, 255) - boxsize = 6 if not self.qr: qp = QtGui.QPainter() @@ -176,11 +175,16 @@ class QRCodeWidget(QWidget): qp.drawRect(0, 0, 198, 198) qp.end() return - - size = self.qr.getModuleCount()*boxsize + k = self.qr.getModuleCount() qp = QtGui.QPainter() qp.begin(self) + r = qp.viewport() + boxsize = min(r.width(), r.height())*0.8/k + size = k*boxsize + left = (r.width() - size)/2 + top = (r.height() - size)/2 + for r in range(k): for c in range(k): if self.qr.isDark(r, c): @@ -189,7 +193,7 @@ class QRCodeWidget(QWidget): else: qp.setBrush(white) qp.setPen(white) - qp.drawRect(c*boxsize, r*boxsize, boxsize, boxsize) + qp.drawRect(left+c*boxsize, top+r*boxsize, boxsize, boxsize) qp.end() @@ -1200,8 +1204,8 @@ class ElectrumWindow(QMainWindow): d.setMinimumSize(270, 300) vbox = QVBoxLayout() qrw = QRCodeWidget(data) - vbox.addWidget(qrw) - vbox.addWidget(QLabel(data)) + vbox.addWidget(qrw, 1) + vbox.addWidget(QLabel(data), 0, Qt.AlignHCenter) hbox = QHBoxLayout() hbox.addStretch(1) From 04dc6a0ea7a39711a71406ce76b0f2307d6b6920 Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Fri, 4 Jan 2013 14:06:35 +0700 Subject: [PATCH 040/124] adjust invoice screen for scaling --- lib/gui_qt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index e11fc1d2..34b90917 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -212,12 +212,12 @@ class QR_Window(QWidget): main_box = QHBoxLayout() self.qrw = QRCodeWidget() - main_box.addWidget(self.qrw) + main_box.addWidget(self.qrw, 1) vbox = QVBoxLayout() main_box.addLayout(vbox) - main_box.addStretch(1) + #main_box.addStretch(1) self.address_label = QLabel("") self.address_label.setFont(QFont(MONOSPACE_FONT)) From 3b0ed36c74499ef0ee6723800e1f53f71675a7e4 Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Fri, 4 Jan 2013 14:11:37 +0700 Subject: [PATCH 041/124] remove commented code --- lib/gui_qt.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 34b90917..5954412e 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -217,8 +217,6 @@ class QR_Window(QWidget): vbox = QVBoxLayout() main_box.addLayout(vbox) - #main_box.addStretch(1) - self.address_label = QLabel("") self.address_label.setFont(QFont(MONOSPACE_FONT)) vbox.addWidget(self.address_label) From e590c1cb20b72ef7bd89a50640e8e9485d9d0967 Mon Sep 17 00:00:00 2001 From: Pontius Date: Fri, 4 Jan 2013 10:35:21 +0100 Subject: [PATCH 042/124] Adjusted quote lookup and added three more currencies (where we have translations for) --- lib/exchange_rate.py | 9 ++++----- lib/gui_lite.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/exchange_rate.py b/lib/exchange_rate.py index 36e34fa6..a4137105 100644 --- a/lib/exchange_rate.py +++ b/lib/exchange_rate.py @@ -36,9 +36,8 @@ class Exchanger(threading.Thread): response = json.loads(response.read()) quote_currencies = {} try: - quote_currencies["GBP"] = self._lookup_rate(response, "GBP") - quote_currencies["EUR"] = self._lookup_rate(response, "EUR") - quote_currencies["USD"] = self._lookup_rate(response, "USD") + for r in response: + quote_currencies[r] = self._lookup_rate(response, r) with self.lock: self.quote_currencies = quote_currencies self.parent.emit(SIGNAL("refresh_balance()")) @@ -46,9 +45,9 @@ class Exchanger(threading.Thread): pass def _lookup_rate(self, response, quote_id): - return decimal.Decimal(response[str(quote_id)]["24h"]) + return decimal.Decimal(response[str(quote_id)]["15m"]) if __name__ == "__main__": - exch = Exchanger(("EUR", "USD", "GBP")) + exch = Exchanger(("BRL", "CNY", "EUR", "GBP", "RUB", "USD")) print exch.exchange(1, "EUR") diff --git a/lib/gui_lite.py b/lib/gui_lite.py index c9e06d6a..29897093 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -197,7 +197,7 @@ class MiniWindow(QDialog): self.actuator = actuator self.config = config self.btc_balance = None - self.quote_currencies = ["EUR", "USD", "GBP"] + self.quote_currencies = ["BRL", "CNY", "EUR", "GBP", "RUB", "USD"] self.actuator.set_configured_currency(self.set_quote_currency) self.exchanger = exchange_rate.Exchanger(self) # Needed because price discovery is done in a different thread From f14f22636f13c04174c7126824170deb32b29f45 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 4 Jan 2013 11:02:39 +0100 Subject: [PATCH 043/124] catch exception raised if url is not reachable --- lib/exchange_rate.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/exchange_rate.py b/lib/exchange_rate.py index a4137105..bde5f52d 100644 --- a/lib/exchange_rate.py +++ b/lib/exchange_rate.py @@ -28,8 +28,11 @@ class Exchanger(threading.Thread): self.discovery() def discovery(self): - connection = httplib.HTTPSConnection('blockchain.info') - connection.request("GET", "/ticker") + try: + connection = httplib.HTTPSConnection('blockchain.info') + connection.request("GET", "/ticker") + except: + return response = connection.getresponse() if response.reason == httplib.responses[httplib.NOT_FOUND]: return From bb0e23faa6db29a64c695a9b2e51e91520d55d88 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 4 Jan 2013 11:06:37 +0100 Subject: [PATCH 044/124] display the ui settings first, as they are the most likely to be accessed --- lib/gui_qt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index adcadf6e..64c5c421 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1499,16 +1499,16 @@ class ElectrumWindow(QMainWindow): tabs = QTabWidget(self) vbox.addWidget(tabs) - tab = QWidget() - grid_wallet = QGridLayout(tab) - grid_wallet.setColumnStretch(0,1) - tabs.addTab(tab, _('Wallet') ) - tab2 = QWidget() grid_ui = QGridLayout(tab2) grid_ui.setColumnStretch(0,1) tabs.addTab(tab2, _('Display') ) + tab = QWidget() + grid_wallet = QGridLayout(tab) + grid_wallet.setColumnStretch(0,1) + tabs.addTab(tab, _('Wallet') ) + fee_label = QLabel(_('Transaction fee')) grid_wallet.addWidget(fee_label, 2, 0) fee_e = QLineEdit() From 261fff2af1ae05046ed78ad2b6ee9b85eb742037 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 4 Jan 2013 12:18:16 +0100 Subject: [PATCH 045/124] do not let users switch to gtk/text from qt --- lib/gui_qt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 64c5c421..343318c6 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1562,12 +1562,12 @@ class ElectrumWindow(QMainWindow): gui_label=QLabel(_('Default GUI') + ':') grid_ui.addWidget(gui_label , 7, 0) gui_combo = QComboBox() - gui_combo.addItems(['Lite', 'Classic', 'Gtk', 'Text']) + gui_combo.addItems(['Lite', 'Classic']) index = gui_combo.findText(self.config.get("gui","classic").capitalize()) if index==-1: index = 1 gui_combo.setCurrentIndex(index) grid_ui.addWidget(gui_combo, 7, 1) - grid_ui.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2) + grid_ui.addWidget(HelpButton(_('Select which GUI mode to use at start up.'+'\n'+'Note: use the command line to access the "text" and "gtk" GUIs')), 7, 2) if not self.config.is_modifiable('gui'): for w in [gui_combo, gui_label]: w.setEnabled(False) From 98cce2af0d0fb51061eef9b888ec6bad61e97398 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 4 Jan 2013 15:38:22 +0100 Subject: [PATCH 046/124] move language list to i18n.py --- lib/gui_qt.py | 6 +----- lib/i18n.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 343318c6..914e1e91 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1574,11 +1574,7 @@ class ElectrumWindow(QMainWindow): lang_label=QLabel(_('Language') + ':') grid_ui.addWidget(lang_label , 8, 0) lang_combo = QComboBox() - languages = {'':_('Default'), 'br':_('Brasilian'), 'cs':_('Czech'), 'de':_('German'), - 'eo':_('Esperanto'), 'en':_('English'), 'es':_('Spanish'), 'fr':_('French'), - 'it':_('Italian'), 'lv':_('Latvian'), 'nl':_('Dutch'), 'ru':_('Russian'), - 'sl':_('Slovenian'), 'vi':_('Vietnamese'), 'zh':_('Chinese') - } + from i18n import languages lang_combo.addItems(languages.values()) try: index = languages.keys().index(self.config.get("language",'')) diff --git a/lib/i18n.py b/lib/i18n.py index 7ef4458f..9436219c 100644 --- a/lib/i18n.py +++ b/lib/i18n.py @@ -34,3 +34,20 @@ def set_language(x): if x: language = gettext.translation('electrum', LOCALE_DIR, fallback = True, languages=[x]) +languages = { + '':_('Default'), + 'br':_('Brasilian'), + 'cs':_('Czech'), + 'de':_('German'), + 'eo':_('Esperanto'), + 'en':_('English'), + 'es':_('Spanish'), + 'fr':_('French'), + 'it':_('Italian'), + 'lv':_('Latvian'), + 'nl':_('Dutch'), + 'ru':_('Russian'), + 'sl':_('Slovenian'), + 'vi':_('Vietnamese'), + 'zh':_('Chinese') + } From e7e169888d40568e0dc3cbeaaf8f2a20ef4b4b1e Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 31 Dec 2012 10:41:02 +0100 Subject: [PATCH 047/124] support for compressed keys --- TODOLIST | 1 - lib/bitcoin.py | 87 +++++++++++++++++++++++++++++++++++++++++++++++--- lib/wallet.py | 66 +++++++++++++++++++++++++------------- 3 files changed, 125 insertions(+), 29 deletions(-) diff --git a/TODOLIST b/TODOLIST index 08b1d6f8..988fbf14 100644 --- a/TODOLIST +++ b/TODOLIST @@ -5,7 +5,6 @@ security: wallet, transactions : - - support compressed keys - dust sweeping - transactions with multiple outputs - BIP 32 diff --git a/lib/bitcoin.py b/lib/bitcoin.py index c8e8ba04..eb205004 100644 --- a/lib/bitcoin.py +++ b/lib/bitcoin.py @@ -43,8 +43,57 @@ Hash = lambda x: hashlib.sha256(hashlib.sha256(x).digest()).digest() hash_encode = lambda x: x[::-1].encode('hex') hash_decode = lambda x: x.decode('hex')[::-1] -############ functions from pywallet ##################### +# pywallet openssl private key implementation + +def i2d_ECPrivateKey(pkey, compressed=False): + if compressed: + key = '3081d30201010420' + \ + '%064x' % pkey.secret + \ + 'a081a53081a2020101302c06072a8648ce3d0101022100' + \ + '%064x' % _p + \ + '3006040100040107042102' + \ + '%064x' % _Gx + \ + '022100' + \ + '%064x' % _r + \ + '020101a124032200' + else: + key = '308201130201010420' + \ + '%064x' % pkey.secret + \ + 'a081a53081a2020101302c06072a8648ce3d0101022100' + \ + '%064x' % _p + \ + '3006040100040107044104' + \ + '%064x' % _Gx + \ + '%064x' % _Gy + \ + '022100' + \ + '%064x' % _r + \ + '020101a144034200' + + return key.decode('hex') + i2o_ECPublicKey(pkey, compressed) + +def i2o_ECPublicKey(pkey, compressed=False): + # public keys are 65 bytes long (520 bits) + # 0x04 + 32-byte X-coordinate + 32-byte Y-coordinate + # 0x00 = point at infinity, 0x02 and 0x03 = compressed, 0x04 = uncompressed + # compressed keys: where is 0x02 if y is even and 0x03 if y is odd + if compressed: + if pkey.pubkey.point.y() & 1: + key = '03' + '%064x' % pkey.pubkey.point.x() + else: + key = '02' + '%064x' % pkey.pubkey.point.x() + else: + key = '04' + \ + '%064x' % pkey.pubkey.point.x() + \ + '%064x' % pkey.pubkey.point.y() + + return key.decode('hex') + +# end pywallet openssl private key implementation + + + +############ functions from pywallet ##################### + addrtype = 0 def hash_160(public_key): @@ -151,17 +200,39 @@ def DecodeBase58Check(psz): def PrivKeyToSecret(privkey): return privkey[9:9+32] -def SecretToASecret(secret): - vchIn = chr(addrtype+128) + secret +def SecretToASecret(secret, compressed=False): + vchIn = chr((addrtype+128)&255) + secret + if compressed: vchIn += '\01' return EncodeBase58Check(vchIn) def ASecretToSecret(key): vch = DecodeBase58Check(key) - if vch and vch[0] == chr(addrtype+128): + if vch and vch[0] == chr((addrtype+128)&255): return vch[1:] else: return False +def regenerate_key(sec): + b = ASecretToSecret(sec) + if not b: + return False + b = b[0:32] + secret = int('0x' + b.encode('hex'), 16) + return EC_KEY(secret) + +def GetPubKey(pkey, compressed=False): + return i2o_ECPublicKey(pkey, compressed) + +def GetPrivKey(pkey, compressed=False): + return i2d_ECPrivateKey(pkey, compressed) + +def GetSecret(pkey): + return ('%064x' % pkey.secret).decode('hex') + +def is_compressed(sec): + b = ASecretToSecret(sec) + return len(b) == 33 + ########### end pywallet functions ####################### # secp256k1, http://www.oid-info.com/get/1.3.132.0.10 @@ -176,6 +247,13 @@ generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r ) oid_secp256k1 = (1,3,132,0,10) SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 ) +class EC_KEY(object): + def __init__( self, secret ): + self.pubkey = ecdsa.ecdsa.Public_key( generator_secp256k1, generator_secp256k1 * secret ) + self.privkey = ecdsa.ecdsa.Private_key( self.pubkey, secret ) + self.secret = secret + + def filter(s): out = re.sub('( [^\n]*|)\n','',s) @@ -195,7 +273,6 @@ def raw_tx( inputs, outputs, for_sig = None ): sig = sig + chr(1) # hashtype script = int_to_hex( len(sig)) + ' push %d bytes\n'%len(sig) script += sig.encode('hex') + ' sig\n' - pubkey = chr(4) + pubkey script += int_to_hex( len(pubkey)) + ' push %d bytes\n'%len(pubkey) script += pubkey.encode('hex') + ' pubkey\n' elif for_sig==i: diff --git a/lib/wallet.py b/lib/wallet.py index 648b30d3..9cde7c89 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -113,22 +113,33 @@ class Wallet: while not self.is_up_to_date(): time.sleep(0.1) def import_key(self, keypair, password): - address, key = keypair.split(':') + + address, sec = keypair.split(':') if not self.is_valid(address): raise BaseException('Invalid Bitcoin address') if address in self.all_addresses(): raise BaseException('Address already in wallet') - b = ASecretToSecret( key ) - if not b: - raise BaseException('Unsupported key format') - secexp = int( b.encode('hex'), 16) - private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve=SECP256k1 ) + + # rebuild public key from private key, compressed or uncompressed + pkey = regenerate_key(sec) + if not pkey: + return False + + # figure out if private key is compressed + compressed = is_compressed(sec) + + # rebuild private and public key from regenerated secret + private_key = GetPrivKey(pkey, compressed) + public_key = GetPubKey(pkey, compressed) + addr = public_key_to_bc_address(public_key) + # sanity check - public_key = private_key.get_verifying_key() - if not address == public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ): + if not address == addr : raise BaseException('Address does not match private key') - self.imported_keys[address] = self.pw_encode( key, password ) - + + # store the originally requested keypair into the imported keys table + self.imported_keys[address] = self.pw_encode(sec, password ) + def new_seed(self, password): seed = "%032x"%ecdsa.util.randrange( pow(2,128) ) @@ -172,19 +183,23 @@ class Wallet: return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key.decode('hex') ) ) def get_private_key_base58(self, address, password): - pk = self.get_private_key(address, password) - if pk is None: return None - return SecretToASecret( pk ) + secexp, compressed = self.get_private_key(address, password) + if secexp is None: return None + pk = number_to_string( secexp, generator_secp256k1.order() ) + return SecretToASecret( pk, compressed ) def get_private_key(self, address, password): """ Privatekey(type,n) = Master_private_key + H(n|S|type) """ order = generator_secp256k1.order() if address in self.imported_keys.keys(): - b = self.pw_decode( self.imported_keys[address], password ) - if not b: return None - b = ASecretToSecret( b ) - secexp = int( b.encode('hex'), 16) + sec = self.pw_decode( self.imported_keys[address], password ) + if not sec: return None, None + + pkey = regenerate_key(sec) + compressed = is_compressed(sec) + secexp = pkey.secret + else: if address in self.addresses: n = self.addresses.index(address) @@ -201,20 +216,21 @@ class Wallet: if not seed: return None secexp = self.stretch_key(seed) secexp = ( secexp + self.get_sequence(n,for_change) ) % order + compressed = False - pk = number_to_string(secexp,order) - return pk + return secexp, compressed def msg_magic(self, message): return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message def sign_message(self, address, message, password): - private_key = ecdsa.SigningKey.from_string( self.get_private_key(address, password), curve = SECP256k1 ) + secexp, compressed = self.get_private_key(address, password) + private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 ) public_key = private_key.get_verifying_key() signature = private_key.sign_digest( Hash( self.msg_magic( message ) ), sigencode = ecdsa.util.sigencode_string ) assert public_key.verify_digest( signature, Hash( self.msg_magic( message ) ), sigdecode = ecdsa.util.sigdecode_string) for i in range(4): - sig = base64.b64encode( chr(27+i) + signature ) + sig = base64.b64encode( chr(27 + i + (4 if compressed else 0)) + signature ) try: self.verify_message( address, sig, message) return sig @@ -598,9 +614,13 @@ class Wallet: s_inputs = [] for i in range(len(inputs)): addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i] - private_key = ecdsa.SigningKey.from_string( self.get_private_key(addr, password), curve = SECP256k1 ) + secexp, compressed = self.get_private_key(addr, password) + private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 ) public_key = private_key.get_verifying_key() - pubkey = public_key.to_string() + + pkey = EC_KEY(secexp) + pubkey = GetPubKey(pkey, compressed) + tx = filter( raw_tx( inputs, outputs, for_sig = i ) ) sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der ) assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der) From 30de518be9a173f9ec64c5e616ff3b6384a0418b Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Sat, 5 Jan 2013 19:44:20 +0700 Subject: [PATCH 048/124] add switch-gui button for qt --- icons.qrc | 1 + icons/switchgui.png | Bin 0 -> 1649 bytes lib/gui_lite.py | 26 +++++++++++++++----------- lib/gui_qt.py | 13 +++++++++++++ 4 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 icons/switchgui.png diff --git a/icons.qrc b/icons.qrc index ef3c6f1c..5ad965f5 100644 --- a/icons.qrc +++ b/icons.qrc @@ -13,6 +13,7 @@ icons/status_connected.png icons/status_disconnected.png icons/status_waiting.png + icons/switchgui.png icons/unconfirmed.png icons/network.png diff --git a/icons/switchgui.png b/icons/switchgui.png new file mode 100644 index 0000000000000000000000000000000000000000..ec18650d7a60d131d2be098860734acb72fcbd1d GIT binary patch literal 1649 zcmV-%29EiOP)6z``>}FTkf}wlft5@&; ze^swuy%|`M6q zB~rzELz6b|6;&hhxv@SsHqpUH(79_D>+XJm^FO>dCxLr5Mick#{h6LQTo~X2N6-og z!+@aW2wRRY2nYf}BzB_{B?uuvkv3q47zhStfI;%W9GIXMAO@xeYN&ykVG8C!oFF0? z!t}PC{PgY@>p5S<>Z%C*ar7^)M3xu;aSkVf3k2tqPbdT}hXmk)crsjn(s*3LP6R~y zI_Y;}Jw(7I{eY3Yi=Y<2uWlKW>o*Kwtt8N!HN?5ha0VjXH3?8A3?xX%dekU}BhnB{ozQ z5y4>@Xaq!4vPO<^97Y0YqKhEF<*xG0@ynPA58u9qFmODz{{-(myNOOudHLWe0_Xa6 z>Ue-W_yQ@z05!u5RO4sbj`gvc#Uw0eAYBYZ(42h}Dd@FB$Hk7aacv7H>B>~%ul9f? zMpI81DX4hEsWU_saY>9SII+?MmU$~h_u7v2+`D-)(|Bm#pKMvz=I|?90Jzxks0n*_ z-oTziXXr(6;JF)l>EJ1Z)C+Y9n4%UBQ;0K)8DffP9PgrniI95OnP9}(C$=L6o3DO$ z4t%^MW)R}o@%9Ncb%%j-!k$BC5P|Ww>xE=lA{i~NPE4gtLi56F;-Mv3>HUawaKO{rFr2IaJRo7X0u ztH`U8ZnxXdGz=;1Xt)P7t*7S!y2+|XQ8Zv@0DRWo>i2rRflWGU)dxaI>JmtXErh5lQ50pshk>8V8;!D1?19n1=34bAiVBBO zlaMJB&9YE!4~#gvM~U|yRn3{Hcb%M*NYis**oG+sQ`HRcun?Kg{kZxZfCihmn6lD5 zU0&Vtq>FK>FYbXxS;!GZ5s@zfTu5Mb^a~!Sd!eB`c(GQs2KXE(u)#a!$aA2|*g~)L zFr5KCanQLi4om<&zz_F8I0jV5_^`?68zyzYOPYmR6Wf6Mft!IzK*}CS2$?aaa`FO9 zjwO>62=yZ^eos$VOFY;2I%bL0q_5ykOO@gUEBqwi?|bv>F32hf!aIORlFmy&uS`II zkl#Q0gsqS7y7P^*eU$lnBr7%2k1O>{^2bQ7p9AUIXHo5P?DOxL zK6?r{51a$0fZnL%whTuq+80|a_sO@d$cikK{0Aekn@j!H(EtDd03~!qSaf7zbY(hY za%Ew3WdJfTF)%GKGA%JMR539+F*G_gHZ3qVIxsNVl~St!001R)MObuXVRU6WZEs|0 vW_bWIFfceRFf=VNGE^}&IyE#pG&n6VH99ab>0%Da00000NkvXXu0mjfvqI^8 literal 0 HcmV?d00001 diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 29897093..dc7b4344 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -88,14 +88,17 @@ def load_theme_paths(): class ElectrumGui(QObject): - def __init__(self, wallet, config): + def __init__(self, wallet, config, expert=None): super(QObject, self).__init__() self.wallet = wallet self.config = config self.check_qt_version() - self.app = QApplication(sys.argv) - + self.expert = expert + if self.expert != None: + self.app = self.expert.app + else: + self.app = QApplication(sys.argv) def check_qt_version(self): qtVersion = qVersion() @@ -122,14 +125,15 @@ class ElectrumGui(QObject): if url: self.set_url(url) - - timer = Timer() - timer.start() - self.expert = gui_qt.ElectrumWindow(self.wallet, self.config) - self.expert.app = self.app - self.expert.connect_slots(timer) - self.expert.update_wallet() - self.app.exec_() + + if self.expert == None: + timer = Timer() + timer.start() + self.expert = gui_qt.ElectrumWindow(self.wallet, self.config) + self.expert.app = self.app + self.expert.connect_slots(timer) + self.expert.update_wallet() + self.app.exec_() def expand(self): """Hide the lite mode window and show pro-mode.""" diff --git a/lib/gui_qt.py b/lib/gui_qt.py index adcadf6e..1c364af8 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -294,6 +294,7 @@ class ElectrumWindow(QMainWindow): def __init__(self, wallet, config): QMainWindow.__init__(self) + self.lite = None self.wallet = wallet self.config = config self.wallet.interface.register_callback('updated', self.update_callback) @@ -1108,6 +1109,9 @@ class ElectrumWindow(QMainWindow): def create_status_bar(self): sb = QStatusBar() sb.setFixedHeight(35) + qtVersion = qVersion() + if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7): + sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) ) if self.wallet.seed: sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) ) sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) ) @@ -1116,6 +1120,15 @@ class ElectrumWindow(QMainWindow): self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) ) sb.addPermanentWidget( self.status_button ) self.setStatusBar(sb) + + def go_lite(self): + import gui_lite + self.hide() + if self.lite: + self.lite.mini.show() + else: + self.lite = gui_lite.ElectrumGui(self.wallet, self.config, self) + self.lite.main(None) def new_contact_dialog(self): text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':') From c7edba0990be451352c77cb6ca7431fdf907779d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 15:23:35 +0100 Subject: [PATCH 049/124] currency conversions (bkkcoins) --- lib/exchange_rate.py | 5 ++++- lib/gui_qt.py | 40 ++++++++++++++++++++++++++++++++++++---- lib/simple_config.py | 2 +- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/lib/exchange_rate.py b/lib/exchange_rate.py index bde5f52d..dc6ad13a 100644 --- a/lib/exchange_rate.py +++ b/lib/exchange_rate.py @@ -46,9 +46,12 @@ class Exchanger(threading.Thread): self.parent.emit(SIGNAL("refresh_balance()")) except KeyError: pass + + def get_currencies(self): + return [] if self.quote_currencies == None else sorted(self.quote_currencies.keys()) def _lookup_rate(self, response, quote_id): - return decimal.Decimal(response[str(quote_id)]["15m"]) + return decimal.Decimal(str(response[str(quote_id)]["15m"])) if __name__ == "__main__": exch = Exchanger(("BRL", "CNY", "EUR", "GBP", "RUB", "USD")) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 914e1e91..54b2bdc7 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -38,6 +38,7 @@ except: from wallet import format_satoshis import bmp, mnemonic, pyqrnative, qrscanner +import exchange_rate from decimal import Decimal @@ -334,6 +335,9 @@ class ElectrumWindow(QMainWindow): self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet) #self.connect(self, SIGNAL('editamount'), self.edit_amount) self.history_list.setFocus(True) + + self.exchanger = exchange_rate.Exchanger(self) + self.connect(self, SIGNAL("refresh_balance()"), self.update_wallet) # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913 if platform.system() == 'Windows': @@ -383,6 +387,7 @@ class ElectrumWindow(QMainWindow): c, u = self.wallet.get_balance() text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) ) if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() ) + text += self.create_quote_text(Decimal(c+u)/100000000) icon = QIcon(":icons/status_connected.png") else: text = _( "Not connected" ) @@ -401,7 +406,15 @@ class ElectrumWindow(QMainWindow): self.update_contacts_tab() self.update_completions() - + def create_quote_text(self, btc_balance): + quote_currency = self.config.get("currency", "None") + quote_balance = self.exchanger.exchange(btc_balance, quote_currency) + if quote_balance is None: + quote_text = "" + else: + quote_text = " (%.2f %s)" % (quote_balance, quote_currency) + return quote_text + def create_history_tab(self): self.history_list = l = MyTreeWidget(self) l.setColumnCount(5) @@ -1586,19 +1599,33 @@ class ElectrumWindow(QMainWindow): if not self.config.is_modifiable('language'): for w in [lang_combo, lang_label]: w.setEnabled(False) + currencies = self.exchanger.get_currencies() + currencies.insert(0, "None") + cur_label=QLabel(_('Currency') + ':') + grid_ui.addWidget(cur_label , 9, 0) + cur_combo = QComboBox() + cur_combo.addItems(currencies) + try: + index = currencies.index(self.config.get('currency', "None")) + except: + index = 0 + cur_combo.setCurrentIndex(index) + grid_ui.addWidget(cur_combo, 9, 1) + grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes. ')), 9, 2) + view_label=QLabel(_('Receive Tab') + ':') - grid_ui.addWidget(view_label , 9, 0) + grid_ui.addWidget(view_label , 10, 0) view_combo = QComboBox() view_combo.addItems([_('Simple'), _('Advanced'), _('Point of Sale')]) view_combo.setCurrentIndex(self.receive_tab_mode) - grid_ui.addWidget(view_combo, 9, 1) + grid_ui.addWidget(view_combo, 10, 1) hh = _('This selects the interaction mode of the "Receive" tab. ') + '\n\n' \ + _('Simple') + ': ' + _('Show only addresses and labels.') + '\n\n' \ + _('Advanced') + ': ' + _('Show address balances and add extra menu items to freeze/prioritize addresses.') + '\n\n' \ + _('Point of Sale') + ': ' + _('Show QR code window and amounts requested for each address. Add menu item to request amount.') + '\n\n' - grid_ui.addWidget(HelpButton(hh), 9, 2) + grid_ui.addWidget(HelpButton(hh), 10, 2) vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) @@ -1661,6 +1688,11 @@ class ElectrumWindow(QMainWindow): if lang_request != self.config.get('language'): self.config.set_key("language", lang_request, True) need_restart = True + + cur_request = str(currencies[cur_combo.currentIndex()]) + if cur_request != self.config.get('currency', "None"): + self.config.set_key('currency', cur_request, True) + self.update_wallet() if need_restart: QMessageBox.warning(self, _('Success'), _('Please restart Electrum to activate the new GUI settings'), _('OK')) diff --git a/lib/simple_config.py b/lib/simple_config.py index a94b837e..8cfa24dd 100644 --- a/lib/simple_config.py +++ b/lib/simple_config.py @@ -94,7 +94,7 @@ a SimpleConfig instance then reads the wallet file. try: out = ast.literal_eval(out) except: - print "type error, using default value" + print "type error for '%s': using default value"%key out = default return out From 058f5cba8294895da638d9f584c4c21f5a6d8ec1 Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Sat, 5 Jan 2013 22:49:19 +0700 Subject: [PATCH 050/124] add confirmations to history checkmark tooltips --- lib/gui_qt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 54b2bdc7..9f53aba9 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -436,7 +436,7 @@ class ElectrumWindow(QMainWindow): self.history_list.selectedIndexes() item = self.history_list.currentItem() if not item: return - tx_hash = str(item.toolTip(0)) + tx_hash = str(item.data(0, Qt.UserRole).toString()) if not tx_hash: return menu = QMenu() menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash)) @@ -617,7 +617,8 @@ class ElectrumWindow(QMainWindow): if value < 0: item.setForeground(3, QBrush(QColor("#BC1E1E"))) if tx_hash: - item.setToolTip(0, tx_hash) + item.setData(0, Qt.UserRole, tx_hash) + item.setToolTip(0, "%d %s\nTxId:%s" % (conf, _('Confirmations'), tx_hash) ) if is_default_label: item.setForeground(2, QBrush(QColor('grey'))) From 42dbf61ba80c768e1345681d01e21b0a3e5beb34 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 21:03:46 +0100 Subject: [PATCH 051/124] import/export functions --- lib/gui_lite.py | 86 +++++++++++++++-------------- lib/gui_qt.py | 142 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 139 insertions(+), 89 deletions(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 29897093..42cbfa5d 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -86,6 +86,49 @@ def load_theme_paths(): return theme_paths +def csv_transaction(wallet): + try: + fileName = QFileDialog.getSaveFileName(QWidget(), 'Select file to export your wallet transactions to', os.path.expanduser('~/'), "*.csv") + if fileName: + with open(fileName, "w+") as csvfile: + transaction = csv.writer(csvfile) + transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"]) + for item in wallet.get_tx_history(): + tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item + if confirmations: + if timestamp is not None: + try: + time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] + except [RuntimeError, TypeError, NameError] as reason: + time_string = "unknown" + pass + else: + time_string = "unknown" + else: + time_string = "pending" + + if value is not None: + value_string = format_satoshis(value, True, wallet.num_zeros) + else: + value_string = '--' + + if fee is not None: + fee_string = format_satoshis(fee, True, wallet.num_zeros) + else: + fee_string = '0' + + if tx_hash: + label, is_default_label = wallet.get_label(tx_hash) + else: + label = "" + + balance_string = format_satoshis(balance, False, wallet.num_zeros) + transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string]) + QMessageBox.information(None,"CSV Export created", "Your CSV export has been succesfully created.") + except (IOError, os.error), reason: + QMessageBox.critical(None,"Unable to create csv", "Electrum was unable to produce a transaction export.\n" + str(reason)) + + class ElectrumGui(QObject): def __init__(self, wallet, config): @@ -318,7 +361,7 @@ class MiniWindow(QDialog): backup_wallet.triggered.connect(self.backup_wallet) export_csv = extra_menu.addAction( _("&Export transactions to CSV") ) - export_csv.triggered.connect(self.actuator.csv_transaction) + export_csv.triggered.connect(lambda: csv_transaction(self.wallet)) master_key = extra_menu.addAction( _("Copy master public key to clipboard") ) master_key.triggered.connect(self.actuator.copy_master_public_key) @@ -769,47 +812,6 @@ class MiniActuator: w.exec_() w.destroy() - def csv_transaction(self): - try: - fileName = QFileDialog.getSaveFileName(QWidget(), 'Select file to export your wallet transactions to', os.path.expanduser('~/'), "*.csv") - if fileName: - with open(fileName, "w+") as csvfile: - transaction = csv.writer(csvfile) - transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"]) - for item in self.wallet.get_tx_history(): - tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item - if confirmations: - if timestamp is not None: - try: - time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] - except [RuntimeError, TypeError, NameError] as reason: - time_string = "unknown" - pass - else: - time_string = "unknown" - else: - time_string = "pending" - - if value is not None: - value_string = format_satoshis(value, True, self.wallet.num_zeros) - else: - value_string = '--' - - if fee is not None: - fee_string = format_satoshis(fee, True, self.wallet.num_zeros) - else: - fee_string = '0' - - if tx_hash: - label, is_default_label = self.wallet.get_label(tx_hash) - else: - label = "" - - balance_string = format_satoshis(balance, False, self.wallet.num_zeros) - transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string]) - QMessageBox.information(None,"CSV Export created", "Your CSV export has been succesfully created.") - except (IOError, os.error), reason: - QMessageBox.critical(None,"Unable to create csv", "Electrum was unable to produce a transaction export.\n" + str(reason)) def send(self, address, amount, parent_window): """Send bitcoins to the target address.""" diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 9f53aba9..be1941b1 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -19,6 +19,7 @@ import sys, time, datetime, re from i18n import _ from util import print_error +import os.path, json, util try: import PyQt4 @@ -1503,6 +1504,34 @@ class ElectrumWindow(QMainWindow): return True + def do_import_labels(self): + labelsFile = QFileDialog.getOpenFileName(QWidget(), "Open text file", util.user_dir(), self.tr("Text Files (labels.dat)")) + if not labelsFile: return + try: + f = open(labelsFile, 'r') + data = f.read() + f.close() + self.wallet.labels = json.loads(data) + self.wallet.save() + QMessageBox.information(None, "Labels imported", "Your labels where imported from '%s'" % str(labelsFile)) + except (IOError, os.error), reason: + QMessageBox.critical(None, "Unable to export labels", "Electrum was unable to export your labels.\n" + str(reason)) + + + def do_export_labels(self): + labels = self.wallet.labels + try: + labelsFile = util.user_dir() + '/labels.dat' + f = open(labelsFile, 'w+') + json.dump(labels, f) + f.close() + QMessageBox.information(None, "Labels exported", "Your labels where exported to '%s'" % str(labelsFile)) + except (IOError, os.error), reason: + QMessageBox.critical(None, "Unable to export labels", "Electrum was unable to export your labels.\n" + str(reason)) + + def do_export_history(self): + from gui_lite import csv_transaction + csv_transaction(self.wallet) def settings_dialog(self): d = QDialog(self) @@ -1513,27 +1542,10 @@ class ElectrumWindow(QMainWindow): tabs = QTabWidget(self) vbox.addWidget(tabs) - tab2 = QWidget() - grid_ui = QGridLayout(tab2) + tab1 = QWidget() + grid_ui = QGridLayout(tab1) grid_ui.setColumnStretch(0,1) - tabs.addTab(tab2, _('Display') ) - - tab = QWidget() - grid_wallet = QGridLayout(tab) - grid_wallet.setColumnStretch(0,1) - tabs.addTab(tab, _('Wallet') ) - - fee_label = QLabel(_('Transaction fee')) - grid_wallet.addWidget(fee_label, 2, 0) - fee_e = QLineEdit() - fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) ) - grid_wallet.addWidget(fee_e, 2, 1) - msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \ - + _('Recommended value') + ': 0.001' - grid_wallet.addWidget(HelpButton(msg), 2, 2) - fee_e.textChanged.connect(lambda: numbify(fee_e,False)) - if not self.config.is_modifiable('fee'): - for w in [fee_e, fee_label]: w.setEnabled(False) + tabs.addTab(tab1, _('Display') ) nz_label = QLabel(_('Display zeros')) grid_ui.addWidget(nz_label, 3, 0) @@ -1545,33 +1557,6 @@ class ElectrumWindow(QMainWindow): nz_e.textChanged.connect(lambda: numbify(nz_e,True)) if not self.config.is_modifiable('num_zeros'): for w in [nz_e, nz_label]: w.setEnabled(False) - - - usechange_label = QLabel(_('Use change addresses')) - grid_wallet.addWidget(usechange_label, 5, 0) - usechange_combo = QComboBox() - usechange_combo.addItems(['Yes', 'No']) - usechange_combo.setCurrentIndex(not self.wallet.use_change) - grid_wallet.addWidget(usechange_combo, 5, 1) - grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 5, 2) - if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False) - - gap_label = QLabel(_('Gap limit')) - grid_wallet.addWidget(gap_label, 6, 0) - gap_e = QLineEdit() - gap_e.setText("%d"% self.wallet.gap_limit) - grid_wallet.addWidget(gap_e, 6, 1) - msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \ - + _('You may increase it if you need more receiving addresses.') + '\n\n' \ - + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \ - + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \ - + _('Warning') + ': ' \ - + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \ - + _('Do not modify it if you do not understand what you are doing, or if you expect to recover your wallet without knowing it!') + '\n\n' - grid_wallet.addWidget(HelpButton(msg), 6, 2) - gap_e.textChanged.connect(lambda: numbify(nz_e,True)) - if not self.config.is_modifiable('gap_limit'): - for w in [gap_e, gap_label]: w.setEnabled(False) gui_label=QLabel(_('Default GUI') + ':') grid_ui.addWidget(gui_label , 7, 0) @@ -1627,7 +1612,70 @@ class ElectrumWindow(QMainWindow): + _('Point of Sale') + ': ' + _('Show QR code window and amounts requested for each address. Add menu item to request amount.') + '\n\n' grid_ui.addWidget(HelpButton(hh), 10, 2) + + # wallet tab + tab2 = QWidget() + grid_wallet = QGridLayout(tab2) + grid_wallet.setColumnStretch(0,1) + tabs.addTab(tab2, _('Wallet') ) + fee_label = QLabel(_('Transaction fee')) + grid_wallet.addWidget(fee_label, 0, 0) + fee_e = QLineEdit() + fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) ) + grid_wallet.addWidget(fee_e, 0, 1) + msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \ + + _('Recommended value') + ': 0.001' + grid_wallet.addWidget(HelpButton(msg), 0, 2) + fee_e.textChanged.connect(lambda: numbify(fee_e,False)) + if not self.config.is_modifiable('fee'): + for w in [fee_e, fee_label]: w.setEnabled(False) + + usechange_label = QLabel(_('Use change addresses')) + grid_wallet.addWidget(usechange_label, 1, 0) + usechange_combo = QComboBox() + usechange_combo.addItems(['Yes', 'No']) + usechange_combo.setCurrentIndex(not self.wallet.use_change) + grid_wallet.addWidget(usechange_combo, 1, 1) + grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 1, 2) + if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False) + + gap_label = QLabel(_('Gap limit')) + grid_wallet.addWidget(gap_label, 2, 0) + gap_e = QLineEdit() + gap_e.setText("%d"% self.wallet.gap_limit) + grid_wallet.addWidget(gap_e, 2, 1) + msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \ + + _('You may increase it if you need more receiving addresses.') + '\n\n' \ + + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \ + + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \ + + _('Warning') + ': ' \ + + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \ + + _('Do not modify it if you do not understand what you are doing, or if you expect to recover your wallet without knowing it!') + '\n\n' + grid_wallet.addWidget(HelpButton(msg), 2, 2) + gap_e.textChanged.connect(lambda: numbify(nz_e,True)) + if not self.config.is_modifiable('gap_limit'): + for w in [gap_e, gap_label]: w.setEnabled(False) + + grid_wallet.setRowStretch(3,1) + + + # wallet tab + tab3 = QWidget() + grid_io = QGridLayout(tab3) + grid_io.setColumnStretch(0,1) + tabs.addTab(tab3, _('Import/Export') ) + + grid_io.addWidget(QLabel(_('Labels')), 1, 0) + grid_io.addWidget(EnterButton(_("Export"), self.do_export_labels), 1, 1) + grid_io.addWidget(EnterButton(_("Import"), self.do_import_labels), 1, 2) + grid_io.addWidget(HelpButton('Export your labels as json'), 1, 3) + + grid_io.addWidget(QLabel(_('History')), 2, 0) + grid_io.addWidget(EnterButton(_("Export"), self.do_export_history), 2, 1) + grid_io.setRowStretch(3,1) + grid_io.addWidget(HelpButton('Export your transaction history as csv'), 2, 3) + vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) From 279b85e3fef1ef398399a9152d2279b5d50708a8 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 21:28:12 +0100 Subject: [PATCH 052/124] use the same syntax as bitcoind for key import --- electrum | 16 ++++++++-------- lib/wallet.py | 16 +++++----------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/electrum b/electrum index 3f134d9d..14893454 100755 --- a/electrum +++ b/electrum @@ -70,7 +70,7 @@ options:\n --fee, -f: set transaction fee\n --fromaddr, -s: send from address 'signtx':"Sign an unsigned transaction created by a deseeded wallet\nSyntax: signtx ", 'seed': "Print the generation seed of your wallet.", - 'import': + 'importprivkey': 'Imports a key pair\nSyntax: import
:', 'signmessage': 'Signs a message with a key\nSyntax: signmessage
\nIf you want to lead or end a message with spaces, or want double spaces inside the message make sure you quote the string. I.e. " Hello This is a weird String "', @@ -99,13 +99,13 @@ offline_commands = [ 'password', 'mktx', 'signtx', 'help', 'validateaddress', 'signmessage', 'verifymessage', 'eval', 'set', 'get', 'create', 'addresses', - 'import', 'seed', + 'importprivkey', 'seed', 'deseed','reseed', 'freeze','unfreeze', 'prioritize','unprioritize'] -protected_commands = ['payto', 'password', 'mktx', 'signtx', 'seed', 'import','signmessage' ] +protected_commands = ['payto', 'password', 'mktx', 'signtx', 'seed', 'importprivkey','signmessage' ] # get password routine def prompt_password(prompt, confirm=True): @@ -395,16 +395,16 @@ if __name__ == '__main__': else: password = None - if cmd == 'import': + if cmd == 'importprivkey': # See if they specificed a key on the cmd line, if not prompt if len(args) > 1: - keypair = args[1] + sec = args[1] else: - keypair = prompt_password('Enter Address:PrivateKey (will not echo):', False) + sec = prompt_password('Enter PrivateKey (will not echo):', False) try: - wallet.import_key(keypair,password) + addr = wallet.import_key(sec,password) wallet.save() - print_msg("Keypair imported") + print_msg("Keypair imported: ", addr) except BaseException as e: print_msg("Error: Keypair import failed: " + str(e)) diff --git a/lib/wallet.py b/lib/wallet.py index 9cde7c89..eea17788 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -112,14 +112,8 @@ class Wallet: self.interface.poke('synchronizer') while not self.is_up_to_date(): time.sleep(0.1) - def import_key(self, keypair, password): + def import_key(self, sec, password): - address, sec = keypair.split(':') - if not self.is_valid(address): - raise BaseException('Invalid Bitcoin address') - if address in self.all_addresses(): - raise BaseException('Address already in wallet') - # rebuild public key from private key, compressed or uncompressed pkey = regenerate_key(sec) if not pkey: @@ -131,14 +125,14 @@ class Wallet: # rebuild private and public key from regenerated secret private_key = GetPrivKey(pkey, compressed) public_key = GetPubKey(pkey, compressed) - addr = public_key_to_bc_address(public_key) + address = public_key_to_bc_address(public_key) - # sanity check - if not address == addr : - raise BaseException('Address does not match private key') + if address in self.all_addresses(): + raise BaseException('Address already in wallet') # store the originally requested keypair into the imported keys table self.imported_keys[address] = self.pw_encode(sec, password ) + return address def new_seed(self, password): From bc9e6f88e68f665d73cb41084e2ab742989d0822 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 21:58:16 +0100 Subject: [PATCH 053/124] test password before importing key --- lib/gui_qt.py | 21 ++++++++++++++++++++- lib/wallet.py | 5 +++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index be1941b1..092bf796 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1533,6 +1533,21 @@ class ElectrumWindow(QMainWindow): from gui_lite import csv_transaction csv_transaction(self.wallet) + def do_import_privkey(self): + text, ok = QInputDialog.getText(self, _('Import private key'), _('Key') + ':') + if ok: + sec = str(text) + password = self.password_dialog() + + try: + addr = self.wallet.import_key(sec, password) + if not addr: + QMessageBox.critical(None, "Unable to import key", "error") + else: + QMessageBox.information(None, "Key imported", addr) + except BaseException as e: + QMessageBox.critical(None, "Unable to import key", str(e)) + def settings_dialog(self): d = QDialog(self) d.setWindowTitle(_('Electrum Settings')) @@ -1673,9 +1688,13 @@ class ElectrumWindow(QMainWindow): grid_io.addWidget(QLabel(_('History')), 2, 0) grid_io.addWidget(EnterButton(_("Export"), self.do_export_history), 2, 1) - grid_io.setRowStretch(3,1) grid_io.addWidget(HelpButton('Export your transaction history as csv'), 2, 3) + grid_io.addWidget(QLabel(_('Private key')), 3, 0) + grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2) + grid_io.addWidget(HelpButton('Import private key'), 3, 3) + + grid_io.setRowStretch(4,1) vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) diff --git a/lib/wallet.py b/lib/wallet.py index eea17788..58248660 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -113,6 +113,11 @@ class Wallet: while not self.is_up_to_date(): time.sleep(0.1) def import_key(self, sec, password): + # try password + try: + seed = self.pw_decode( self.seed, password) + except: + raise BaseException("Invalid password") # rebuild public key from private key, compressed or uncompressed pkey = regenerate_key(sec) From 73cd5545aaef5508c65de2165c33e02b9b6366b2 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 22:04:32 +0100 Subject: [PATCH 054/124] fix: return early on cancel --- lib/gui_qt.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 092bf796..2dac583c 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1535,10 +1535,9 @@ class ElectrumWindow(QMainWindow): def do_import_privkey(self): text, ok = QInputDialog.getText(self, _('Import private key'), _('Key') + ':') - if ok: - sec = str(text) - password = self.password_dialog() - + if not ok: return + sec = str(text) + password = self.password_dialog() try: addr = self.wallet.import_key(sec, password) if not addr: From 5a4822a044f69f9e8ac3861f46ab00071472fcca Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 22:10:17 +0100 Subject: [PATCH 055/124] add warning to help message --- lib/gui_qt.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 2dac583c..33347ccf 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1691,7 +1691,9 @@ class ElectrumWindow(QMainWindow): grid_io.addWidget(QLabel(_('Private key')), 3, 0) grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2) - grid_io.addWidget(HelpButton('Import private key'), 3, 3) + grid_io.addWidget(HelpButton('Import private key' + '\n' \ + + _('Warning: Imported keys are not recoverable with your seed.') + '\n' \ + + _('If you import keys, you will need to do backups of your wallet.')), 3, 3) grid_io.setRowStretch(4,1) vbox.addLayout(ok_cancel_buttons(d)) From 1d9c8dc5198e35cbedad3fb291852be926497194 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 22:13:41 +0100 Subject: [PATCH 056/124] don't ask pw if wallet is unencrypted --- lib/gui_qt.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 33347ccf..d23e8fb9 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1537,7 +1537,12 @@ class ElectrumWindow(QMainWindow): text, ok = QInputDialog.getText(self, _('Import private key'), _('Key') + ':') if not ok: return sec = str(text) - password = self.password_dialog() + if self.wallet.use_encryption: + password = self.password_dialog() + if not password: + return + else: + password = None try: addr = self.wallet.import_key(sec, password) if not addr: From eb5c521469cd18430ce7b3ae66fb72d4e4f7e6cb Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 22:24:03 +0100 Subject: [PATCH 057/124] delete imported key in gui --- lib/gui_qt.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index d23e8fb9..f5155910 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -923,6 +923,14 @@ class ElectrumWindow(QMainWindow): return w + def delete_imported_key(self, addr): + if self.question("Do you want to remove %s from your wallet?"%addr): + self.wallet.imported_keys.pop(addr) + self.update_receive_tab() + self.update_history_tab() + self.wallet.save() + + def create_receive_menu(self, position): # fixme: this function apparently has a side effect. # if it is not called the menu pops up several times @@ -938,6 +946,8 @@ class ElectrumWindow(QMainWindow): menu.addAction(_("View QR"), lambda: ElectrumWindow.show_qrcode("Address","bitcoin:"+addr) ) menu.addAction(_("Edit label"), lambda: self.edit_label(True)) menu.addAction(_("Sign message"), lambda: self.sign_message(addr)) + if addr in self.wallet.imported_keys: + menu.addAction(_("Remove from wallet"), lambda: self.delete_imported_key(addr)) if self.receive_tab_mode == 1: t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze") From c30deb28d339232b4bc4ba04a787d537dae8595d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 22:30:55 +0100 Subject: [PATCH 058/124] convert address to str in sign_message; update tabs after import --- lib/gui_qt.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index f5155910..8685559a 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1284,7 +1284,7 @@ class ElectrumWindow(QMainWindow): password = None try: - signature = self.wallet.sign_message(sign_address.text(), str(sign_message.toPlainText()), password) + signature = self.wallet.sign_message(str(sign_address.text()), str(sign_message.toPlainText()), password) sign_signature.setText(signature) except BaseException, e: self.show_message(str(e)) @@ -1559,6 +1559,8 @@ class ElectrumWindow(QMainWindow): QMessageBox.critical(None, "Unable to import key", "error") else: QMessageBox.information(None, "Key imported", addr) + self.update_receive_tab() + self.update_history_tab() except BaseException as e: QMessageBox.critical(None, "Unable to import key", str(e)) From 5be1d9e0192f68fa97b9a26dbc3a238c96d22902 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 22:50:59 +0100 Subject: [PATCH 059/124] improve layout for sign/verify message --- lib/gui_qt.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 8685559a..4fa136c0 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1256,24 +1256,27 @@ class ElectrumWindow(QMainWindow): d = QDialog(self) d.setModal(1) d.setWindowTitle('Sign Message') - d.setMinimumSize(270, 350) + d.setMinimumSize(410, 290) tab_widget = QTabWidget() tab = QWidget() layout = QGridLayout(tab) sign_address = QLineEdit() + sign_address.setText(address) layout.addWidget(QLabel(_('Address')), 1, 0) layout.addWidget(sign_address, 1, 1) sign_message = QTextEdit() layout.addWidget(QLabel(_('Message')), 2, 0) - layout.addWidget(sign_message, 2, 1, 2, 1) + layout.addWidget(sign_message, 2, 1) + layout.setRowStretch(2,3) - sign_signature = QLineEdit() + sign_signature = QTextEdit() layout.addWidget(QLabel(_('Signature')), 3, 0) layout.addWidget(sign_signature, 3, 1) + layout.setRowStretch(3,1) def do_sign(): if self.wallet.use_encryption: @@ -1310,11 +1313,13 @@ class ElectrumWindow(QMainWindow): verify_message = QTextEdit() layout.addWidget(QLabel(_('Message')), 2, 0) - layout.addWidget(verify_message, 2, 1, 2, 1) + layout.addWidget(verify_message, 2, 1) + layout.setRowStretch(2,3) - verify_signature = QLineEdit() + verify_signature = QTextEdit() layout.addWidget(QLabel(_('Signature')), 3, 0) layout.addWidget(verify_signature, 3, 1) + layout.setRowStretch(3,1) def do_verify(): try: From 7f8d1ff65ccc8165fccf4ec258b5c34cd7bf7341 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 22:54:01 +0100 Subject: [PATCH 060/124] fix: toPlainText() --- lib/gui_qt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 4fa136c0..8babe3cf 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1323,7 +1323,7 @@ class ElectrumWindow(QMainWindow): def do_verify(): try: - self.wallet.verify_message(verify_address.text(), verify_signature.text(), str(verify_message.toPlainText())) + self.wallet.verify_message(verify_address.text(), str(verify_signature.toPlainText()), str(verify_message.toPlainText())) self.show_message("Signature verified") except BaseException, e: self.show_message(str(e)) From b55984057957b03aec01fd5238dbd30963845373 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 5 Jan 2013 22:56:49 +0100 Subject: [PATCH 061/124] bump version number --- lib/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/version.py b/lib/version.py index 269e80fe..3b65393a 100644 --- a/lib/version.py +++ b/lib/version.py @@ -1,4 +1,4 @@ -ELECTRUM_VERSION = "1.5.8" # version of the client package +ELECTRUM_VERSION = "1.6.0" # version of the client package PROTOCOL_VERSION = '0.6' # protocol version requested SEED_VERSION = 4 # bump this everytime the seed generation is modified -TRANSLATION_ID = 34259 # version of the wiki page +TRANSLATION_ID = 34514 # version of the wiki page From 4451745387ccbe887dfebfc9d8bf17e1e991ebed Mon Sep 17 00:00:00 2001 From: "Eagle[TM]" Date: Sun, 6 Jan 2013 01:16:55 +0100 Subject: [PATCH 062/124] Draft release notes for 1.6.0 --- RELEASE-NOTES | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 8e75c6c6..8f19a9c9 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,3 +1,21 @@ +# Release 1.6.0 (07-01-2013) + +== Core +* (Feature) Add support for importing, signing and verifiying compressed keys +* (Feature) Auto reconnect to random server on disconnect +* (Feautre) Ultimate fallback to HTTP port 80 if TCP doesn't work on any server + +== Lite GUI +* (Chore) Use blockchain.info for exchange rate data +* (Feature) added currency conversion for BRL, CNY, RUB +* (Feature) Saraha theme +* (Feature) csv import/export for transactions including labels + +== Classic GUI +* (Chore) pruning servers now called "p", full servers "f" to avoid confusion with terms +* (Feature) Debits in history shown in red +* (Feature) csv import/export for transactions including labels + # Release 1.5.8 (02-01-2013) == Core From 272b79effeedae24e427e56885a2db8ad5ccf85d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 6 Jan 2013 01:44:12 +0100 Subject: [PATCH 063/124] warning against key import --- lib/gui_qt.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 8babe3cf..1d8c419e 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1549,7 +1549,13 @@ class ElectrumWindow(QMainWindow): csv_transaction(self.wallet) def do_import_privkey(self): - text, ok = QInputDialog.getText(self, _('Import private key'), _('Key') + ':') + if not self.wallet.imported_keys: + r = QMessageBox.question(None, _('Warning'), _('Warning: Imported keys are not recoverable from seed.') + ' ' \ + + _('If you ever need to restore your wallet from its seed, these keys will be lost.') + '\n\n' \ + + _('Are you sure you understand what you are doing?'), 3, 4) + if r == 4: return + + text, ok = QInputDialog.getText(self, _('Import private key'), _('Private Key') + ':') if not ok: return sec = str(text) if self.wallet.use_encryption: @@ -1713,9 +1719,7 @@ class ElectrumWindow(QMainWindow): grid_io.addWidget(QLabel(_('Private key')), 3, 0) grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2) - grid_io.addWidget(HelpButton('Import private key' + '\n' \ - + _('Warning: Imported keys are not recoverable with your seed.') + '\n' \ - + _('If you import keys, you will need to do backups of your wallet.')), 3, 3) + grid_io.addWidget(HelpButton('Import private key'), 3, 3) grid_io.setRowStretch(4,1) vbox.addLayout(ok_cancel_buttons(d)) From 04322b7cc91b8fd213e2a1c5e1ab447edb32fa77 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 6 Jan 2013 09:41:06 +0100 Subject: [PATCH 064/124] fix bug with undetected incorrect password that can damage wallet --- electrum | 8 +------- lib/gui.py | 4 ++-- lib/gui_android.py | 4 ++-- lib/gui_qt.py | 7 +++---- lib/wallet.py | 33 +++++++++++++++++++++------------ 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/electrum b/electrum index 14893454..9ee2d1d2 100755 --- a/electrum +++ b/electrum @@ -388,7 +388,7 @@ if __name__ == '__main__': exit(1) # check password try: - wallet.pw_decode( wallet.seed, password) + seed = wallet.decode_seed(password) except: print_msg("Error: This password does not decode this wallet.") exit(1) @@ -419,7 +419,6 @@ if __name__ == '__main__': print_msg(known_commands[cmd2]) elif cmd == 'seed': - seed = wallet.pw_decode( wallet.seed, password) print_msg(seed + ' "' + ' '.join(mnemonic_encode(seed)) + '"') elif cmd == 'deseed': @@ -622,11 +621,6 @@ if __name__ == '__main__': print_msg(h) elif cmd == 'password': - try: - seed = wallet.pw_decode( wallet.seed, password) - except ValueError: - sys.exit("Error: Password does not decrypt this wallet.") - new_password = prompt_password('New password:') wallet.update_password(seed, password, new_password) diff --git a/lib/gui.py b/lib/gui.py index ab25ec25..766ac819 100644 --- a/lib/gui.py +++ b/lib/gui.py @@ -65,7 +65,7 @@ def show_seed_dialog(wallet, password, parent): show_message("No seed") return try: - seed = wallet.pw_decode( wallet.seed, password) + seed = wallet.decode_seed(password) except: show_message("Incorrect password") return @@ -477,7 +477,7 @@ def change_password_dialog(wallet, parent, icon): return try: - seed = wallet.pw_decode( wallet.seed, password) + seed = wallet.decode_seed(password) except: show_message("Incorrect password") return diff --git a/lib/gui_android.py b/lib/gui_android.py index aee85d5e..77832f57 100644 --- a/lib/gui_android.py +++ b/lib/gui_android.py @@ -709,7 +709,7 @@ def seed_dialog(): password = None try: - seed = wallet.pw_decode( wallet.seed, password) + seed = wallet.decode_seed(password) except: modal_dialog('error','incorrect password') return @@ -725,7 +725,7 @@ def change_password_dialog(): password = None try: - seed = wallet.pw_decode( wallet.seed, password) + seed = wallet.decode_seed(password) except: modal_dialog('error','incorrect password') return diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 1d8c419e..2afea6b8 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1170,10 +1170,9 @@ class ElectrumWindow(QMainWindow): password = None try: - seed = wallet.pw_decode(wallet.seed, password) + seed = wallet.decode_seed(password) except: - QMessageBox.warning(parent, _('Error'), - _('Incorrect Password'), _('OK')) + QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK')) return dialog = QDialog(None) @@ -1454,7 +1453,7 @@ class ElectrumWindow(QMainWindow): new_password2 = unicode(conf_pw.text()) try: - seed = wallet.pw_decode( wallet.seed, password) + seed = wallet.decode_seed(password) except: QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK')) return diff --git a/lib/wallet.py b/lib/wallet.py index 58248660..e5583dfb 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -115,7 +115,7 @@ class Wallet: def import_key(self, sec, password): # try password try: - seed = self.pw_decode( self.seed, password) + seed = self.decode_seed(password) except: raise BaseException("Invalid password") @@ -194,7 +194,6 @@ class Wallet: if address in self.imported_keys.keys(): sec = self.pw_decode( self.imported_keys[address], password ) if not sec: return None, None - pkey = regenerate_key(sec) compressed = is_compressed(sec) secexp = pkey.secret @@ -208,14 +207,19 @@ class Wallet: for_change = True else: raise BaseException("unknown address") - try: - seed = self.pw_decode( self.seed, password) - except: - raise BaseException("Invalid password") + + seed = self.pw_decode( self.seed, password) if not seed: return None secexp = self.stretch_key(seed) secexp = ( secexp + self.get_sequence(n,for_change) ) % order compressed = False + pkey = EC_KEY(secexp) + + public_key = GetPubKey(pkey, compressed) + addr = public_key_to_bc_address(public_key) + if addr != address: + print_error('Invalid password with correct decoding') + raise BaseException('Invalid password') return secexp, compressed @@ -636,16 +640,21 @@ class Wallet: def pw_decode(self, s, password): if password is not None: secret = Hash(password) - d = DecodeAES(secret, s) - if s == self.seed: - try: - d.decode('hex') - except: - raise ValueError("Invalid password") + try: + d = DecodeAES(secret, s) + except: + raise BaseException('Invalid password') return d else: return s + def decode_seed(self, password): + # test password on an address + addr = self.all_addresses()[0] + self.get_private_key(addr, password) + # return seed + return self.pw_decode(self.seed, password) + def get_history(self, address): with self.lock: From aa6f84cecdd1d38ea037b2e6bccc45b2f3fbac2f Mon Sep 17 00:00:00 2001 From: "Eagle[TM]" Date: Sun, 6 Jan 2013 10:34:37 +0100 Subject: [PATCH 065/124] update release notes for 1.6.0 --- RELEASE-NOTES | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 8f19a9c9..c0af8217 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -3,7 +3,8 @@ == Core * (Feature) Add support for importing, signing and verifiying compressed keys * (Feature) Auto reconnect to random server on disconnect -* (Feautre) Ultimate fallback to HTTP port 80 if TCP doesn't work on any server +* (Feature) Ultimate fallback to HTTP port 80 if TCP doesn't work on any server +* (Bug) Under rare circumstances changing password with incorrect password could damage wallet == Lite GUI * (Chore) Use blockchain.info for exchange rate data From ad51d88297c3320946cdfe57a4a12235265408f1 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 6 Jan 2013 14:53:23 +0100 Subject: [PATCH 066/124] updated website url --- README | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README b/README index f34b4b05..e1738157 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ Electrum - lightweight Bitcoin client Licence: GNU GPL v3 -Author: thomasv@gitorious +Author: thomasv@bitcointalk.org Language: Python -Homepage: http://electrum.ecdsa.org/ +Homepage: http://electrum.org/ == INSTALL == @@ -42,4 +42,4 @@ On Mac OS X: == BROWSER CONFIGURATION == -see http://ecdsa.org/bitcoin_URIs.html +See http://electrum.org/bitcoin_URIs.html From 4345f637ee9c84d5a931b4e3591d61b2d17de361 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 6 Jan 2013 15:11:20 +0100 Subject: [PATCH 067/124] fix slowness with status bar text --- lib/gui_qt.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 2afea6b8..dcbb5eab 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -394,9 +394,7 @@ class ElectrumWindow(QMainWindow): text = _( "Not connected" ) icon = QIcon(":icons/status_disconnected.png") - if self.funds_error: - text = _( "Not enough funds" ) - + self.status_text = text self.statusBar().showMessage(text) self.status_button.setIcon( icon ) @@ -718,13 +716,16 @@ class ElectrumWindow(QMainWindow): if inputs: palette = QPalette() palette.setColor(self.amount_e.foregroundRole(), QColor('black')) + text = self.status_text else: palette = QPalette() palette.setColor(self.amount_e.foregroundRole(), QColor('red')) self.funds_error = True + text = _( "Not enough funds" ) + + self.statusBar().showMessage(text) self.amount_e.setPalette(palette) self.fee_e.setPalette(palette) - self.update_wallet() self.amount_e.textChanged.connect(lambda: entry_changed(False) ) self.fee_e.textChanged.connect(lambda: entry_changed(True) ) @@ -1131,6 +1132,7 @@ class ElectrumWindow(QMainWindow): return textbox def create_status_bar(self): + self.status_text = "" sb = QStatusBar() sb.setFixedHeight(35) if self.wallet.seed: From 088ed3d2dd64a9b732572256c934f48048b51c67 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 6 Jan 2013 15:57:01 +0100 Subject: [PATCH 068/124] fix: unencrypted seed --- electrum | 1 + 1 file changed, 1 insertion(+) diff --git a/electrum b/electrum index 9ee2d1d2..1849e143 100755 --- a/electrum +++ b/electrum @@ -394,6 +394,7 @@ if __name__ == '__main__': exit(1) else: password = None + seed = wallet.seed if cmd == 'importprivkey': # See if they specificed a key on the cmd line, if not prompt From 3e8099b6197568976b663b4feeebfd3fb205f1d2 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 6 Jan 2013 16:31:17 +0100 Subject: [PATCH 069/124] check decoded seed with master public key instead of an address --- lib/wallet.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/wallet.py b/lib/wallet.py index e5583dfb..ca428af1 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -649,11 +649,18 @@ class Wallet: return s def decode_seed(self, password): - # test password on an address - addr = self.all_addresses()[0] - self.get_private_key(addr, password) - # return seed - return self.pw_decode(self.seed, password) + seed = self.pw_decode(self.seed, password) + + # check decoded seed with master public key + curve = SECP256k1 + secexp = self.stretch_key(seed) + master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 ) + master_public_key = master_private_key.get_verifying_key().to_string().encode('hex') + if master_public_key != self.master_public_key: + print_error('invalid password (mpk)') + raise BaseException('Invalid password') + + return seed def get_history(self, address): From 2d031013a524e1743a7bdc911be7fb34f7b6bc2e Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Sat, 5 Jan 2013 19:44:20 +0700 Subject: [PATCH 070/124] add switch-gui button for qt --- icons.qrc | 1 + icons/switchgui.png | Bin 0 -> 1649 bytes lib/gui_lite.py | 26 +++++++++++++++----------- lib/gui_qt.py | 13 +++++++++++++ 4 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 icons/switchgui.png diff --git a/icons.qrc b/icons.qrc index ef3c6f1c..5ad965f5 100644 --- a/icons.qrc +++ b/icons.qrc @@ -13,6 +13,7 @@ icons/status_connected.png icons/status_disconnected.png icons/status_waiting.png + icons/switchgui.png icons/unconfirmed.png icons/network.png diff --git a/icons/switchgui.png b/icons/switchgui.png new file mode 100644 index 0000000000000000000000000000000000000000..ec18650d7a60d131d2be098860734acb72fcbd1d GIT binary patch literal 1649 zcmV-%29EiOP)6z``>}FTkf}wlft5@&; ze^swuy%|`M6q zB~rzELz6b|6;&hhxv@SsHqpUH(79_D>+XJm^FO>dCxLr5Mick#{h6LQTo~X2N6-og z!+@aW2wRRY2nYf}BzB_{B?uuvkv3q47zhStfI;%W9GIXMAO@xeYN&ykVG8C!oFF0? z!t}PC{PgY@>p5S<>Z%C*ar7^)M3xu;aSkVf3k2tqPbdT}hXmk)crsjn(s*3LP6R~y zI_Y;}Jw(7I{eY3Yi=Y<2uWlKW>o*Kwtt8N!HN?5ha0VjXH3?8A3?xX%dekU}BhnB{ozQ z5y4>@Xaq!4vPO<^97Y0YqKhEF<*xG0@ynPA58u9qFmODz{{-(myNOOudHLWe0_Xa6 z>Ue-W_yQ@z05!u5RO4sbj`gvc#Uw0eAYBYZ(42h}Dd@FB$Hk7aacv7H>B>~%ul9f? zMpI81DX4hEsWU_saY>9SII+?MmU$~h_u7v2+`D-)(|Bm#pKMvz=I|?90Jzxks0n*_ z-oTziXXr(6;JF)l>EJ1Z)C+Y9n4%UBQ;0K)8DffP9PgrniI95OnP9}(C$=L6o3DO$ z4t%^MW)R}o@%9Ncb%%j-!k$BC5P|Ww>xE=lA{i~NPE4gtLi56F;-Mv3>HUawaKO{rFr2IaJRo7X0u ztH`U8ZnxXdGz=;1Xt)P7t*7S!y2+|XQ8Zv@0DRWo>i2rRflWGU)dxaI>JmtXErh5lQ50pshk>8V8;!D1?19n1=34bAiVBBO zlaMJB&9YE!4~#gvM~U|yRn3{Hcb%M*NYis**oG+sQ`HRcun?Kg{kZxZfCihmn6lD5 zU0&Vtq>FK>FYbXxS;!GZ5s@zfTu5Mb^a~!Sd!eB`c(GQs2KXE(u)#a!$aA2|*g~)L zFr5KCanQLi4om<&zz_F8I0jV5_^`?68zyzYOPYmR6Wf6Mft!IzK*}CS2$?aaa`FO9 zjwO>62=yZ^eos$VOFY;2I%bL0q_5ykOO@gUEBqwi?|bv>F32hf!aIORlFmy&uS`II zkl#Q0gsqS7y7P^*eU$lnBr7%2k1O>{^2bQ7p9AUIXHo5P?DOxL zK6?r{51a$0fZnL%whTuq+80|a_sO@d$cikK{0Aekn@j!H(EtDd03~!qSaf7zbY(hY za%Ew3WdJfTF)%GKGA%JMR539+F*G_gHZ3qVIxsNVl~St!001R)MObuXVRU6WZEs|0 vW_bWIFfceRFf=VNGE^}&IyE#pG&n6VH99ab>0%Da00000NkvXXu0mjfvqI^8 literal 0 HcmV?d00001 diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 42cbfa5d..dbf92910 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -131,14 +131,17 @@ def csv_transaction(wallet): class ElectrumGui(QObject): - def __init__(self, wallet, config): + def __init__(self, wallet, config, expert=None): super(QObject, self).__init__() self.wallet = wallet self.config = config self.check_qt_version() - self.app = QApplication(sys.argv) - + self.expert = expert + if self.expert != None: + self.app = self.expert.app + else: + self.app = QApplication(sys.argv) def check_qt_version(self): qtVersion = qVersion() @@ -165,14 +168,15 @@ class ElectrumGui(QObject): if url: self.set_url(url) - - timer = Timer() - timer.start() - self.expert = gui_qt.ElectrumWindow(self.wallet, self.config) - self.expert.app = self.app - self.expert.connect_slots(timer) - self.expert.update_wallet() - self.app.exec_() + + if self.expert == None: + timer = Timer() + timer.start() + self.expert = gui_qt.ElectrumWindow(self.wallet, self.config) + self.expert.app = self.app + self.expert.connect_slots(timer) + self.expert.update_wallet() + self.app.exec_() def expand(self): """Hide the lite mode window and show pro-mode.""" diff --git a/lib/gui_qt.py b/lib/gui_qt.py index dcbb5eab..6c113e97 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -296,6 +296,7 @@ class ElectrumWindow(QMainWindow): def __init__(self, wallet, config): QMainWindow.__init__(self) + self.lite = None self.wallet = wallet self.config = config self.wallet.interface.register_callback('updated', self.update_callback) @@ -1135,6 +1136,9 @@ class ElectrumWindow(QMainWindow): self.status_text = "" sb = QStatusBar() sb.setFixedHeight(35) + qtVersion = qVersion() + if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7): + sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) ) if self.wallet.seed: sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) ) sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) ) @@ -1143,6 +1147,15 @@ class ElectrumWindow(QMainWindow): self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) ) sb.addPermanentWidget( self.status_button ) self.setStatusBar(sb) + + def go_lite(self): + import gui_lite + self.hide() + if self.lite: + self.lite.mini.show() + else: + self.lite = gui_lite.ElectrumGui(self.wallet, self.config, self) + self.lite.main(None) def new_contact_dialog(self): text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':') From 04ee4194bebb609f0d05b47191290c54a5bdc0de Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 7 Jan 2013 12:03:32 +0100 Subject: [PATCH 071/124] remove gui selector from preferences. store last state --- lib/gui_lite.py | 1 + lib/gui_qt.py | 18 +----------------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index dbf92910..4539c92c 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -180,6 +180,7 @@ class ElectrumGui(QObject): def expand(self): """Hide the lite mode window and show pro-mode.""" + self.config.set_key('gui', 'classic', True) self.mini.hide() self.expert.show() diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 6c113e97..375ba57f 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1150,6 +1150,7 @@ class ElectrumWindow(QMainWindow): def go_lite(self): import gui_lite + self.config.set_key('gui', 'lite', True) self.hide() if self.lite: self.lite.mini.show() @@ -1614,18 +1615,6 @@ class ElectrumWindow(QMainWindow): if not self.config.is_modifiable('num_zeros'): for w in [nz_e, nz_label]: w.setEnabled(False) - gui_label=QLabel(_('Default GUI') + ':') - grid_ui.addWidget(gui_label , 7, 0) - gui_combo = QComboBox() - gui_combo.addItems(['Lite', 'Classic']) - index = gui_combo.findText(self.config.get("gui","classic").capitalize()) - if index==-1: index = 1 - gui_combo.setCurrentIndex(index) - grid_ui.addWidget(gui_combo, 7, 1) - grid_ui.addWidget(HelpButton(_('Select which GUI mode to use at start up.'+'\n'+'Note: use the command line to access the "text" and "gtk" GUIs')), 7, 2) - if not self.config.is_modifiable('gui'): - for w in [gui_combo, gui_label]: w.setEnabled(False) - lang_label=QLabel(_('Language') + ':') grid_ui.addWidget(lang_label , 8, 0) lang_combo = QComboBox() @@ -1788,11 +1777,6 @@ class ElectrumWindow(QMainWindow): need_restart = False - gui_request = str(gui_combo.currentText()).lower() - if gui_request != self.config.get('gui'): - self.config.set_key('gui', gui_request, True) - need_restart = True - lang_request = languages.keys()[lang_combo.currentIndex()] if lang_request != self.config.get('language'): self.config.set_key("language", lang_request, True) From b35617f6db3b3d76c8af61c25de3b10767a63a96 Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 7 Jan 2013 14:03:45 +0100 Subject: [PATCH 072/124] merge imported labels with existing labels --- lib/gui_qt.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 375ba57f..decb1b9d 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1541,11 +1541,13 @@ class ElectrumWindow(QMainWindow): f = open(labelsFile, 'r') data = f.read() f.close() - self.wallet.labels = json.loads(data) + for key, value in json.loads(data).items(): + self.wallet.labels[key] = value self.wallet.save() QMessageBox.information(None, "Labels imported", "Your labels where imported from '%s'" % str(labelsFile)) except (IOError, os.error), reason: - QMessageBox.critical(None, "Unable to export labels", "Electrum was unable to export your labels.\n" + str(reason)) + QMessageBox.critical(None, "Unable to import labels", "Electrum was unable to import your labels.\n" + str(reason)) + def do_export_labels(self): From cf5661046b481ebfd8c7c6acec9d601c5b8ff231 Mon Sep 17 00:00:00 2001 From: slush Date: Mon, 7 Jan 2013 16:03:03 +0000 Subject: [PATCH 073/124] Import WalletFactory instead of Wallet WalletFactory and WalletBitkey classes --usb parameter enables experimental support for bitkey protocol --- electrum | 1 + lib/__init__.py | 3 ++- lib/wallet_bitkey.py | 28 ++++++++++++++++++++++++++++ lib/wallet_factory.py | 11 +++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 lib/wallet_bitkey.py create mode 100644 lib/wallet_factory.py diff --git a/electrum b/electrum index 1849e143..bd5558a0 100755 --- a/electrum +++ b/electrum @@ -139,6 +139,7 @@ def arg_parser(): parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="show debugging information") parser.add_option("-P", "--portable", action="store_true", dest="portable", default=False, help="portable wallet") parser.add_option("-L", "--lang", dest="language", default=None, help="defaut language used in GUI") + parser.add_option("-u", "--usb", dest="bitkey", action="store_true", help="Turn on support for hardware wallets (EXPERIMENTAL)") return parser diff --git a/lib/__init__.py b/lib/__init__.py index 1506f3e7..dd818943 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,7 +1,8 @@ from version import ELECTRUM_VERSION from util import format_satoshis, print_msg, print_error, set_verbosity from i18n import set_language -from wallet import Wallet, WalletSynchronizer +from wallet import WalletSynchronizer +from wallet_factory import WalletFactory as Wallet from verifier import WalletVerifier from interface import Interface, pick_random_server, DEFAULT_SERVERS from simple_config import SimpleConfig diff --git a/lib/wallet_bitkey.py b/lib/wallet_bitkey.py new file mode 100644 index 00000000..e8adf7d0 --- /dev/null +++ b/lib/wallet_bitkey.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# +# Electrum - lightweight Bitcoin client +# Copyright (C) 2011 thomasv@gitorious +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os + +from wallet import Wallet +#import bitkeylib.bitkey_pb2 as proto + +from version import ELECTRUM_VERSION +SEED_VERSION = 4 # Version of bitkey algorithm + +class WalletBitkey(Wallet): + pass diff --git a/lib/wallet_factory.py b/lib/wallet_factory.py new file mode 100644 index 00000000..5f82eca3 --- /dev/null +++ b/lib/wallet_factory.py @@ -0,0 +1,11 @@ +class WalletFactory(object): + def __new__(cls, config): + if config.get('bitkey', False): + # if user requested support for Bitkey device, + # import Bitkey driver + from wallet_bitkey import WalletBitkey + return WalletBitkey(config) + + # Load standard wallet + from wallet import Wallet + return Wallet(config) From 6498ff2bca083121ada594fde88f89598c01c5b4 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 7 Jan 2013 20:47:29 +0100 Subject: [PATCH 074/124] Add slush's bitwallet files to setup --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index a83642e6..d6004b30 100644 --- a/setup.py +++ b/setup.py @@ -50,6 +50,8 @@ setup(name = "Electrum", data_files = data_files, py_modules = ['electrum.version', 'electrum.wallet', + 'electrum.wallet_bitkey', + 'electrum.wallet_factory', 'electrum.interface', 'electrum.gui', 'electrum.gui_qt', From d2a342c22b489d8b463328984c2b900ef9eb3655 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 8 Jan 2013 11:04:04 +0100 Subject: [PATCH 075/124] POS: request amount in other currencies and convert to BTC --- TODOLIST | 4 --- lib/gui_qt.py | 86 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/TODOLIST b/TODOLIST index 988fbf14..df5b0873 100644 --- a/TODOLIST +++ b/TODOLIST @@ -14,10 +14,6 @@ code improvements: - qrcode and bmp patches are on github (they are incompatible with android) -classic gui : - - in POS mode, request amount in USD, convert to BTC - - android: - kivy-based gui diff --git a/lib/gui_qt.py b/lib/gui_qt.py index decb1b9d..86cdebbf 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -202,8 +202,9 @@ class QRCodeWidget(QWidget): class QR_Window(QWidget): - def __init__(self): + def __init__(self, exchanger): QWidget.__init__(self) + self.exchanger = exchanger self.setWindowTitle('Electrum - Invoice') self.setMinimumSize(800, 250) self.address = '' @@ -233,13 +234,23 @@ class QR_Window(QWidget): self.setLayout(main_box) - def set_content(self, addr, label, amount): + def set_content(self, addr, label, amount, currency): self.address = addr address_text = "%s" % addr if addr else "" self.address_label.setText(address_text) - self.amount = amount - amount_text = "%s BTC " % format_satoshis(amount) if amount else "" + if currency == 'BTC': currency = None + amount_text = '' + if amount: + if currency: + self.amount = Decimal(amount) / self.exchanger.exchange(1, currency) if currency else amount + else: + self.amount = Decimal(amount) + self.amount = self.amount.quantize(Decimal('1.0000')) + + if currency: + amount_text += "%s %s
" % (amount, currency) + amount_text += "%s BTC " % str(self.amount) self.amount_label.setText(amount_text) self.label = label @@ -248,7 +259,7 @@ class QR_Window(QWidget): msg = 'bitcoin:'+self.address if self.amount is not None: - msg += '?amount=%s'%(str( Decimal(self.amount) /100000000)) + msg += '?amount=%s'%(str( self.amount)) if self.label is not None: msg += '&label=%s'%(self.label) elif self.label is not None: @@ -321,7 +332,6 @@ class ElectrumWindow(QMainWindow): tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setCentralWidget(tabs) self.create_status_bar() - self.toggle_QR_window(self.receive_tab_mode == 2) g = self.config.get("winpos-qt",[100, 100, 840, 400]) self.setGeometry(g[0], g[1], g[2], g[3]) @@ -339,6 +349,7 @@ class ElectrumWindow(QMainWindow): self.history_list.setFocus(True) self.exchanger = exchange_rate.Exchanger(self) + self.toggle_QR_window(self.receive_tab_mode == 2) self.connect(self, SIGNAL("refresh_balance()"), self.update_wallet) # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913 @@ -536,34 +547,38 @@ class ElectrumWindow(QMainWindow): self.recv_changed(item) if column == 3: - address = unicode( item.text(column_addr) ) - text = unicode( item.text(3) ) + address = str( item.text(column_addr) ) + text = str( item.text(3) ) try: index = self.wallet.addresses.index(address) except: return - try: - amount = int( Decimal(text) * 100000000 ) - item.setText(3,format_satoshis(amount,False, self.wallet.num_zeros)) - except: - amount = self.wallet.requested_amounts.get(address) - if amount: - item.setText(3,format_satoshis(amount,False, self.wallet.num_zeros)) + text = text.strip().upper() + m = re.match('^(\d+(|\.\d*))\s*(|BTC|EUR|USD|GBP|CNY|JPY|RUB|BRL)$', text) + if m: + amount = m.group(1) + currency = m.group(3) + if not currency: + currency = 'BTC' else: - item.setText(3,"") - return + currency = currency.upper() + self.wallet.requested_amounts[address] = (amount, currency) - self.wallet.requested_amounts[address] = amount + label = self.wallet.labels.get(address) + if label is None: + label = self.merchant_name + ' - %04d'%(index+1) + self.wallet.labels[address] = label - label = self.wallet.labels.get(address) - if label is None: - label = self.merchant_name + ' - %04d'%(index+1) - self.wallet.labels[address] = label + if self.qr_window: + self.qr_window.set_content( address, label, amount, currency ) + else: + item.setText(3,'') + if address in self.wallet.requested_amounts: + self.wallet.requested_amounts.pop(address) + self.update_receive_item(self.receive_list.currentItem()) - if self.qr_window: - self.qr_window.set_content( address, label, amount ) def recv_changed(self, a): @@ -571,8 +586,11 @@ class ElectrumWindow(QMainWindow): if a is not None and self.qr_window and self.qr_window.isVisible(): address = str(a.text(1)) label = self.wallet.labels.get(address) - amount = self.wallet.requested_amounts.get(address) - self.qr_window.set_content( address, label, amount ) + try: + amount, currency = self.wallet.requested_amounts.get(address, (None, None)) + except: + amount, currency = None, None + self.qr_window.set_content( address, label, amount, currency ) def update_history_tab(self): @@ -1017,10 +1035,14 @@ class ElectrumWindow(QMainWindow): label = self.wallet.labels.get(address,'') item.setData(2,0,label) - amount = self.wallet.requested_amounts.get(address,None) - amount_str = format_satoshis( amount, False, self.wallet.num_zeros ) if amount is not None else "" + try: + amount, currency = self.wallet.requested_amounts.get(address, (None, None)) + except: + amount, currency = None, None + + amount_str = amount + (' ' + currency if currency else '') if amount is not None else '' item.setData(3,0,amount_str) - + c, u = self.wallet.get_addr_balance(address) balance = format_satoshis( c + u, False, self.wallet.num_zeros ) item.setData(4,0,balance) @@ -1362,15 +1384,15 @@ class ElectrumWindow(QMainWindow): def toggle_QR_window(self, show): if show and not self.qr_window: - self.qr_window = QR_Window() + self.qr_window = QR_Window(self.exchanger) self.qr_window.setVisible(True) self.qr_window_geometry = self.qr_window.geometry() item = self.receive_list.currentItem() if item: address = str(item.text(1)) label = self.wallet.labels.get(address) - amount = self.wallet.requested_amounts.get(address) - self.qr_window.set_content( address, label, amount ) + amount, currency = self.wallet.requested_amounts.get(address, (None, None)) + self.qr_window.set_content( address, label, amount, currency ) elif show and self.qr_window and not self.qr_window.isVisible(): self.qr_window.setVisible(True) From 8b9d14e3033b40ff1b61fc3ba40720576c09d77a Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 8 Jan 2013 14:29:42 +0100 Subject: [PATCH 076/124] export seed in gui/restore from seed --- electrum | 14 +++++++++-- lib/gui.py | 5 +--- lib/gui_android.py | 6 +++-- lib/gui_qt.py | 61 ++++++++++++++++++++++++++++++++++++---------- 4 files changed, 65 insertions(+), 21 deletions(-) diff --git a/electrum b/electrum index bd5558a0..5eebe71e 100755 --- a/electrum +++ b/electrum @@ -224,8 +224,18 @@ if __name__ == '__main__': wallet.init_mpk( wallet.seed ) else: # ask for seed and gap. - if not gui.seed_dialog(): exit() - wallet.init_mpk( wallet.seed ) + sg = gui.seed_dialog() + if not sg: exit() + seed, gap = sg + if not seed: exit() + wallet.gap_limit = gap + if len(seed) == 128: + wallet.seed = None + wallet.master_public_key = seed + else: + wallet.seed = str(seed) + wallet.init_mpk( wallet.seed ) + # generate the first addresses, in case we are offline if s is None or a == 'create': diff --git a/lib/gui.py b/lib/gui.py index 766ac819..03b92855 100644 --- a/lib/gui.py +++ b/lib/gui.py @@ -164,10 +164,7 @@ def run_recovery_dialog(wallet): show_message("no seed") return False - wallet.seed = seed - wallet.gap_limit = gap - wallet.save() - return True + return seed, gap diff --git a/lib/gui_android.py b/lib/gui_android.py index 77832f57..8b380d53 100644 --- a/lib/gui_android.py +++ b/lib/gui_android.py @@ -956,8 +956,10 @@ class ElectrumGui: except: modal_dialog('error: could not decode this seed') return - wallet.seed = str(seed) - return True + + gap = 5 # default + + return str(seed), gap def network_dialog(self): diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 86cdebbf..bad0a7e4 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -139,11 +139,12 @@ class StatusBarButton(QPushButton): class QRCodeWidget(QWidget): - def __init__(self, data = None): + def __init__(self, data = None, size=4): QWidget.__init__(self) self.setMinimumSize(210, 210) self.addr = None self.qr = None + self.size = size if data: self.set_addr(data) self.update_qr() @@ -156,7 +157,7 @@ class QRCodeWidget(QWidget): def update_qr(self): if self.addr and not self.qr: - self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.L) + self.qr = pyqrnative.QRCode(self.size, pyqrnative.QRErrorCorrectLevel.L) self.qr.addData(self.addr) self.qr.make() self.update() @@ -1193,11 +1194,43 @@ class ElectrumWindow(QMainWindow): else: QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK')) + def show_master_public_key(self): + dialog = QDialog(None) + dialog.setModal(1) + dialog.setWindowTitle("Master Public Key") + + main_text = QTextEdit() + main_text.setText(self.wallet.master_public_key) + main_text.setReadOnly(True) + + copy_function = lambda: self.app.clipboard().setText(self.wallet.master_public_key) + copy_button = QPushButton(_("Copy to Clipboard")) + copy_button.clicked.connect(copy_function) + + qrw = QRCodeWidget(self.wallet.master_public_key, 6) + + + ok_button = QPushButton(_("OK")) + ok_button.setDefault(True) + ok_button.clicked.connect(dialog.accept) + + main_layout = QGridLayout() + main_layout.addWidget(qrw, 0, 0 ) + + main_layout.addWidget(main_text, 0, 1, 1, -1) + + main_layout.setColumnStretch( 0, 1) + main_layout.addWidget(copy_button, 1, 1) + main_layout.addWidget(ok_button, 1, 3) + dialog.setLayout(main_layout) + + dialog.exec_() + + @staticmethod def show_seed_dialog(wallet, parent=None): if not wallet.seed: - QMessageBox.information(parent, _('Message'), - _('No seed'), _('OK')) + QMessageBox.information(parent, _('Message'), _('No seed'), _('OK')) return if wallet.use_encryption: @@ -1534,10 +1567,10 @@ class ElectrumWindow(QMainWindow): gap = int(unicode(gap_e.text())) except: QMessageBox.warning(None, _('Error'), 'error', 'OK') - sys.exit(0) + return try: - seed = unicode(seed_e.text()) + seed = str(seed_e.text()) seed.decode('hex') except: print_error("Warning: Not hex, trying decode") @@ -1545,15 +1578,13 @@ class ElectrumWindow(QMainWindow): seed = mnemonic.mn_decode( seed.split(' ') ) except: QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK')) - sys.exit(0) + return + if not seed: QMessageBox.warning(None, _('Error'), _('No seed'), 'OK') - sys.exit(0) - - wallet.seed = str(seed) - #print repr(wallet.seed) - wallet.gap_limit = gap - return True + return + + return seed, gap def do_import_labels(self): @@ -1748,6 +1779,10 @@ class ElectrumWindow(QMainWindow): grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2) grid_io.addWidget(HelpButton('Import private key'), 3, 3) + grid_io.addWidget(QLabel(_('Master Public key')), 4, 0) + grid_io.addWidget(EnterButton(_("Show"), self.show_master_public_key), 4, 1) + grid_io.addWidget(HelpButton('Your master public key can be used to created a watching-only (deseeded) wallet'), 4, 3) + grid_io.setRowStretch(4,1) vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) From fe1a406309db0f6fff41b5da3d1d2faed67cb0b0 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 8 Jan 2013 17:01:45 +0100 Subject: [PATCH 077/124] seed dialog simplification --- lib/gui_qt.py | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index bad0a7e4..220301fc 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1248,46 +1248,45 @@ class ElectrumWindow(QMainWindow): dialog = QDialog(None) dialog.setModal(1) - dialog.setWindowTitle("Electrum") + dialog.setWindowTitle(_("Electrum") + ' - ' + _('Seed')) brainwallet = ' '.join(mnemonic.mn_encode(seed)) - msg = _("Your wallet generation seed is") +":

\"" + brainwallet + "\"

" \ - + _("Please write down or memorize these 12 words (order is important).") + " " \ - + _("This seed will allow you to recover your wallet in case of computer failure.") + "

" \ - + _("WARNING: Never disclose your seed. Never type it on a website.") + "

" + label1 = QLabel(_("Your wallet generation seed is")+ ":") - main_text = QLabel(msg) - main_text.setWordWrap(True) + seed_text = QTextEdit(brainwallet) + seed_text.setReadOnly(True) + seed_text.setMaximumHeight(55) + + msg2 = _("Please write down or memorize these 12 words (order is important).") + " " \ + + _("This seed will allow you to recover your wallet in case of computer failure.") + "

" \ + + _("Your seed is included in the following QR code.") + " " \ + + _("Use it if you need to access your wallet from a mobile phone.") + "

" \ + + ""+_("WARNING")+":
"+_("Never disclose your seed. Never type it on a website.") + "

" + label2 = QLabel(msg2) + label2.setWordWrap(True) logo = QLabel() logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56)) + logo.setMaximumWidth(60) - if parent: - app = parent.app - else: - app = QApplication - - copy_function = lambda: app.clipboard().setText(brainwallet) - copy_button = QPushButton(_("Copy to Clipboard")) - copy_button.clicked.connect(copy_function) - - show_qr_function = lambda: ElectrumWindow.show_qrcode(_("Seed"), seed) - qr_button = QPushButton(_("View as QR Code")) - qr_button.clicked.connect(show_qr_function) + qrw = QRCodeWidget(seed, 4) ok_button = QPushButton(_("OK")) ok_button.setDefault(True) ok_button.clicked.connect(dialog.accept) main_layout = QGridLayout() - main_layout.addWidget(logo, 0, 0) - main_layout.addWidget(main_text, 0, 1, 1, -1) - main_layout.addWidget(copy_button, 1, 1) - main_layout.addWidget(qr_button, 1, 2) - main_layout.addWidget(ok_button, 1, 3) - dialog.setLayout(main_layout) + main_layout.addWidget(logo, 0, 0, 2, 1) + main_layout.addWidget(label1, 0, 1) + main_layout.addWidget(seed_text, 1, 1, 1, 3) + main_layout.addWidget( label2, 2, 0, 1, 3) + main_layout.addWidget(qrw, 2, 3) + + #main_layout.addWidget(qr_button, 3, 2) + main_layout.addWidget(ok_button, 3, 3) + dialog.setLayout(main_layout) dialog.exec_() @staticmethod From ec11dac3af962ea48280fd7ec7df7b28d49b5d94 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 8 Jan 2013 21:30:03 +0100 Subject: [PATCH 078/124] restore from mpk (text) --- electrum | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/electrum b/electrum index 5eebe71e..15e91024 100755 --- a/electrum +++ b/electrum @@ -315,10 +315,14 @@ if __name__ == '__main__': if not seed: sys.exit("Error: No seed") - wallet.seed = str(seed) - wallet.init_mpk( wallet.seed ) - if not options.offline: + if len(seed) == 128: + wallet.seed = None + wallet.master_public_key = seed + else: + wallet.seed = str(seed) + wallet.init_mpk( wallet.seed ) + if not options.offline: interface = Interface(config) interface.start() wallet.interface = interface From a3cf8eb81d49e014f0a894fd26bf281a4c7265f1 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 9 Jan 2013 09:46:17 +0100 Subject: [PATCH 079/124] remove until an easy way back is provided --- lib/gui_qt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 220301fc..2000b86c 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1160,8 +1160,8 @@ class ElectrumWindow(QMainWindow): sb = QStatusBar() sb.setFixedHeight(35) qtVersion = qVersion() - if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7): - sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) ) + # if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7): + # sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) ) if self.wallet.seed: sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) ) sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) ) From 72a271beeed01335d3419506b3ad4c22d779a5f4 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 9 Jan 2013 10:11:01 +0100 Subject: [PATCH 080/124] fix regression caused by tooltips --- lib/gui_qt.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 2000b86c..1db1ac0f 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -464,9 +464,7 @@ class ElectrumWindow(QMainWindow): def tx_label_clicked(self, item, column): if column==2 and item.isSelected(): - tx_hash = str(item.toolTip(0)) self.is_edit=True - #if not self.wallet.labels.get(tx_hash): item.setText(2,'') item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled) self.history_list.editItem( item, column ) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled) @@ -476,7 +474,7 @@ class ElectrumWindow(QMainWindow): if self.is_edit: return self.is_edit=True - tx_hash = str(item.toolTip(0)) + tx_hash = str(item.data(0, Qt.UserRole).toString()) tx = self.wallet.transactions.get(tx_hash) s = self.wallet.labels.get(tx_hash) text = unicode( item.text(2) ) From 29305af7807b2fdf76ac8e22e44293514d28cad9 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 9 Jan 2013 10:20:34 +0100 Subject: [PATCH 081/124] text change: access -> install --- lib/gui_qt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 1db1ac0f..4953a2b4 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1258,8 +1258,8 @@ class ElectrumWindow(QMainWindow): msg2 = _("Please write down or memorize these 12 words (order is important).") + " " \ + _("This seed will allow you to recover your wallet in case of computer failure.") + "

" \ - + _("Your seed is included in the following QR code.") + " " \ - + _("Use it if you need to access your wallet from a mobile phone.") + "

" \ + + _("Your seed is also included in the following QR code.") + " " \ + + _("Use it if you need to install your wallet on a mobile phone.") + "

" \ + ""+_("WARNING")+":
"+_("Never disclose your seed. Never type it on a website.") + "

" label2 = QLabel(msg2) label2.setWordWrap(True) From e9e117712af55fdd071d571ce0f30655ba977b91 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 9 Jan 2013 10:42:19 +0100 Subject: [PATCH 082/124] enable gui switch button (classic <-> lite) both ways --- lib/gui_lite.py | 15 +++++++++------ lib/gui_qt.py | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 4539c92c..80a18168 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -302,15 +302,18 @@ class MiniWindow(QDialog): self.receive_button.setObjectName("receive_button") self.receive_button.setDefault(True) + self.switch_button = QPushButton( QIcon(":icons/switchgui.png"),'' ) + self.switch_button.clicked.connect(expand_callback) + main_layout = QGridLayout(self) - main_layout.addWidget(self.balance_label, 0, 0) - main_layout.addWidget(self.receive_button, 0, 1) + main_layout.addWidget(self.switch_button, 0, 0) + main_layout.addWidget(self.balance_label, 0, 1) + main_layout.addWidget(self.receive_button, 0, 2) - main_layout.addWidget(self.address_input, 1, 0) - - main_layout.addWidget(self.amount_input, 2, 0) - main_layout.addWidget(self.send_button, 2, 1) + main_layout.addWidget(self.address_input, 1, 0, 1, 3) + main_layout.addWidget(self.amount_input, 2, 0, 1, 2) + main_layout.addWidget(self.send_button, 2, 2) self.history_list = history_widget.HistoryWidget() self.history_list.setObjectName("history") diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 4953a2b4..0525cfef 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1158,8 +1158,8 @@ class ElectrumWindow(QMainWindow): sb = QStatusBar() sb.setFixedHeight(35) qtVersion = qVersion() - # if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7): - # sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) ) + if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7): + sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) ) if self.wallet.seed: sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) ) sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) ) From b79da90d02c110c5caf55dd5435456ba12088c8f Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 9 Jan 2013 14:19:03 +0100 Subject: [PATCH 083/124] fix history width --- lib/gui_lite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 80a18168..b4786779 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -320,7 +320,7 @@ class MiniWindow(QDialog): self.history_list.hide() self.history_list.setAlternatingRowColors(True) - main_layout.addWidget(self.history_list, 3, 0, 1, 2) + main_layout.addWidget(self.history_list, 3, 0, 1, 3) self.receiving = receiving_widget.ReceivingWidget(self) From e9544df2701068463812ef79cc26cf6f654b9ede Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 10 Jan 2013 14:39:03 +0100 Subject: [PATCH 084/124] fix width of switch button in lite gui --- lib/gui_lite.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index b4786779..e6a820e8 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -303,6 +303,7 @@ class MiniWindow(QDialog): self.receive_button.setDefault(True) self.switch_button = QPushButton( QIcon(":icons/switchgui.png"),'' ) + self.switch_button.setMaximumWidth(25) self.switch_button.clicked.connect(expand_callback) main_layout = QGridLayout(self) From 67866afa7e7ceb0a4c73d2596e198f1f883e2e30 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 10 Jan 2013 17:58:04 +0100 Subject: [PATCH 085/124] replace receive button with menu --- lib/gui_lite.py | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index e6a820e8..34b5716e 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -298,23 +298,19 @@ class MiniWindow(QDialog): self.send_button.clicked.connect(self.send) # Creating the receive button - self.receive_button = QPushButton(_("&Receive")) - self.receive_button.setObjectName("receive_button") - self.receive_button.setDefault(True) - self.switch_button = QPushButton( QIcon(":icons/switchgui.png"),'' ) self.switch_button.setMaximumWidth(25) + self.switch_button.setFlat(True) self.switch_button.clicked.connect(expand_callback) main_layout = QGridLayout(self) - main_layout.addWidget(self.switch_button, 0, 0) - main_layout.addWidget(self.balance_label, 0, 1) - main_layout.addWidget(self.receive_button, 0, 2) + main_layout.addWidget(self.balance_label, 0, 0, 1, 3) + main_layout.addWidget(self.switch_button, 0, 3) - main_layout.addWidget(self.address_input, 1, 0, 1, 3) + main_layout.addWidget(self.address_input, 1, 0, 1, 4) main_layout.addWidget(self.amount_input, 2, 0, 1, 2) - main_layout.addWidget(self.send_button, 2, 2) + main_layout.addWidget(self.send_button, 2, 2, 1, 2) self.history_list = history_widget.HistoryWidget() self.history_list.setObjectName("history") @@ -349,11 +345,9 @@ class MiniWindow(QDialog): extra_layout.setColumnMinimumWidth(0,200) self.receiving_box.setLayout(extra_layout) - main_layout.addWidget(self.receiving_box,0,3,-1,3) + main_layout.addWidget(self.receiving_box,0,4,-1,3) self.receiving_box.hide() - self.receive_button.clicked.connect(self.toggle_receiving_layout) - # Creating the menu bar menubar = QMenuBar() electrum_menu = menubar.addMenu(_("&Bitcoin")) @@ -396,6 +390,11 @@ class MiniWindow(QDialog): theme_action.toggled.connect(delegate) theme_group.addAction(theme_action) view_menu.addSeparator() + + show_history = view_menu.addAction(_("Receive")) + show_history.setCheckable(True) + show_history.toggled.connect(self.toggle_receiving_layout) + show_history = view_menu.addAction(_("Show History")) show_history.setCheckable(True) show_history.toggled.connect(self.show_history) @@ -433,16 +432,8 @@ class MiniWindow(QDialog): def toggle_receiving_layout(self): if self.receiving_box.isVisible(): self.receiving_box.hide() - self.receive_button.setProperty("isActive", False) - - qApp.style().unpolish(self.receive_button) - qApp.style().polish(self.receive_button) else: self.receiving_box.show() - self.receive_button.setProperty("isActive", 'true') - - qApp.style().unpolish(self.receive_button) - qApp.style().polish(self.receive_button) def toggle_theme(self, theme_name): old_path = QDir.currentPath() From 49c6055880fd1d93aeb80c2370e2f78dd3093f3c Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 10 Jan 2013 18:02:28 +0100 Subject: [PATCH 086/124] adjust width of history list --- lib/gui_lite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 34b5716e..7908d6f1 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -317,7 +317,7 @@ class MiniWindow(QDialog): self.history_list.hide() self.history_list.setAlternatingRowColors(True) - main_layout.addWidget(self.history_list, 3, 0, 1, 3) + main_layout.addWidget(self.history_list, 3, 0, 1, 4) self.receiving = receiving_widget.ReceivingWidget(self) From cec247d1a554cb4af66e11daf480af66ea116fd7 Mon Sep 17 00:00:00 2001 From: Maran Date: Thu, 10 Jan 2013 21:51:27 +0100 Subject: [PATCH 087/124] Modified styling, removed obsolete styles and added wether we have are displaying receiving addresses --- data/cleanlook/style.css | 51 +++++----------------------------------- lib/gui_lite.py | 29 ++++++++++++++++------- 2 files changed, 26 insertions(+), 54 deletions(-) diff --git a/data/cleanlook/style.css b/data/cleanlook/style.css index eda21177..888cbcf9 100644 --- a/data/cleanlook/style.css +++ b/data/cleanlook/style.css @@ -9,19 +9,18 @@ MiniWindow QPushButton { border-radius: 0px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 white, stop: 1 #E6E6E6); - min-height: 25px; + min-height: 30px; min-width: 30px; } #send_button{ - color: #E5F2FF; + color: #FFF; border: 1px solid #3786E6; border-radius: 4px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #72B2F8, stop: 1 #3484E6); - min-width: 80px; - min-height: 23px; padding: 2px; + width: 20px; } #send_button:disabled{ @@ -30,65 +29,27 @@ MiniWindow QPushButton { border-radius: 4px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #A5CFFA, stop: 1 #72B2F8); - min-width: 80px; - min-height: 23px; - padding: 2px; } -#receive_button -{ - color: #777; - border: 1px solid #CCC; - border-radius: 0px; - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 white, stop: 1 #E6E6E6); - min-height: 25px; - min-width: 30px; -} -#receive_button[isActive=true] -{ - color: #575757; - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 white, stop: 1 #D1D1D1); -} - #address_input, #amount_input, #label_input { color: #000; padding: 5px; - border-radius: 4px; + border-radius: 5px; + min-height: 23px; border: 1px solid #AAA9A9; - width: 225px; + width: 200px; } #address_input[isValid=true] { color: #4D9948; - padding: 5px; - border-radius: 4px; - border: 1px solid #AAA9A9; - width: 225px; - margin-top: 4px; } #address_input[isValid=false] { color: #CE4141; - padding: 5px; - border-radius: 4px; - border: 1px solid #AAA9A9; - width: 225px; - margin-top: 4px; } -#address_input[isValid=placeholder] -{ - color: blue; - padding: 5px; - border-radius: 4px; - border: 1px solid #AAA9A9; - width: 225px; - margin-top: 4px; -} #balance_label { color: #333; diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 7908d6f1..fc4199d9 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -312,13 +312,14 @@ class MiniWindow(QDialog): main_layout.addWidget(self.amount_input, 2, 0, 1, 2) main_layout.addWidget(self.send_button, 2, 2, 1, 2) + self.send_button.setMaximumWidth(125) + self.history_list = history_widget.HistoryWidget() self.history_list.setObjectName("history") self.history_list.hide() self.history_list.setAlternatingRowColors(True) main_layout.addWidget(self.history_list, 3, 0, 1, 4) - self.receiving = receiving_widget.ReceivingWidget(self) self.receiving.setObjectName("receiving") @@ -337,6 +338,7 @@ class MiniWindow(QDialog): self.receiving.itemDoubleClicked.connect(self.receiving.edit_label) self.receiving.itemChanged.connect(self.receiving.update_label) + # Label extra_layout.addWidget( QLabel(_('Selecting an address will copy it to the clipboard.\nDouble clicking the label will allow you to edit it.') ),0,0) @@ -391,9 +393,16 @@ class MiniWindow(QDialog): theme_group.addAction(theme_action) view_menu.addSeparator() - show_history = view_menu.addAction(_("Receive")) - show_history.setCheckable(True) - show_history.toggled.connect(self.toggle_receiving_layout) + show_receiving = view_menu.addAction(_("Show Receiving addresses")) + show_receiving.setCheckable(True) + show_receiving.toggled.connect(self.toggle_receiving_layout) + + show_receiving_toggle = self.config.get("gui_show_receiving",False) + show_receiving.setChecked(show_receiving_toggle) + self.show_receiving = show_receiving + + self.toggle_receiving_layout(show_receiving_toggle ) + show_history = view_menu.addAction(_("Show History")) show_history.setCheckable(True) @@ -429,11 +438,6 @@ class MiniWindow(QDialog): self.setObjectName("main_window") self.show() - def toggle_receiving_layout(self): - if self.receiving_box.isVisible(): - self.receiving_box.hide() - else: - self.receiving_box.show() def toggle_theme(self, theme_name): old_path = QDir.currentPath() @@ -447,6 +451,7 @@ class MiniWindow(QDialog): g = self.geometry() self.config.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()],True) self.config.set_key("gui_show_history", self.history_list.isVisible(),True) + self.config.set_key("gui_show_receiving", self.show_receiving.isVisible(),True) super(MiniWindow, self).closeEvent(event) qApp.quit() @@ -601,6 +606,12 @@ class MiniWindow(QDialog): QMessageBox.information(self, "Electrum - " + _("Reporting Bugs"), _("Please report any bugs as issues on github: https://github.com/spesmilo/electrum/issues")) + def toggle_receiving_layout(self, toggle_state): + if toggle_state: + self.receiving_box.show() + else: + self.receiving_box.hide() + def show_history(self, toggle_state): if toggle_state: self.main_layout.setRowMinimumHeight(3,200) From 01399103bec901b9c7f9b8eda39480f1f24197fe Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 10 Jan 2013 22:04:52 +0100 Subject: [PATCH 088/124] improve layout and help text for master public key --- lib/gui_qt.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 0525cfef..9912a62a 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1200,28 +1200,27 @@ class ElectrumWindow(QMainWindow): main_text = QTextEdit() main_text.setText(self.wallet.master_public_key) main_text.setReadOnly(True) - - copy_function = lambda: self.app.clipboard().setText(self.wallet.master_public_key) - copy_button = QPushButton(_("Copy to Clipboard")) - copy_button.clicked.connect(copy_function) - + main_text.setMaximumHeight(170) qrw = QRCodeWidget(self.wallet.master_public_key, 6) - ok_button = QPushButton(_("OK")) ok_button.setDefault(True) ok_button.clicked.connect(dialog.accept) main_layout = QGridLayout() - main_layout.addWidget(qrw, 0, 0 ) + main_layout.addWidget(QLabel(_('Your Master Public Key is:')), 0, 0, 1, 2) - main_layout.addWidget(main_text, 0, 1, 1, -1) + main_layout.addWidget(main_text, 1, 0) + main_layout.addWidget(qrw, 1, 1 ) - main_layout.setColumnStretch( 0, 1) - main_layout.addWidget(copy_button, 1, 1) - main_layout.addWidget(ok_button, 1, 3) - dialog.setLayout(main_layout) + vbox = QVBoxLayout() + vbox.addLayout(main_layout) + hbox = QHBoxLayout() + hbox.addStretch(1) + hbox.addWidget(ok_button) + vbox.addLayout(hbox) + dialog.setLayout(vbox) dialog.exec_() @@ -1778,7 +1777,9 @@ class ElectrumWindow(QMainWindow): grid_io.addWidget(QLabel(_('Master Public key')), 4, 0) grid_io.addWidget(EnterButton(_("Show"), self.show_master_public_key), 4, 1) - grid_io.addWidget(HelpButton('Your master public key can be used to created a watching-only (deseeded) wallet'), 4, 3) + grid_io.addWidget(HelpButton(_('Your master public key can be used to create receiving adresses, but not to sign transactions.') + ' ' \ + + _('If you give it to someone, they will be able to see your transactions, but not to spend your money.') + ' ' \ + + _('If you restore your wallet from it, a watching-only (deseeded) wallet will be created.')), 4, 3) grid_io.setRowStretch(4,1) vbox.addLayout(ok_cancel_buttons(d)) From 2957e2a4d1a8dcb67f252cbcd7e4ebe91112d5ae Mon Sep 17 00:00:00 2001 From: Maran Date: Thu, 10 Jan 2013 22:18:38 +0100 Subject: [PATCH 089/124] Updated release notes --- RELEASE-NOTES | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index c0af8217..bc45b3fa 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,3 +1,15 @@ +# Release 1.6.1 (10-01-2013) +== Core +* (Feature) Restore from MPK (actually added in 1.6.0) +* (Chore) Rewrote seed dialog to be more clear +* (Chore) Added button to the classic GUI to switch to lite GUI, also added the same button to the Lite GUI to keep it consistent. + +== Classic GUI +* (Feature) Added request amount in other currencies to POS. + +== Lite GUI +* (Chore) Removed receiving button in favor of menu item to keep it consistant with the history toggle. + # Release 1.6.0 (07-01-2013) == Core From 84c5f90997f82536886b3d02bfa3d60f5a6f36d8 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 10 Jan 2013 23:10:01 +0100 Subject: [PATCH 090/124] improve layout of the seed dialog --- lib/gui_qt.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 9912a62a..d3cb232c 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1253,13 +1253,12 @@ class ElectrumWindow(QMainWindow): seed_text = QTextEdit(brainwallet) seed_text.setReadOnly(True) - seed_text.setMaximumHeight(55) + seed_text.setMaximumHeight(130) msg2 = _("Please write down or memorize these 12 words (order is important).") + " " \ - + _("This seed will allow you to recover your wallet in case of computer failure.") + "

" \ - + _("Your seed is also included in the following QR code.") + " " \ - + _("Use it if you need to install your wallet on a mobile phone.") + "

" \ - + ""+_("WARNING")+":
"+_("Never disclose your seed. Never type it on a website.") + "

" + + _("This seed will allow you to recover your wallet in case of computer failure.") + " " \ + + _("Your seed is also displayed as QR code, in case you want to use the same wallet on a mobile phone.") + "

" \ + + ""+_("WARNING")+": " + _("Never disclose your seed. Never type it on a website.") + "

" label2 = QLabel(msg2) label2.setWordWrap(True) @@ -1273,17 +1272,26 @@ class ElectrumWindow(QMainWindow): ok_button.setDefault(True) ok_button.clicked.connect(dialog.accept) - main_layout = QGridLayout() - main_layout.addWidget(logo, 0, 0, 2, 1) - main_layout.addWidget(label1, 0, 1) - main_layout.addWidget(seed_text, 1, 1, 1, 3) + grid = QGridLayout() + #main_layout.addWidget(logo, 0, 0) - main_layout.addWidget( label2, 2, 0, 1, 3) - main_layout.addWidget(qrw, 2, 3) + grid.addWidget(logo, 0, 0) + grid.addWidget(label1, 0, 1) - #main_layout.addWidget(qr_button, 3, 2) - main_layout.addWidget(ok_button, 3, 3) - dialog.setLayout(main_layout) + grid.addWidget(seed_text, 1, 0, 1, 2) + + grid.addWidget(qrw, 0, 2, 2, 1) + + vbox = QVBoxLayout() + vbox.addLayout(grid) + vbox.addWidget(label2) + + hbox = QHBoxLayout() + hbox.addStretch(1) + hbox.addWidget(ok_button) + vbox.addLayout(hbox) + + dialog.setLayout(vbox) dialog.exec_() @staticmethod From 70717ab4462f3d7a481201c99d75e26734d036ad Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 10 Jan 2013 23:12:28 +0100 Subject: [PATCH 091/124] change seed text --- lib/gui_qt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index d3cb232c..25fab485 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1257,7 +1257,7 @@ class ElectrumWindow(QMainWindow): msg2 = _("Please write down or memorize these 12 words (order is important).") + " " \ + _("This seed will allow you to recover your wallet in case of computer failure.") + " " \ - + _("Your seed is also displayed as QR code, in case you want to use the same wallet on a mobile phone.") + "

" \ + + _("Your seed is also displayed as QR code, in case you want to transfer it to a mobile phone.") + "

" \ + ""+_("WARNING")+": " + _("Never disclose your seed. Never type it on a website.") + "

" label2 = QLabel(msg2) label2.setWordWrap(True) From 272cdaceaba6f6c094b43ede08eea485a435eb20 Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Fri, 11 Jan 2013 11:57:49 +0700 Subject: [PATCH 092/124] add default filename for export history --- lib/gui_lite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 80a18168..b6dce878 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -88,7 +88,7 @@ def load_theme_paths(): def csv_transaction(wallet): try: - fileName = QFileDialog.getSaveFileName(QWidget(), 'Select file to export your wallet transactions to', os.path.expanduser('~/'), "*.csv") + fileName = QFileDialog.getSaveFileName(QWidget(), 'Select file to export your wallet transactions to', os.path.expanduser('~/electrum-history.csv'), "*.csv") if fileName: with open(fileName, "w+") as csvfile: transaction = csv.writer(csvfile) From a2d0ae00829572f7d574ad543d7ce9a7db39c83f Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Sun, 6 Jan 2013 00:47:25 +0700 Subject: [PATCH 093/124] remember column widths --- lib/gui_qt.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 0525cfef..5f230740 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -304,6 +304,8 @@ def ok_cancel_buttons(dialog): return hbox +default_column_widths = { "history":[40,140,350,140,140], "receive":[50,310,200,130,130,10], "contacts":[350,330,100] } + class ElectrumWindow(QMainWindow): def __init__(self, wallet, config): @@ -324,6 +326,7 @@ class ElectrumWindow(QMainWindow): self.completions = QStringListModel() self.tabs = tabs = QTabWidget(self) + self.column_widths = self.config.get("column-widths", default_column_widths ) tabs.addTab(self.create_history_tab(), _('History') ) tabs.addTab(self.create_send_tab(), _('Send') ) tabs.addTab(self.create_receive_tab(), _('Receive') ) @@ -430,11 +433,8 @@ class ElectrumWindow(QMainWindow): def create_history_tab(self): self.history_list = l = MyTreeWidget(self) l.setColumnCount(5) - l.setColumnWidth(0, 40) - l.setColumnWidth(1, 140) - l.setColumnWidth(2, 350) - l.setColumnWidth(3, 140) - l.setColumnWidth(4, 140) + for i,width in enumerate(self.column_widths['history']): + l.setColumnWidth(i, width) l.setHeaderLabels( [ '', _( 'Date' ), _( 'Description' ) , _('Amount'), _('Balance')] ) self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked) self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed) @@ -1061,12 +1061,8 @@ class ElectrumWindow(QMainWindow): l.setColumnHidden(3, not self.receive_tab_mode == 2) l.setColumnHidden(4, self.receive_tab_mode == 0) l.setColumnHidden(5, not self.receive_tab_mode == 1) - l.setColumnWidth(0, 50) - l.setColumnWidth(1, 310) - l.setColumnWidth(2, 200) - l.setColumnWidth(3, 130) - l.setColumnWidth(4, 130) - l.setColumnWidth(5, 10) + for i,width in enumerate(self.column_widths['receive']): + l.setColumnWidth(i, width) gap = 0 is_red = False @@ -1122,9 +1118,8 @@ class ElectrumWindow(QMainWindow): l = self.contacts_list l.clear() - l.setColumnWidth(0, 350) - l.setColumnWidth(1, 330) - l.setColumnWidth(2, 100) + for i,width in enumerate(self.column_widths['contacts']): + l.setColumnWidth(i, width) alias_targets = [] for alias, v in self.wallet.aliases.items(): @@ -2023,6 +2018,14 @@ class ElectrumWindow(QMainWindow): def closeEvent(self, event): g = self.geometry() self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True) + widths = { "history":[], "receive":[], "contacts":[] } + for i in range(self.history_list.columnCount()): + widths["history"].append(self.history_list.columnWidth(i)) + for i in range(self.receive_list.columnCount()): + widths["receive"].append(self.receive_list.columnWidth(i)) + for i in range(self.contacts_list.columnCount()): + widths["contacts"].append(self.contacts_list.columnWidth(i)) + self.config.set_key("column-widths", widths, True) event.accept() From 867d75769f950c698174f8dffbecb29089661a4e Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 11 Jan 2013 10:20:28 +0100 Subject: [PATCH 094/124] Revert "remember column widths" This reverts commit a2d0ae00829572f7d574ad543d7ce9a7db39c83f. --- lib/gui_qt.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index fd328bda..25fab485 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -304,8 +304,6 @@ def ok_cancel_buttons(dialog): return hbox -default_column_widths = { "history":[40,140,350,140,140], "receive":[50,310,200,130,130,10], "contacts":[350,330,100] } - class ElectrumWindow(QMainWindow): def __init__(self, wallet, config): @@ -326,7 +324,6 @@ class ElectrumWindow(QMainWindow): self.completions = QStringListModel() self.tabs = tabs = QTabWidget(self) - self.column_widths = self.config.get("column-widths", default_column_widths ) tabs.addTab(self.create_history_tab(), _('History') ) tabs.addTab(self.create_send_tab(), _('Send') ) tabs.addTab(self.create_receive_tab(), _('Receive') ) @@ -433,8 +430,11 @@ class ElectrumWindow(QMainWindow): def create_history_tab(self): self.history_list = l = MyTreeWidget(self) l.setColumnCount(5) - for i,width in enumerate(self.column_widths['history']): - l.setColumnWidth(i, width) + l.setColumnWidth(0, 40) + l.setColumnWidth(1, 140) + l.setColumnWidth(2, 350) + l.setColumnWidth(3, 140) + l.setColumnWidth(4, 140) l.setHeaderLabels( [ '', _( 'Date' ), _( 'Description' ) , _('Amount'), _('Balance')] ) self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked) self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed) @@ -1061,8 +1061,12 @@ class ElectrumWindow(QMainWindow): l.setColumnHidden(3, not self.receive_tab_mode == 2) l.setColumnHidden(4, self.receive_tab_mode == 0) l.setColumnHidden(5, not self.receive_tab_mode == 1) - for i,width in enumerate(self.column_widths['receive']): - l.setColumnWidth(i, width) + l.setColumnWidth(0, 50) + l.setColumnWidth(1, 310) + l.setColumnWidth(2, 200) + l.setColumnWidth(3, 130) + l.setColumnWidth(4, 130) + l.setColumnWidth(5, 10) gap = 0 is_red = False @@ -1118,8 +1122,9 @@ class ElectrumWindow(QMainWindow): l = self.contacts_list l.clear() - for i,width in enumerate(self.column_widths['contacts']): - l.setColumnWidth(i, width) + l.setColumnWidth(0, 350) + l.setColumnWidth(1, 330) + l.setColumnWidth(2, 100) alias_targets = [] for alias, v in self.wallet.aliases.items(): @@ -2027,14 +2032,6 @@ class ElectrumWindow(QMainWindow): def closeEvent(self, event): g = self.geometry() self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True) - widths = { "history":[], "receive":[], "contacts":[] } - for i in range(self.history_list.columnCount()): - widths["history"].append(self.history_list.columnWidth(i)) - for i in range(self.receive_list.columnCount()): - widths["receive"].append(self.receive_list.columnWidth(i)) - for i in range(self.contacts_list.columnCount()): - widths["contacts"].append(self.contacts_list.columnWidth(i)) - self.config.set_key("column-widths", widths, True) event.accept() From 753c49e2bca933034080f0545cd5a5b185cc2a33 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 11 Jan 2013 10:26:19 +0100 Subject: [PATCH 095/124] bump version number --- lib/version.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/version.py b/lib/version.py index 3b65393a..682183de 100644 --- a/lib/version.py +++ b/lib/version.py @@ -1,4 +1,4 @@ -ELECTRUM_VERSION = "1.6.0" # version of the client package -PROTOCOL_VERSION = '0.6' # protocol version requested -SEED_VERSION = 4 # bump this everytime the seed generation is modified -TRANSLATION_ID = 34514 # version of the wiki page +ELECTRUM_VERSION = "1.6.1" # version of the client package +PROTOCOL_VERSION = '0.6' # protocol version requested +SEED_VERSION = 4 # bump this everytime the seed generation is modified +TRANSLATION_ID = 34864 # version of the wiki page From ff8f68b87c05b3b34c2feb4e4fc3c0392449767c Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 11 Jan 2013 10:52:43 +0100 Subject: [PATCH 096/124] release notes --- RELEASE-NOTES | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index bc45b3fa..01263086 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,14 +1,15 @@ -# Release 1.6.1 (10-01-2013) +# Release 1.6.1 (11-01-2013) + == Core -* (Feature) Restore from MPK (actually added in 1.6.0) -* (Chore) Rewrote seed dialog to be more clear -* (Chore) Added button to the classic GUI to switch to lite GUI, also added the same button to the Lite GUI to keep it consistent. +* It is now possible to restore a wallet from MPK (this will create a watching-only wallet) +* A switch button allows to easily switch between Lite and Classic GUI. == Classic GUI -* (Feature) Added request amount in other currencies to POS. +* Seed and MPK help dialogs were rewritten +* Point of Sale: requested amounts can be expressed in other currencies and are converted to bitcoin. == Lite GUI -* (Chore) Removed receiving button in favor of menu item to keep it consistant with the history toggle. +* The receiving button was removed in favor of a menu item to keep it consistent with the history toggle. # Release 1.6.0 (07-01-2013) From ccc127bc5943adec29a41fc139c55ff96e70b13c Mon Sep 17 00:00:00 2001 From: Maran Date: Fri, 11 Jan 2013 11:24:11 +0100 Subject: [PATCH 097/124] Fix receiving widget not saving state properly --- lib/gui_lite.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index fc4199d9..41fa1076 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -398,10 +398,11 @@ class MiniWindow(QDialog): show_receiving.toggled.connect(self.toggle_receiving_layout) show_receiving_toggle = self.config.get("gui_show_receiving",False) + print self.config.get("gui_show_receiving") show_receiving.setChecked(show_receiving_toggle) self.show_receiving = show_receiving - self.toggle_receiving_layout(show_receiving_toggle ) + self.toggle_receiving_layout(show_receiving_toggle) show_history = view_menu.addAction(_("Show History")) @@ -451,7 +452,7 @@ class MiniWindow(QDialog): g = self.geometry() self.config.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()],True) self.config.set_key("gui_show_history", self.history_list.isVisible(),True) - self.config.set_key("gui_show_receiving", self.show_receiving.isVisible(),True) + self.config.set_key("gui_show_receiving", self.receiving_box.isVisible(),True) super(MiniWindow, self).closeEvent(event) qApp.quit() From 609a7c7fc076272dca8917903b843fc4db20c4c2 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 11 Jan 2013 11:25:59 +0100 Subject: [PATCH 098/124] rm unneeded print --- lib/gui_lite.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 41fa1076..e4ba7c70 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -398,7 +398,6 @@ class MiniWindow(QDialog): show_receiving.toggled.connect(self.toggle_receiving_layout) show_receiving_toggle = self.config.get("gui_show_receiving",False) - print self.config.get("gui_show_receiving") show_receiving.setChecked(show_receiving_toggle) self.show_receiving = show_receiving From 87079a6e2c163daab0c0f198cd47c0b331575b82 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 11 Jan 2013 17:32:06 +0100 Subject: [PATCH 099/124] update paths --- contrib/build-wine/README | 2 +- contrib/build-wine/build-electrum.sh | 6 +++--- contrib/build-wine/prepare-wine.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/build-wine/README b/contrib/build-wine/README index 80083874..73b58845 100644 --- a/contrib/build-wine/README +++ b/contrib/build-wine/README @@ -1,7 +1,7 @@ These scripts can be used for cross-compilation of Windows Electrum executables from Linux/Wine. Usage: -1. Copy content of this directory to /electrum-wine. +1. Copy content of this directory to /build-wine. 2. Install Wine (version 1.4 or 1.5+ works fine, 1.4.1 has bug). 3. Run "./prepare-wine.sh", it will download all dependencies. When you'll be asked, always leave default settings and press "Next >". 4. By running "./build-electrum.sh", sources will be packed into three separate versions to dist/ directory: diff --git a/contrib/build-wine/build-electrum.sh b/contrib/build-wine/build-electrum.sh index f0e65cf2..1f025fb6 100755 --- a/contrib/build-wine/build-electrum.sh +++ b/contrib/build-wine/build-electrum.sh @@ -1,11 +1,11 @@ #!/bin/bash # You probably need to update only this link -ELECTRUM_URL=https://github.com/downloads/spesmilo/electrum/Electrum-1.5.6.tar.gz -NAME_ROOT=electrum-1.5.6 +ELECTRUM_URL=https://github.com/downloads/spesmilo/electrum/Electrum-1.6.1.tar.gz +NAME_ROOT=electrum-1.6.1 # These settings probably don't need any change -export WINEPREFIX=~/.wine-electrum +export WINEPREFIX=/opt/wine-electrum PYHOME=c:/python26 PYTHON="wine $PYHOME/python.exe -OO -B" diff --git a/contrib/build-wine/prepare-wine.sh b/contrib/build-wine/prepare-wine.sh index 955ed606..e407db47 100755 --- a/contrib/build-wine/prepare-wine.sh +++ b/contrib/build-wine/prepare-wine.sh @@ -9,7 +9,7 @@ NSIS_URL=http://prdownloads.sourceforge.net/nsis/nsis-2.46-setup.exe?download #ZBAR_URL=http://sourceforge.net/projects/zbar/files/zbar/0.10/zbar-0.10-setup.exe/download # These settings probably don't need change -export WINEPREFIX=~/.wine-electrum +export WINEPREFIX=/opt/wine-electrum PYHOME=c:/python26 PYTHON="wine $PYHOME/python.exe -OO -B" From 8175eaed4b424a36b55ef308bd9d4e14ce6ac84b Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 11 Jan 2013 17:39:39 +0100 Subject: [PATCH 100/124] update one more path in windows builder --- contrib/build-wine/build-electrum-git.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/build-wine/build-electrum-git.sh b/contrib/build-wine/build-electrum-git.sh index e31d9ef6..bafa2277 100755 --- a/contrib/build-wine/build-electrum-git.sh +++ b/contrib/build-wine/build-electrum-git.sh @@ -6,7 +6,7 @@ BRANCH=master NAME_ROOT=electrum # These settings probably don't need any change -export WINEPREFIX=~/.wine-electrum +export WINEPREFIX=/opt/wine-electrum PYHOME=c:/python26 PYTHON="wine $PYHOME/python.exe -OO -B" From 3276c3394ff2f0009ca59900d7ab1efa6a9dd6c8 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 11 Jan 2013 18:01:40 +0100 Subject: [PATCH 101/124] strip spaces around private key --- lib/gui_qt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 25fab485..cb2ed593 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1631,7 +1631,7 @@ class ElectrumWindow(QMainWindow): text, ok = QInputDialog.getText(self, _('Import private key'), _('Private Key') + ':') if not ok: return - sec = str(text) + sec = str(text).strip() if self.wallet.use_encryption: password = self.password_dialog() if not password: From c981940b165abe7d5492139408d599c9bb80d8da Mon Sep 17 00:00:00 2001 From: rdymac Date: Sat, 12 Jan 2013 02:54:27 +0100 Subject: [PATCH 102/124] Update lib/gui_qt.py Fixed a typo on line 1788 --- lib/gui_qt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index cb2ed593..7551c03c 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1785,7 +1785,7 @@ class ElectrumWindow(QMainWindow): grid_io.addWidget(QLabel(_('Master Public key')), 4, 0) grid_io.addWidget(EnterButton(_("Show"), self.show_master_public_key), 4, 1) - grid_io.addWidget(HelpButton(_('Your master public key can be used to create receiving adresses, but not to sign transactions.') + ' ' \ + grid_io.addWidget(HelpButton(_('Your master public key can be used to create receiving addresses, but not to sign transactions.') + ' ' \ + _('If you give it to someone, they will be able to see your transactions, but not to spend your money.') + ' ' \ + _('If you restore your wallet from it, a watching-only (deseeded) wallet will be created.')), 4, 3) From a3e728a1543aa38fe263d1c574cf3736ada300e1 Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Sat, 12 Jan 2013 17:23:35 +0700 Subject: [PATCH 103/124] fixed remember column widths for receive modes --- lib/gui_qt.py | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 25fab485..d77e001f 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -304,6 +304,9 @@ def ok_cancel_buttons(dialog): return hbox +default_column_widths = { "history":[40,140,350,140,140], "contacts":[350,330,100], + "receive":[[50,310,200,130,130,10],[50,310,200,130,130,10],[50,310,200,130,130,10]] } + class ElectrumWindow(QMainWindow): def __init__(self, wallet, config): @@ -324,6 +327,7 @@ class ElectrumWindow(QMainWindow): self.completions = QStringListModel() self.tabs = tabs = QTabWidget(self) + self.column_widths = self.config.get("column-widths", default_column_widths ) tabs.addTab(self.create_history_tab(), _('History') ) tabs.addTab(self.create_send_tab(), _('Send') ) tabs.addTab(self.create_receive_tab(), _('Receive') ) @@ -430,11 +434,8 @@ class ElectrumWindow(QMainWindow): def create_history_tab(self): self.history_list = l = MyTreeWidget(self) l.setColumnCount(5) - l.setColumnWidth(0, 40) - l.setColumnWidth(1, 140) - l.setColumnWidth(2, 350) - l.setColumnWidth(3, 140) - l.setColumnWidth(4, 140) + for i,width in enumerate(self.column_widths['history']): + l.setColumnWidth(i, width) l.setHeaderLabels( [ '', _( 'Date' ), _( 'Description' ) , _('Amount'), _('Balance')] ) self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked) self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed) @@ -922,12 +923,24 @@ class ElectrumWindow(QMainWindow): def receive_tab_set_mode(self, i): + self.save_column_widths() self.receive_tab_mode = i self.config.set_key('qt_receive_tab_mode', self.receive_tab_mode, True) self.wallet.save() self.update_receive_tab() self.toggle_QR_window(self.receive_tab_mode == 2) + def save_column_widths(self): + widths = [] + for i in range(self.receive_list.columnCount()): + widths.append(self.receive_list.columnWidth(i)) + self.column_widths["receive"][self.receive_tab_mode] = widths + self.column_widths["history"] = [] + for i in range(self.history_list.columnCount()): + self.column_widths["history"].append(self.history_list.columnWidth(i)) + self.column_widths["contacts"] = [] + for i in range(self.contacts_list.columnCount()): + self.column_widths["contacts"].append(self.contacts_list.columnWidth(i)) def create_contacts_tab(self): l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')]) @@ -1061,12 +1074,8 @@ class ElectrumWindow(QMainWindow): l.setColumnHidden(3, not self.receive_tab_mode == 2) l.setColumnHidden(4, self.receive_tab_mode == 0) l.setColumnHidden(5, not self.receive_tab_mode == 1) - l.setColumnWidth(0, 50) - l.setColumnWidth(1, 310) - l.setColumnWidth(2, 200) - l.setColumnWidth(3, 130) - l.setColumnWidth(4, 130) - l.setColumnWidth(5, 10) + for i,width in enumerate(self.column_widths['receive'][self.receive_tab_mode]): + l.setColumnWidth(i, width) gap = 0 is_red = False @@ -1122,9 +1131,8 @@ class ElectrumWindow(QMainWindow): l = self.contacts_list l.clear() - l.setColumnWidth(0, 350) - l.setColumnWidth(1, 330) - l.setColumnWidth(2, 100) + for i,width in enumerate(self.column_widths['contacts']): + l.setColumnWidth(i, width) alias_targets = [] for alias, v in self.wallet.aliases.items(): @@ -2032,6 +2040,8 @@ class ElectrumWindow(QMainWindow): def closeEvent(self, event): g = self.geometry() self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True) + self.save_column_widths() + self.config.set_key("column-widths", self.column_widths, True) event.accept() From 84f845383b6717d166edbd224596571a46ae4b68 Mon Sep 17 00:00:00 2001 From: rdymac Date: Sat, 12 Jan 2013 18:42:53 +0100 Subject: [PATCH 104/124] Update lib/version.py 167 strings added to the wiki to be translated (+6 more that are not being added to translations files yet) --- lib/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/version.py b/lib/version.py index 682183de..cc0d66ae 100644 --- a/lib/version.py +++ b/lib/version.py @@ -1,4 +1,4 @@ ELECTRUM_VERSION = "1.6.1" # version of the client package PROTOCOL_VERSION = '0.6' # protocol version requested SEED_VERSION = 4 # bump this everytime the seed generation is modified -TRANSLATION_ID = 34864 # version of the wiki page +TRANSLATION_ID = 34952 # version of the wiki page From 9af25bde66b6e01c95b9e7ef75a3fb9d487d02cb Mon Sep 17 00:00:00 2001 From: rdymac Date: Sat, 12 Jan 2013 18:56:53 +0100 Subject: [PATCH 105/124] Update lib/gui_qt.py Added the 5 _("") strings to be translated. Don't know if python cares about single and double quotes. --- lib/gui_qt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index cb2ed593..c11ae545 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1773,15 +1773,15 @@ class ElectrumWindow(QMainWindow): grid_io.addWidget(QLabel(_('Labels')), 1, 0) grid_io.addWidget(EnterButton(_("Export"), self.do_export_labels), 1, 1) grid_io.addWidget(EnterButton(_("Import"), self.do_import_labels), 1, 2) - grid_io.addWidget(HelpButton('Export your labels as json'), 1, 3) + grid_io.addWidget(HelpButton(_('Export your labels as json')), 1, 3) grid_io.addWidget(QLabel(_('History')), 2, 0) grid_io.addWidget(EnterButton(_("Export"), self.do_export_history), 2, 1) - grid_io.addWidget(HelpButton('Export your transaction history as csv'), 2, 3) + grid_io.addWidget(HelpButton(_('Export your transaction history as csv')), 2, 3) grid_io.addWidget(QLabel(_('Private key')), 3, 0) grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2) - grid_io.addWidget(HelpButton('Import private key'), 3, 3) + grid_io.addWidget(HelpButton(_('Import private key')), 3, 3) grid_io.addWidget(QLabel(_('Master Public key')), 4, 0) grid_io.addWidget(EnterButton(_("Show"), self.show_master_public_key), 4, 1) @@ -1863,7 +1863,7 @@ class ElectrumWindow(QMainWindow): interface = wallet.interface if parent: if interface.is_connected: - status = _("Connected to")+" %s\n%d blocks"%(interface.host, wallet.verifier.height) + status = _("Connected to")+" %s\n%d "+_("blocks")%(interface.host, wallet.verifier.height) else: status = _("Not connected") server = interface.server @@ -1974,7 +1974,7 @@ class ElectrumWindow(QMainWindow): for w in [server_host, server_port, server_protocol, servers_list_widget]: w.setEnabled(False) # auto cycle - autocycle_cb = QCheckBox('Try random servers if disconnected') + autocycle_cb = QCheckBox(_('Try random servers if disconnected')) autocycle_cb.setChecked(wallet.config.get('auto_cycle', False)) grid.addWidget(autocycle_cb, 3, 1, 3, 2) if not wallet.config.is_modifiable('auto_cycle'): autocycle_cb.setEnabled(False) From 04c95c4b642fdcca8d3a5b7440854b80d8e43227 Mon Sep 17 00:00:00 2001 From: rdymac Date: Sun, 13 Jan 2013 03:56:13 +0100 Subject: [PATCH 106/124] Update lib/gui_qt.py --- lib/gui_qt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 8978a93a..1906c83d 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1693,7 +1693,7 @@ class ElectrumWindow(QMainWindow): index = 0 lang_combo.setCurrentIndex(index) grid_ui.addWidget(lang_combo, 8, 1) - grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart). ')), 8, 2) + grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart).')+' '), 8, 2) if not self.config.is_modifiable('language'): for w in [lang_combo, lang_label]: w.setEnabled(False) @@ -1710,7 +1710,7 @@ class ElectrumWindow(QMainWindow): index = 0 cur_combo.setCurrentIndex(index) grid_ui.addWidget(cur_combo, 9, 1) - grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes. ')), 9, 2) + grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes.')+' '), 9, 2) view_label=QLabel(_('Receive Tab') + ':') grid_ui.addWidget(view_label , 10, 0) @@ -1718,7 +1718,7 @@ class ElectrumWindow(QMainWindow): view_combo.addItems([_('Simple'), _('Advanced'), _('Point of Sale')]) view_combo.setCurrentIndex(self.receive_tab_mode) grid_ui.addWidget(view_combo, 10, 1) - hh = _('This selects the interaction mode of the "Receive" tab. ') + '\n\n' \ + hh = _('This selects the interaction mode of the "Receive" tab.')+' ' + '\n\n' \ + _('Simple') + ': ' + _('Show only addresses and labels.') + '\n\n' \ + _('Advanced') + ': ' + _('Show address balances and add extra menu items to freeze/prioritize addresses.') + '\n\n' \ + _('Point of Sale') + ': ' + _('Show QR code window and amounts requested for each address. Add menu item to request amount.') + '\n\n' @@ -1749,7 +1749,7 @@ class ElectrumWindow(QMainWindow): usechange_combo.addItems(['Yes', 'No']) usechange_combo.setCurrentIndex(not self.wallet.use_change) grid_wallet.addWidget(usechange_combo, 1, 1) - grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 1, 2) + grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions.')+' '), 1, 2) if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False) gap_label = QLabel(_('Gap limit')) @@ -1760,7 +1760,7 @@ class ElectrumWindow(QMainWindow): msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \ + _('You may increase it if you need more receiving addresses.') + '\n\n' \ + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \ - + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \ + + _('Given the current status of your address sequence, the minimum gap limit you can use is:')+' ' + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \ + _('Warning') + ': ' \ + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \ + _('Do not modify it if you do not understand what you are doing, or if you expect to recover your wallet without knowing it!') + '\n\n' From 0a12b0570f3fbb8343f0dd17f34ac988e542229e Mon Sep 17 00:00:00 2001 From: bkkcoins Date: Sun, 13 Jan 2013 11:52:03 +0700 Subject: [PATCH 107/124] make tx details scrollable --- lib/gui_qt.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 8978a93a..5d26c152 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -459,9 +459,28 @@ class ElectrumWindow(QMainWindow): def tx_details(self, tx_hash): - tx_details = self.wallet.get_tx_details(tx_hash) - QMessageBox.information(self, 'Details', tx_details, 'OK') + dialog = QDialog(None) + dialog.setModal(1) + dialog.setWindowTitle(_("Transaction Details")) + main_text = QTextEdit() + main_text.setText(self.wallet.get_tx_details(tx_hash)) + main_text.setReadOnly(True) + main_text.setMinimumSize(550,275) + + ok_button = QPushButton(_("OK")) + ok_button.setDefault(True) + ok_button.clicked.connect(dialog.accept) + + hbox = QHBoxLayout() + hbox.addStretch(1) + hbox.addWidget(ok_button) + + vbox = QVBoxLayout() + vbox.addWidget(main_text) + vbox.addLayout(hbox) + dialog.setLayout(vbox) + dialog.exec_() def tx_label_clicked(self, item, column): if column==2 and item.isSelected(): From 964c7dd75b0af0492edea9f706ac8a486b7f8b17 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 13 Jan 2013 09:01:31 +0100 Subject: [PATCH 108/124] fix network dialog --- lib/gui_qt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 5d26c152..4eef05e6 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1890,7 +1890,7 @@ class ElectrumWindow(QMainWindow): interface = wallet.interface if parent: if interface.is_connected: - status = _("Connected to")+" %s\n%d "+_("blocks")%(interface.host, wallet.verifier.height) + status = _("Connected to")+" %s\n%d "%(interface.host, wallet.verifier.height)+_("blocks") else: status = _("Not connected") server = interface.server From e0cf89a794a5efeaad45b6bde23b140e246d9db5 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 13 Jan 2013 21:40:28 +0100 Subject: [PATCH 109/124] update help for importprivkey --- electrum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrum b/electrum index 15e91024..4ce9fddc 100755 --- a/electrum +++ b/electrum @@ -71,7 +71,7 @@ options:\n --fee, -f: set transaction fee\n --fromaddr, -s: send from address 'seed': "Print the generation seed of your wallet.", 'importprivkey': - 'Imports a key pair\nSyntax: import

:', + 'Import a private key\nSyntax: importprivkey ', 'signmessage': 'Signs a message with a key\nSyntax: signmessage
\nIf you want to lead or end a message with spaces, or want double spaces inside the message make sure you quote the string. I.e. " Hello This is a weird String "', 'verifymessage': From ca5798c2b784255eb48e56dd7fadfff94dfd4c51 Mon Sep 17 00:00:00 2001 From: rdymac Date: Mon, 14 Jan 2013 01:02:29 +0100 Subject: [PATCH 110/124] Fixed some typos in text strings Fixed some typos in text strings --- lib/gui_lite.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 96032c96..733aaa43 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -124,7 +124,7 @@ def csv_transaction(wallet): balance_string = format_satoshis(balance, False, wallet.num_zeros) transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string]) - QMessageBox.information(None,"CSV Export created", "Your CSV export has been succesfully created.") + QMessageBox.information(None,"CSV Export created", "Your CSV export has been successfully created.") except (IOError, os.error), reason: QMessageBox.critical(None,"Unable to create csv", "Electrum was unable to produce a transaction export.\n" + str(reason)) @@ -600,7 +600,7 @@ class MiniWindow(QDialog): def show_about(self): QMessageBox.about(self, "Electrum", - _("Electrum's focus is speed, with low resource usage and simplifying Bitcoin. You do not need to perform regular backups, because your wallet can be recovered from a secret phrase that you can memorize or write on paper. Startup times are instant because it operates in conjuction with high-performance servers that handle the most complicated parts of the Bitcoin system.")) + _("Electrum's focus is speed, with low resource usage and simplifying Bitcoin. You do not need to perform regular backups, because your wallet can be recovered from a secret phrase that you can memorize or write on paper. Startup times are instant because it operates in conjunction with high-performance servers that handle the most complicated parts of the Bitcoin system.")) def show_report_bug(self): QMessageBox.information(self, "Electrum - " + _("Reporting Bugs"), @@ -746,7 +746,7 @@ class ReceivePopup(QDialog): class MiniActuator: """Initialize the definitions relating to themes and - sending/recieving bitcoins.""" + sending/receiving bitcoins.""" def __init__(self, wallet): @@ -906,7 +906,7 @@ class MiniActuator: def copy_master_public_key(self): master_pubkey = self.wallet.master_public_key qApp.clipboard().setText(master_pubkey) - QMessageBox.information(None,"Copy succesful", "Your public master key has been copied to your clipboard.") + QMessageBox.information(None,"Copy successful", "Your public master key has been copied to your clipboard.") def acceptbit(self, currency): From 4fc59480386b58790fd4b40b0961521a1d5f00c3 Mon Sep 17 00:00:00 2001 From: rdymac Date: Mon, 14 Jan 2013 01:34:06 +0100 Subject: [PATCH 111/124] Added icons' text strings to be translated Added icons' text strings to be translated. Updated the wiki with the three text strings that weren't there. --- lib/gui_qt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 0728d48d..d77e5deb 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1186,13 +1186,13 @@ class ElectrumWindow(QMainWindow): sb.setFixedHeight(35) qtVersion = qVersion() if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7): - sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) ) + sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), _("Switch to Lite Mode"), self.go_lite ) ) if self.wallet.seed: - sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) ) - sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) ) + sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), _("Password"), lambda: self.change_password_dialog(self.wallet, self) ) ) + sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), _("Preferences"), self.settings_dialog ) ) if self.wallet.seed: - sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) ) - self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) ) + sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), _("Seed"), lambda: self.show_seed_dialog(self.wallet, self) ) ) + self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), _("Network"), lambda: self.network_dialog(self.wallet, self) ) sb.addPermanentWidget( self.status_button ) self.setStatusBar(sb) From 198d6a810a8921ad568acfb98556b7059a8ec4b1 Mon Sep 17 00:00:00 2001 From: rdymac Date: Wed, 16 Jan 2013 00:59:22 +0100 Subject: [PATCH 112/124] "public master key" to master public key Fixed the order of these two words --- lib/gui_lite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 733aaa43..c7e2bdc8 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -906,7 +906,7 @@ class MiniActuator: def copy_master_public_key(self): master_pubkey = self.wallet.master_public_key qApp.clipboard().setText(master_pubkey) - QMessageBox.information(None,"Copy successful", "Your public master key has been copied to your clipboard.") + QMessageBox.information(None,"Copy successful", "Your master public key has been copied to your clipboard.") def acceptbit(self, currency): From 5ece1d31b5d1cd338f55f0cf24fea463701d86da Mon Sep 17 00:00:00 2001 From: rdymac Date: Wed, 16 Jan 2013 02:00:15 +0100 Subject: [PATCH 113/124] Changed electrum-desktop.com to electrum.org Changed the website link under the Help menu option from electrum-desktop.com to electrum.org --- lib/gui_lite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 733aaa43..86a0de31 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -596,7 +596,7 @@ class MiniWindow(QDialog): self.actuator.acceptbit(self.quote_currencies[0]) def the_website(self): - webbrowser.open("http://electrum-desktop.com") + webbrowser.open("http://electrum.org") def show_about(self): QMessageBox.about(self, "Electrum", From 79dcc2bd57604df8d15a23249156be1acaafd147 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 16 Jan 2013 06:47:27 +0100 Subject: [PATCH 114/124] fix csv export in lite gui --- lib/gui_lite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index ce1af10f..5ea8e62d 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -366,7 +366,7 @@ class MiniWindow(QDialog): backup_wallet.triggered.connect(self.backup_wallet) export_csv = extra_menu.addAction( _("&Export transactions to CSV") ) - export_csv.triggered.connect(lambda: csv_transaction(self.wallet)) + export_csv.triggered.connect(lambda: csv_transaction(self.actuator.wallet)) master_key = extra_menu.addAction( _("Copy master public key to clipboard") ) master_key.triggered.connect(self.actuator.copy_master_public_key) From de778da3c5c1249c6ff06cf48aafe8a3d6a8b604 Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 15 Jan 2013 21:31:21 +0100 Subject: [PATCH 115/124] Implemented update notification to classic GUI --- RELEASE-NOTES | 5 ++++ lib/gui_qt.py | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 01263086..f0be273c 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,3 +1,8 @@ +# Release 1.6.2 (Not released) + +== Classic GUI +* Added new version notification + # Release 1.6.1 (11-01-2013) == Core diff --git a/lib/gui_qt.py b/lib/gui_qt.py index d77e5deb..0e641fbd 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -44,6 +44,9 @@ import exchange_rate from decimal import Decimal import platform +import httplib +import socket +import webbrowser if platform.system() == 'Windows': MONOSPACE_FONT = 'Lucida Console' @@ -54,6 +57,79 @@ else: ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$' +from version import ELECTRUM_VERSION +import re + +class UpdateLabel(QtGui.QLabel): + def __init__(self, config, parent=None): + QtGui.QLabel.__init__(self, parent) + + self.setText("New version available:") + try: + con = httplib.HTTPConnection('electrum.bysh.me', 80, timeout=5) + con.request("GET", "/version") + res = con.getresponse() + if res.status == 200: + latest_version = res.read() + latest_version = latest_version.replace("\n","") + if(re.match('^\d\.\d.\d$', latest_version)): + self.config = config + + self.latest_version = tuple(latest_version.split(".")) + self.latest_version_text = latest_version + self.current_version = tuple(ELECTRUM_VERSION.split(".")) + + if(self.latest_version > self.current_version): + latest_seen = self.config.get("last_seen_version") + if(self.latest_version > latest_seen): + self.setText("New version available: " + '.'.join(list(self.latest_version))) + + except socket.error as msg: + print "Could not retrieve version information" + + def ignore_this_version(self): + self.setText("") + self.config.set_key("last_seen_version", self.latest_version) + QMessageBox.information(self, _("Preference saved"), _("Notifications about this update will not be shown again.")) + self.dialog.done(0) + + def ignore_all_version(self): + self.setText("") + self.config.set_key("last_seen_version", tuple(['9','9','9'])) + QMessageBox.information(self, _("Preference saved"), _("No more notifications about version updates will be shown.")) + self.dialog.done(0) + + def open_website(self): + webbrowser.open("http://electrum.org/download.html") + self.dialog.done(0) + + def mouseReleaseEvent(self, event): + dialog = QDialog(self) + dialog.setWindowTitle(_('Electrum update')) + dialog.setModal(1) + + main_layout = QGridLayout() + main_layout.addWidget(QLabel("A new version of Electrum is available: " + self.latest_version_text), 0,0,1,3) + + ignore_version = QPushButton(_("Ignore this version")) + ignore_version.clicked.connect(self.ignore_this_version) + + ignore_all_versions = QPushButton(_("Ignore all versions")) + ignore_all_versions.clicked.connect(self.ignore_all_version) + + open_website = QPushButton(_("Goto download page")) + open_website.clicked.connect(self.open_website) + + main_layout.addWidget(ignore_version, 1, 0) + main_layout.addWidget(ignore_all_versions, 1, 1) + main_layout.addWidget(open_website, 1, 2) + + dialog.setLayout(main_layout) + + self.dialog = dialog + + if not dialog.exec_(): return + def numbify(entry, is_int = False): text = unicode(entry.text()).strip() pos = entry.cursorPosition() @@ -1180,11 +1256,16 @@ class ElectrumWindow(QMainWindow): textbox.setReadOnly(True) return textbox + def create_status_bar(self): self.status_text = "" sb = QStatusBar() sb.setFixedHeight(35) qtVersion = qVersion() + + update_notification = UpdateLabel(self.config) + sb.addPermanentWidget(update_notification) + if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7): sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), _("Switch to Lite Mode"), self.go_lite ) ) if self.wallet.seed: @@ -1194,6 +1275,7 @@ class ElectrumWindow(QMainWindow): sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), _("Seed"), lambda: self.show_seed_dialog(self.wallet, self) ) ) self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), _("Network"), lambda: self.network_dialog(self.wallet, self) ) sb.addPermanentWidget( self.status_button ) + self.setStatusBar(sb) def go_lite(self): From 499a336c458b6c2724a82af4a322dcf0fa43d4c4 Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 16 Jan 2013 09:32:03 +0100 Subject: [PATCH 116/124] Removed extra set gui line and updated version address --- lib/gui_qt.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 0e641fbd..93be18a3 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -64,9 +64,8 @@ class UpdateLabel(QtGui.QLabel): def __init__(self, config, parent=None): QtGui.QLabel.__init__(self, parent) - self.setText("New version available:") try: - con = httplib.HTTPConnection('electrum.bysh.me', 80, timeout=5) + con = httplib.HTTPConnection('electrum.org', 80, timeout=5) con.request("GET", "/version") res = con.getresponse() if res.status == 200: From 9009fba35cd184744e3eb1f16e1566752234d7c8 Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 16 Jan 2013 12:37:21 +0100 Subject: [PATCH 117/124] Implemented new version comparing --- lib/gui_qt.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 93be18a3..f5278190 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -68,33 +68,38 @@ class UpdateLabel(QtGui.QLabel): con = httplib.HTTPConnection('electrum.org', 80, timeout=5) con.request("GET", "/version") res = con.getresponse() - if res.status == 200: - latest_version = res.read() - latest_version = latest_version.replace("\n","") - if(re.match('^\d\.\d.\d$', latest_version)): - self.config = config - - self.latest_version = tuple(latest_version.split(".")) - self.latest_version_text = latest_version - self.current_version = tuple(ELECTRUM_VERSION.split(".")) - - if(self.latest_version > self.current_version): - latest_seen = self.config.get("last_seen_version") - if(self.latest_version > latest_seen): - self.setText("New version available: " + '.'.join(list(self.latest_version))) - except socket.error as msg: print "Could not retrieve version information" + return + + if res.status == 200: + self.latest_version = res.read() + self.latest_version = self.latest_version.replace("\n","") + if(re.match('^\d\.\d.\d$', self.latest_version)): + self.config = config + + self.current_version = ELECTRUM_VERSION + + if(self.compare_versions(self.latest_version, self.current_version) == 1): + latest_seen = self.config.get("last_seen_version") + if(self.compare_versions(self.latest_version, latest_seen) == 1): + self.setText("New version available: " + self.latest_version) + + + def compare_versions(self, version1, version2): + def normalize(v): + return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")] + return cmp(normalize(version1), normalize(version2)) def ignore_this_version(self): self.setText("") - self.config.set_key("last_seen_version", self.latest_version) + self.config.set_key("last_seen_version", self.latest_version, True) QMessageBox.information(self, _("Preference saved"), _("Notifications about this update will not be shown again.")) self.dialog.done(0) def ignore_all_version(self): self.setText("") - self.config.set_key("last_seen_version", tuple(['9','9','9'])) + self.config.set_key("last_seen_version", "9.9.9", True) QMessageBox.information(self, _("Preference saved"), _("No more notifications about version updates will be shown.")) self.dialog.done(0) @@ -108,7 +113,7 @@ class UpdateLabel(QtGui.QLabel): dialog.setModal(1) main_layout = QGridLayout() - main_layout.addWidget(QLabel("A new version of Electrum is available: " + self.latest_version_text), 0,0,1,3) + main_layout.addWidget(QLabel("A new version of Electrum is available: " + self.latest_version), 0,0,1,3) ignore_version = QPushButton(_("Ignore this version")) ignore_version.clicked.connect(self.ignore_this_version) From c933e6c614ad11d3b6616587dd3325840b78fc9d Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 16 Jan 2013 13:30:53 +0100 Subject: [PATCH 118/124] use print_error; transalte New version string --- lib/gui_qt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index f5278190..3d91e437 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -69,7 +69,7 @@ class UpdateLabel(QtGui.QLabel): con.request("GET", "/version") res = con.getresponse() except socket.error as msg: - print "Could not retrieve version information" + print_error("Could not retrieve version information") return if res.status == 200: @@ -83,7 +83,7 @@ class UpdateLabel(QtGui.QLabel): if(self.compare_versions(self.latest_version, self.current_version) == 1): latest_seen = self.config.get("last_seen_version") if(self.compare_versions(self.latest_version, latest_seen) == 1): - self.setText("New version available: " + self.latest_version) + self.setText(_("New version available") + ": " + self.latest_version) def compare_versions(self, version1, version2): From e0bdf0e079ec732b0c3764c2d70159c00f47d5bd Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 16 Jan 2013 13:44:18 +0100 Subject: [PATCH 119/124] fix regexp and compare_versions --- lib/gui_qt.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 3d91e437..be19aab8 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -75,21 +75,30 @@ class UpdateLabel(QtGui.QLabel): if res.status == 200: self.latest_version = res.read() self.latest_version = self.latest_version.replace("\n","") - if(re.match('^\d\.\d.\d$', self.latest_version)): + if(re.match('^\d+(\.\d+)*$', self.latest_version)): self.config = config - self.current_version = ELECTRUM_VERSION - if(self.compare_versions(self.latest_version, self.current_version) == 1): - latest_seen = self.config.get("last_seen_version") + latest_seen = self.config.get("last_seen_version",ELECTRUM_VERSION) if(self.compare_versions(self.latest_version, latest_seen) == 1): self.setText(_("New version available") + ": " + self.latest_version) def compare_versions(self, version1, version2): - def normalize(v): - return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")] - return cmp(normalize(version1), normalize(version2)) + parts1 = [int(x) for x in version1.split('.')] + parts2 = [int(x) for x in version2.split('.')] + + # fill up the shorter version with zeros ... + lendiff = len(parts1) - len(parts2) + if lendiff > 0: + parts2.extend([0] * lendiff) + elif lendiff < 0: + parts1.extend([0] * (-lendiff)) + + for i, p in enumerate(parts1): + ret = cmp(p, parts2[i]) + if ret: return ret + return 0 def ignore_this_version(self): self.setText("") From 24da38415e79f79febc7568d085d51485c5471ee Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 16 Jan 2013 14:45:07 +0100 Subject: [PATCH 120/124] Rolled back original compare code --- lib/gui_qt.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index be19aab8..141ece11 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -85,20 +85,9 @@ class UpdateLabel(QtGui.QLabel): def compare_versions(self, version1, version2): - parts1 = [int(x) for x in version1.split('.')] - parts2 = [int(x) for x in version2.split('.')] - - # fill up the shorter version with zeros ... - lendiff = len(parts1) - len(parts2) - if lendiff > 0: - parts2.extend([0] * lendiff) - elif lendiff < 0: - parts1.extend([0] * (-lendiff)) - - for i, p in enumerate(parts1): - ret = cmp(p, parts2[i]) - if ret: return ret - return 0 + def normalize(v): + return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")] + return cmp(normalize(version1), normalize(version2)) def ignore_this_version(self): self.setText("") From 839824e94dba46fb5190ffd3023b684398b0e0dc Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 16 Jan 2013 17:24:34 +0100 Subject: [PATCH 121/124] Fix quit electrum menu item not appearing in OSX; #fixes 110 --- lib/gui_lite.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/gui_lite.py b/lib/gui_lite.py index 5ea8e62d..d1ec35fc 100644 --- a/lib/gui_lite.py +++ b/lib/gui_lite.py @@ -352,11 +352,10 @@ class MiniWindow(QDialog): # Creating the menu bar menubar = QMenuBar() - electrum_menu = menubar.addMenu(_("&Bitcoin")) + electrum_menu = menubar.addMenu(_("&Electrum")) - electrum_menu.addSeparator() + quit_option = electrum_menu.addAction(_("&Close")) - quit_option = electrum_menu.addAction(_("&Quit")) quit_option.triggered.connect(self.close) view_menu = menubar.addMenu(_("&View")) From 41b1eff1e246503b855531b9b9a9ea68d102e3aa Mon Sep 17 00:00:00 2001 From: rdymac Date: Thu, 17 Jan 2013 00:22:43 +0100 Subject: [PATCH 122/124] Changed Master Public Key to first capital letters Changed Master Public Key to first capital letters to maintain consistency with all MPK text strings. --- lib/gui_qt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 141ece11..668c1be1 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1894,9 +1894,9 @@ class ElectrumWindow(QMainWindow): grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2) grid_io.addWidget(HelpButton(_('Import private key')), 3, 3) - grid_io.addWidget(QLabel(_('Master Public key')), 4, 0) + grid_io.addWidget(QLabel(_('Master Public Key')), 4, 0) grid_io.addWidget(EnterButton(_("Show"), self.show_master_public_key), 4, 1) - grid_io.addWidget(HelpButton(_('Your master public key can be used to create receiving addresses, but not to sign transactions.') + ' ' \ + grid_io.addWidget(HelpButton(_('Your Master Public Key can be used to create receiving addresses, but not to sign transactions.') + ' ' \ + _('If you give it to someone, they will be able to see your transactions, but not to spend your money.') + ' ' \ + _('If you restore your wallet from it, a watching-only (deseeded) wallet will be created.')), 4, 3) From 8b955844fbf419598910a292361f9503e2d192cc Mon Sep 17 00:00:00 2001 From: rdymac Date: Thu, 17 Jan 2013 00:34:19 +0100 Subject: [PATCH 123/124] More text strings to be translated Added more text strings to be translated --- lib/gui_qt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui_qt.py b/lib/gui_qt.py index 141ece11..135d93be 100644 --- a/lib/gui_qt.py +++ b/lib/gui_qt.py @@ -1849,7 +1849,7 @@ class ElectrumWindow(QMainWindow): usechange_label = QLabel(_('Use change addresses')) grid_wallet.addWidget(usechange_label, 1, 0) usechange_combo = QComboBox() - usechange_combo.addItems(['Yes', 'No']) + usechange_combo.addItems([_('Yes'), _('No')]) usechange_combo.setCurrentIndex(not self.wallet.use_change) grid_wallet.addWidget(usechange_combo, 1, 1) grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions.')+' '), 1, 2) From 044e3b08a7793df102065451052ff1c2d546ebac Mon Sep 17 00:00:00 2001 From: rdymac Date: Thu, 17 Jan 2013 15:56:19 +0100 Subject: [PATCH 124/124] Added lib/gui_lite.py to be able to translate text Added lib/gui_lite.py to be able to translate text strings from the Lite mode. I don't know if the lib/gui_text.py need to be added too. Now text strings from the Lite mode can be translated. --- app.fil | 1 + 1 file changed, 1 insertion(+) diff --git a/app.fil b/app.fil index 5266edb7..600b603b 100644 --- a/app.fil +++ b/app.fil @@ -1,2 +1,3 @@ lib/gui_qt.py lib/gui.py +lib/gui_lite.py