2011-11-04 10:00:37 -07:00
#!/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 <http://www.gnu.org/licenses/>.
import datetime
2012-02-03 06:45:41 -08:00
import thread , time , ast , sys , re
2011-11-10 13:21:40 -08:00
import socket , traceback
2011-11-04 10:00:37 -07:00
import pygtk
pygtk . require ( ' 2.0 ' )
import gtk , gobject
2011-12-06 12:05:46 -08:00
from decimal import Decimal
2013-03-02 03:26:21 -08:00
from electrum . util import print_error
2013-09-11 03:05:28 -07:00
from electrum . bitcoin import is_valid
from electrum import mnemonic , pyqrnative , WalletStorage , Wallet
2012-05-30 07:46:04 -07:00
2011-11-04 10:00:37 -07:00
gtk . gdk . threads_init ( )
APP_NAME = " Electrum "
2012-03-31 07:46:32 -07:00
import platform
MONOSPACE_FONT = ' Lucida Console ' if platform . system ( ) == ' Windows ' else ' monospace '
2011-11-04 10:00:37 -07:00
2013-03-02 03:26:21 -08:00
from electrum . util import format_satoshis
2013-09-12 05:58:42 -07:00
from electrum . network import DEFAULT_SERVERS
2013-03-24 03:25:17 -07:00
from electrum . bitcoin import MIN_RELAY_TX_FEE
2011-11-04 10:00:37 -07:00
def numbify ( entry , is_int = False ) :
text = entry . get_text ( ) . strip ( )
2011-12-07 13:38:31 -08:00
chars = ' 0123456789 '
if not is_int : chars + = ' . '
s = ' ' . join ( [ i for i in text if i in chars ] )
2011-12-07 09:54:32 -08:00
if not is_int :
2011-12-07 13:38:31 -08:00
if ' . ' in s :
p = s . find ( ' . ' )
s = s . replace ( ' . ' , ' ' )
s = s [ : p ] + ' . ' + s [ p : p + 8 ]
2011-12-07 09:54:32 -08:00
try :
2011-12-07 10:24:04 -08:00
amount = int ( Decimal ( s ) * 100000000 )
2011-12-07 09:54:32 -08:00
except :
2011-12-07 13:11:35 -08:00
amount = None
2011-12-07 13:38:31 -08:00
else :
try :
amount = int ( s )
except :
amount = None
2011-12-07 10:24:04 -08:00
entry . set_text ( s )
2011-12-07 09:54:32 -08:00
return amount
2011-11-08 04:56:35 -08:00
2011-12-06 03:39:42 -08:00
def show_seed_dialog ( wallet , password , parent ) :
2012-05-12 15:43:22 -07:00
if not wallet . seed :
show_message ( " No seed " )
return
2011-11-08 04:56:35 -08:00
try :
2013-01-06 00:41:06 -08:00
seed = wallet . decode_seed ( password )
2011-11-08 04:56:35 -08:00
except :
show_message ( " Incorrect password " )
return
dialog = gtk . MessageDialog (
2011-12-06 03:39:42 -08:00
parent = parent ,
2011-11-08 04:56:35 -08:00
flags = gtk . DIALOG_MODAL ,
buttons = gtk . BUTTONS_OK ,
message_format = " Your wallet generation seed is: \n \n " + seed \
+ " \n \n Please keep it in a safe place; if you lose it, you will not be able to restore your wallet. \n \n " \
2011-12-02 07:44:17 -08:00
+ " Equivalently, your wallet seed can be stored and recovered with the following mnemonic code: \n \n \" " + ' ' . join ( mnemonic . mn_encode ( seed ) ) + " \" " )
2011-12-06 03:39:42 -08:00
dialog . set_title ( " Seed " )
2011-11-08 04:56:35 -08:00
dialog . show ( )
dialog . run ( )
dialog . destroy ( )
2013-09-28 04:45:49 -07:00
def restore_create_dialog ( ) :
2011-11-04 10:00:37 -07:00
2012-02-11 04:14:12 -08:00
# ask if the user wants to create a new wallet, or recover from a seed.
# if he wants to recover, and nothing is found, do not create wallet
dialog = gtk . Dialog ( " electrum " , parent = None ,
flags = gtk . DIALOG_MODAL | gtk . DIALOG_NO_SEPARATOR ,
buttons = ( " create " , 0 , " restore " , 1 , " cancel " , 2 ) )
label = gtk . Label ( " Wallet file not found. \n Do you want to create a new wallet, \n or to restore an existing one? " )
label . show ( )
dialog . vbox . pack_start ( label )
dialog . show ( )
r = dialog . run ( )
dialog . destroy ( )
2012-02-14 00:52:03 -08:00
if r == 2 : return False
2012-11-20 06:30:46 -08:00
return ' restore ' if r == 1 else ' create '
2011-11-06 02:13:58 -08:00
2012-02-14 00:52:03 -08:00
2011-11-04 10:00:37 -07:00
2013-09-28 04:45:49 -07:00
def run_recovery_dialog ( ) :
2012-01-12 05:48:41 -08:00
message = " Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet. "
dialog = gtk . MessageDialog (
parent = None ,
flags = gtk . DIALOG_MODAL ,
buttons = gtk . BUTTONS_OK_CANCEL ,
message_format = message )
vbox = dialog . vbox
dialog . set_default_response ( gtk . RESPONSE_OK )
# ask seed, server and gap in the same dialog
seed_box = gtk . HBox ( )
seed_label = gtk . Label ( ' Seed or mnemonic: ' )
seed_label . set_size_request ( 150 , - 1 )
seed_box . pack_start ( seed_label , False , False , 10 )
seed_label . show ( )
seed_entry = gtk . Entry ( )
seed_entry . show ( )
seed_entry . set_size_request ( 450 , - 1 )
seed_box . pack_start ( seed_entry , False , False , 10 )
add_help_button ( seed_box , ' . ' )
seed_box . show ( )
vbox . pack_start ( seed_box , False , False , 5 )
dialog . show ( )
r = dialog . run ( )
2012-01-14 16:39:16 -08:00
seed = seed_entry . get_text ( )
2012-01-12 05:48:41 -08:00
dialog . destroy ( )
if r == gtk . RESPONSE_CANCEL :
2012-11-20 06:30:46 -08:00
return False
2012-01-12 05:48:41 -08:00
try :
seed . decode ( ' hex ' )
except :
2012-07-07 09:24:52 -07:00
print_error ( " Warning: Not hex, trying decode " )
2012-01-12 05:48:41 -08:00
seed = mnemonic . mn_decode ( seed . split ( ' ' ) )
2012-01-12 06:42:13 -08:00
if not seed :
show_message ( " no seed " )
2012-11-20 06:30:46 -08:00
return False
2012-01-12 05:48:41 -08:00
2013-09-28 04:45:49 -07:00
return seed
2012-01-12 05:48:41 -08:00
def run_settings_dialog ( wallet , parent ) :
2012-02-14 00:52:03 -08:00
message = " Here are the settings of your wallet. For more explanations, click on the question mark buttons next to each input field. "
2011-12-06 03:39:42 -08:00
dialog = gtk . MessageDialog (
parent = parent ,
flags = gtk . DIALOG_MODAL ,
buttons = gtk . BUTTONS_OK_CANCEL ,
message_format = message )
2011-11-04 10:00:37 -07:00
2011-12-03 05:08:21 -08:00
image = gtk . Image ( )
image . set_from_stock ( gtk . STOCK_PREFERENCES , gtk . ICON_SIZE_DIALOG )
image . show ( )
dialog . set_image ( image )
2011-12-06 03:39:42 -08:00
dialog . set_title ( " Settings " )
2011-12-03 05:08:21 -08:00
2011-11-06 02:13:58 -08:00
vbox = dialog . vbox
2011-11-04 16:15:02 -07:00
dialog . set_default_response ( gtk . RESPONSE_OK )
2011-11-04 10:00:37 -07:00
2012-01-12 05:48:41 -08:00
fee = gtk . HBox ( )
fee_entry = gtk . Entry ( )
fee_label = gtk . Label ( ' Transaction fee: ' )
fee_label . set_size_request ( 150 , 10 )
fee_label . show ( )
fee . pack_start ( fee_label , False , False , 10 )
fee_entry . set_text ( str ( Decimal ( wallet . fee ) / 100000000 ) )
fee_entry . connect ( ' changed ' , numbify , False )
fee_entry . show ( )
fee . pack_start ( fee_entry , False , False , 10 )
2013-03-23 01:23:57 -07:00
add_help_button ( fee , ' Fee per kilobyte of transaction. Recommended value:0.0001 ' )
2012-01-12 05:48:41 -08:00
fee . show ( )
vbox . pack_start ( fee , False , False , 5 )
2011-11-06 02:13:58 -08:00
2012-05-02 08:40:39 -07:00
nz = gtk . HBox ( )
nz_entry = gtk . Entry ( )
nz_label = gtk . Label ( ' Display zeros: ' )
nz_label . set_size_request ( 150 , 10 )
nz_label . show ( )
nz . pack_start ( nz_label , False , False , 10 )
nz_entry . set_text ( str ( wallet . num_zeros ) )
nz_entry . connect ( ' changed ' , numbify , True )
nz_entry . show ( )
nz . pack_start ( nz_entry , False , False , 10 )
add_help_button ( nz , " Number of zeros displayed after the decimal point. \n For example, if this number is 2, then ' 5. ' is displayed as ' 5.00 ' " )
nz . show ( )
vbox . pack_start ( nz , False , False , 5 )
2012-10-12 08:04:16 -07:00
# gui setting
gui_box = gtk . HBox ( )
gui_label = gtk . Label ( ' Default GUI: ' )
gui_label . set_size_request ( 150 , 10 )
gui_label . show ( )
gui_box . pack_start ( gui_label , False , False , 10 )
gui_combo = gtk . combo_box_new_text ( )
2012-10-30 10:36:09 -07:00
gui_names = [ ' lite ' , ' classic ' , ' gtk ' , ' text ' ]
for name in gui_names : gui_combo . append_text ( name . capitalize ( ) )
2012-10-12 08:04:16 -07:00
gui_combo . show ( )
gui_box . pack_start ( gui_combo , False , False , 10 )
gui_combo . set_active ( gui_names . index ( wallet . config . get ( " gui " , " lite " ) ) )
gui_box . show ( )
add_help_button ( gui_box , " Select which GUI mode to use at start up. " )
vbox . pack_start ( gui_box , False , False , 5 )
2011-11-04 10:00:37 -07:00
dialog . show ( )
r = dialog . run ( )
2012-01-12 05:48:41 -08:00
fee = fee_entry . get_text ( )
2012-05-02 08:40:39 -07:00
nz = nz_entry . get_text ( )
2012-10-12 08:04:16 -07:00
gui = gui_names [ gui_combo . get_active ( ) ]
2011-12-06 03:39:42 -08:00
2011-11-04 10:00:37 -07:00
dialog . destroy ( )
2011-11-08 04:56:35 -08:00
if r == gtk . RESPONSE_CANCEL :
2012-01-12 05:48:41 -08:00
return
2011-11-08 04:56:35 -08:00
2011-12-02 07:40:48 -08:00
try :
2012-01-12 05:48:41 -08:00
fee = int ( 100000000 * Decimal ( fee ) )
2011-12-02 07:40:48 -08:00
except :
show_message ( " error " )
return
2013-05-02 01:00:52 -07:00
wallet . set_fee ( fee )
2011-12-02 07:40:48 -08:00
2012-05-02 08:40:39 -07:00
try :
nz = int ( nz )
if nz > 8 : nz = 8
except :
show_message ( " error " )
return
if wallet . num_zeros != nz :
wallet . num_zeros = nz
wallet . save ( )
2011-12-02 07:40:48 -08:00
2012-10-12 08:04:16 -07:00
wallet . config . set_key ( ' gui ' , gui , True )
2011-12-02 07:40:48 -08:00
2011-11-04 10:00:37 -07:00
2012-03-31 05:20:25 -07:00
2013-09-28 04:45:49 -07:00
def run_network_dialog ( network , parent ) :
2012-01-12 05:48:41 -08:00
image = gtk . Image ( )
image . set_from_stock ( gtk . STOCK_NETWORK , gtk . ICON_SIZE_DIALOG )
2013-09-28 04:45:49 -07:00
interface = network . interface
2012-01-12 06:42:13 -08:00
if parent :
2012-03-14 08:08:23 -07:00
if interface . is_connected :
2013-09-28 04:45:49 -07:00
status = " Connected to %s : %d \n %d blocks " % ( interface . host , interface . port , network . blockchain . height )
2012-01-12 06:42:13 -08:00
else :
status = " Not connected "
2012-01-12 05:48:41 -08:00
else :
2012-01-13 02:52:14 -08:00
import random
2012-11-20 06:30:46 -08:00
status = " Please choose a server. \n Select cancel if you are offline. "
2012-10-16 00:29:08 -07:00
server = interface . server
2013-09-28 04:45:49 -07:00
servers = network . get_servers ( )
2012-03-31 05:46:00 -07:00
2012-01-12 05:48:41 -08:00
dialog = gtk . MessageDialog ( parent , gtk . DIALOG_MODAL | gtk . DIALOG_DESTROY_WITH_PARENT ,
gtk . MESSAGE_QUESTION , gtk . BUTTONS_OK_CANCEL , status )
dialog . set_title ( " Server " )
dialog . set_image ( image )
image . show ( )
vbox = dialog . vbox
2012-01-13 02:52:14 -08:00
host_box = gtk . HBox ( )
2012-01-12 05:48:41 -08:00
host_label = gtk . Label ( ' Connect to: ' )
host_label . set_size_request ( 100 , - 1 )
host_label . show ( )
2012-01-13 02:52:14 -08:00
host_box . pack_start ( host_label , False , False , 10 )
2012-01-12 05:48:41 -08:00
host_entry = gtk . Entry ( )
host_entry . set_size_request ( 200 , - 1 )
2012-03-30 14:44:54 -07:00
host_entry . set_text ( server )
2012-01-12 05:48:41 -08:00
host_entry . show ( )
2012-01-13 02:52:14 -08:00
host_box . pack_start ( host_entry , False , False , 10 )
add_help_button ( host_box , ' The name and port number of your Electrum server, separated by a colon. Example: " ecdsa.org:50000 " . If no port number is provided, port 50000 will be tried. Some servers allow you to connect through http (port 80) or https (port 443) ' )
host_box . show ( )
2012-03-31 05:46:00 -07:00
p_box = gtk . HBox ( False , 10 )
p_box . show ( )
p_label = gtk . Label ( ' Protocol: ' )
p_label . set_size_request ( 100 , - 1 )
p_label . show ( )
p_box . pack_start ( p_label , False , False , 10 )
radio1 = gtk . RadioButton ( None , " tcp " )
p_box . pack_start ( radio1 , True , True , 0 )
radio1 . show ( )
radio2 = gtk . RadioButton ( radio1 , " http " )
p_box . pack_start ( radio2 , True , True , 0 )
radio2 . show ( )
def current_line ( ) :
return unicode ( host_entry . get_text ( ) ) . split ( ' : ' )
def set_button ( protocol ) :
if protocol == ' t ' :
radio1 . set_active ( 1 )
elif protocol == ' h ' :
radio2 . set_active ( 1 )
def set_protocol ( protocol ) :
host = current_line ( ) [ 0 ]
2013-04-28 04:47:19 -07:00
pp = servers [ host ]
2012-03-31 05:46:00 -07:00
if protocol not in pp . keys ( ) :
protocol = pp . keys ( ) [ 0 ]
set_button ( protocol )
port = pp [ protocol ]
host_entry . set_text ( host + ' : ' + port + ' : ' + protocol )
radio1 . connect ( " toggled " , lambda x , y : set_protocol ( ' t ' ) , " radio button 1 " )
radio2 . connect ( " toggled " , lambda x , y : set_protocol ( ' h ' ) , " radio button 1 " )
2012-01-12 05:48:41 -08:00
server_list = gtk . ListStore ( str )
2013-04-28 04:47:19 -07:00
for host in servers . keys ( ) :
2012-03-31 05:46:00 -07:00
server_list . append ( [ host ] )
2012-01-12 05:48:41 -08:00
treeview = gtk . TreeView ( model = server_list )
treeview . show ( )
2013-09-12 05:58:42 -07:00
if interface . servers :
2012-05-02 08:44:41 -07:00
label = ' Active Servers '
else :
label = ' Default Servers '
tvcolumn = gtk . TreeViewColumn ( label )
2012-01-12 05:48:41 -08:00
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
tvcolumn . pack_start ( cell , False )
tvcolumn . add_attribute ( cell , ' text ' , 0 )
scroll = gtk . ScrolledWindow ( )
scroll . set_policy ( gtk . POLICY_AUTOMATIC , gtk . POLICY_AUTOMATIC )
scroll . add ( treeview )
scroll . show ( )
2012-01-13 02:52:14 -08:00
vbox . pack_start ( host_box , False , False , 5 )
2012-03-31 05:46:00 -07:00
vbox . pack_start ( p_box , True , True , 0 )
2012-01-12 05:48:41 -08:00
vbox . pack_start ( scroll )
def my_treeview_cb ( treeview ) :
path , view_column = treeview . get_cursor ( )
2012-03-31 05:46:00 -07:00
host = server_list . get_value ( server_list . get_iter ( path ) , 0 )
2013-04-28 04:47:19 -07:00
pp = servers [ host ]
2012-03-31 05:46:00 -07:00
if ' t ' in pp . keys ( ) :
protocol = ' t '
else :
protocol = pp . keys ( ) [ 0 ]
port = pp [ protocol ]
host_entry . set_text ( host + ' : ' + port + ' : ' + protocol )
set_button ( protocol )
2012-01-12 05:48:41 -08:00
treeview . connect ( ' cursor-changed ' , my_treeview_cb )
dialog . show ( )
r = dialog . run ( )
2012-03-30 14:44:54 -07:00
server = host_entry . get_text ( )
2012-01-12 05:48:41 -08:00
dialog . destroy ( )
2012-01-12 06:42:13 -08:00
2012-01-12 05:48:41 -08:00
if r == gtk . RESPONSE_CANCEL :
2012-02-14 06:42:13 -08:00
return False
2012-01-12 06:42:13 -08:00
2012-01-12 05:48:41 -08:00
try :
2012-10-11 16:32:26 -07:00
interface . set_server ( server )
2012-01-12 05:48:41 -08:00
except :
2012-03-30 14:44:54 -07:00
show_message ( " error: " + server )
2012-02-14 06:42:13 -08:00
return False
2012-01-12 05:48:41 -08:00
2012-02-13 06:44:16 -08:00
if parent :
2012-10-11 16:32:26 -07:00
wallet . config . set_key ( " server " , server , True )
2012-02-14 06:42:13 -08:00
return True
2012-01-12 05:48:41 -08:00
2011-11-04 10:00:37 -07:00
2011-12-07 09:54:32 -08:00
def show_message ( message , parent = None ) :
2011-11-04 10:00:37 -07:00
dialog = gtk . MessageDialog (
2011-12-07 09:54:32 -08:00
parent = parent ,
2011-11-04 10:00:37 -07:00
flags = gtk . DIALOG_MODAL ,
buttons = gtk . BUTTONS_CLOSE ,
message_format = message )
dialog . show ( )
dialog . run ( )
dialog . destroy ( )
def password_line ( label ) :
password = gtk . HBox ( )
password_label = gtk . Label ( label )
password_label . set_size_request ( 120 , 10 )
password_label . show ( )
password . pack_start ( password_label , False , False , 10 )
password_entry = gtk . Entry ( )
2011-11-11 02:12:40 -08:00
password_entry . set_size_request ( 300 , - 1 )
2011-11-04 10:00:37 -07:00
password_entry . set_visibility ( False )
password_entry . show ( )
password . pack_start ( password_entry , False , False , 10 )
password . show ( )
return password , password_entry
2012-02-06 09:01:35 -08:00
def password_dialog ( parent ) :
dialog = gtk . MessageDialog ( parent , gtk . DIALOG_MODAL | gtk . DIALOG_DESTROY_WITH_PARENT ,
2011-11-08 04:56:35 -08:00
gtk . MESSAGE_QUESTION , gtk . BUTTONS_OK_CANCEL , " Please enter your password. " )
2011-11-04 10:00:37 -07:00
dialog . get_image ( ) . set_visible ( False )
current_pw , current_pw_entry = password_line ( ' Password: ' )
current_pw_entry . connect ( " activate " , lambda entry , dialog , response : dialog . response ( response ) , dialog , gtk . RESPONSE_OK )
dialog . vbox . pack_start ( current_pw , False , True , 0 )
dialog . show ( )
result = dialog . run ( )
pw = current_pw_entry . get_text ( )
dialog . destroy ( )
2011-12-08 16:54:49 -08:00
if result != gtk . RESPONSE_CANCEL : return pw
2011-11-04 10:00:37 -07:00
2011-12-06 03:39:42 -08:00
def change_password_dialog ( wallet , parent , icon ) :
2012-05-12 15:43:22 -07:00
if not wallet . seed :
show_message ( " No seed " )
return
2011-12-06 03:39:42 -08:00
if parent :
2011-11-08 04:56:35 -08:00
msg = ' Your wallet is encrypted. Use this dialog to change the password. To disable wallet encryption, enter an empty new password. ' if wallet . use_encryption else ' Your wallet keys are not encrypted '
2011-11-06 02:13:58 -08:00
else :
msg = " Please choose a password to encrypt your wallet keys "
2011-12-06 03:39:42 -08:00
dialog = gtk . MessageDialog ( parent , gtk . DIALOG_MODAL | gtk . DIALOG_DESTROY_WITH_PARENT , gtk . MESSAGE_QUESTION , gtk . BUTTONS_OK_CANCEL , msg )
2011-12-03 02:31:00 -08:00
dialog . set_title ( " Change password " )
2011-12-03 05:08:21 -08:00
image = gtk . Image ( )
image . set_from_stock ( gtk . STOCK_DIALOG_AUTHENTICATION , gtk . ICON_SIZE_DIALOG )
image . show ( )
dialog . set_image ( image )
2011-11-08 04:56:35 -08:00
2011-11-04 10:00:37 -07:00
if wallet . use_encryption :
2011-11-04 16:15:02 -07:00
current_pw , current_pw_entry = password_line ( ' Current password: ' )
2011-11-04 10:00:37 -07:00
dialog . vbox . pack_start ( current_pw , False , True , 0 )
password , password_entry = password_line ( ' New password: ' )
dialog . vbox . pack_start ( password , False , True , 5 )
password2 , password2_entry = password_line ( ' Confirm password: ' )
dialog . vbox . pack_start ( password2 , False , True , 5 )
dialog . show ( )
result = dialog . run ( )
password = current_pw_entry . get_text ( ) if wallet . use_encryption else None
new_password = password_entry . get_text ( )
new_password2 = password2_entry . get_text ( )
dialog . destroy ( )
2011-11-04 16:15:02 -07:00
if result == gtk . RESPONSE_CANCEL :
2011-11-04 10:00:37 -07:00
return
try :
2013-01-06 00:41:06 -08:00
seed = wallet . decode_seed ( password )
2011-11-04 10:00:37 -07:00
except :
2011-11-04 16:15:02 -07:00
show_message ( " Incorrect password " )
2011-11-04 10:00:37 -07:00
return
if new_password != new_password2 :
show_message ( " passwords do not match " )
return
2012-05-16 23:32:49 -07:00
wallet . update_password ( seed , password , new_password )
2011-11-04 16:15:02 -07:00
if icon :
if wallet . use_encryption :
icon . set_tooltip_text ( ' wallet is encrypted ' )
else :
icon . set_tooltip_text ( ' wallet is unencrypted ' )
2011-11-04 10:00:37 -07:00
def add_help_button ( hbox , message ) :
button = gtk . Button ( ' ? ' )
button . connect ( " clicked " , lambda x : show_message ( message ) )
button . show ( )
hbox . pack_start ( button , False , False )
class MyWindow ( gtk . Window ) : __gsignals__ = dict ( mykeypress = ( gobject . SIGNAL_RUN_LAST | gobject . SIGNAL_ACTION , None , ( str , ) ) )
gobject . type_register ( MyWindow )
gtk . binding_entry_add_signal ( MyWindow , gtk . keysyms . W , gtk . gdk . CONTROL_MASK , ' mykeypress ' , str , ' ctrl+W ' )
gtk . binding_entry_add_signal ( MyWindow , gtk . keysyms . Q , gtk . gdk . CONTROL_MASK , ' mykeypress ' , str , ' ctrl+Q ' )
2012-02-14 00:52:03 -08:00
class ElectrumWindow :
2011-11-04 10:00:37 -07:00
2011-12-07 09:54:32 -08:00
def show_message ( self , msg ) :
show_message ( msg , self . window )
2013-09-28 04:45:49 -07:00
def __init__ ( self , wallet , config , network ) :
2012-10-11 11:10:12 -07:00
self . config = config
2011-11-04 10:00:37 -07:00
self . wallet = wallet
2013-09-28 04:45:49 -07:00
self . network = network
2012-02-08 04:37:14 -08:00
self . funds_error = False # True if not enough funds
2013-09-11 03:05:28 -07:00
self . num_zeros = int ( self . config . get ( ' num_zeros ' , 0 ) )
2011-11-04 10:00:37 -07:00
self . window = MyWindow ( gtk . WINDOW_TOPLEVEL )
2012-10-11 11:10:12 -07:00
title = ' Electrum ' + self . wallet . electrum_version + ' - ' + self . config . path
2012-05-13 01:54:03 -07:00
if not self . wallet . seed : title + = ' [seedless] '
self . window . set_title ( title )
2011-11-04 10:00:37 -07:00
self . window . connect ( " destroy " , gtk . main_quit )
self . window . set_border_width ( 0 )
self . window . connect ( ' mykeypress ' , gtk . main_quit )
2011-12-07 09:54:32 -08:00
self . window . set_default_size ( 720 , 350 )
2012-11-20 06:30:46 -08:00
self . wallet_updated = False
2011-11-04 10:00:37 -07:00
vbox = gtk . VBox ( )
self . notebook = gtk . Notebook ( )
self . create_history_tab ( )
2012-05-13 02:05:26 -07:00
if self . wallet . seed :
self . create_send_tab ( )
2011-11-04 10:00:37 -07:00
self . create_recv_tab ( )
self . create_book_tab ( )
self . create_about_tab ( )
self . notebook . show ( )
vbox . pack_start ( self . notebook , True , True , 2 )
self . status_bar = gtk . Statusbar ( )
vbox . pack_start ( self . status_bar , False , False , 0 )
self . status_image = gtk . Image ( )
2011-12-03 11:18:34 -08:00
self . status_image . set_from_stock ( gtk . STOCK_NO , gtk . ICON_SIZE_MENU )
2011-11-04 10:00:37 -07:00
self . status_image . set_alignment ( True , 0.5 )
self . status_image . show ( )
2011-12-02 07:40:48 -08:00
2011-12-02 08:22:49 -08:00
self . network_button = gtk . Button ( )
2013-09-28 04:45:49 -07:00
self . network_button . connect ( " clicked " , lambda x : run_network_dialog ( self . network , self . window ) )
2011-12-02 08:22:49 -08:00
self . network_button . add ( self . status_image )
self . network_button . set_relief ( gtk . RELIEF_NONE )
self . network_button . show ( )
self . status_bar . pack_end ( self . network_button , False , False )
2011-11-08 04:56:35 -08:00
2012-05-13 01:54:03 -07:00
if self . wallet . seed :
def seedb ( w , wallet ) :
if wallet . use_encryption :
password = password_dialog ( self . window )
if not password : return
else : password = None
show_seed_dialog ( wallet , password , self . window )
button = gtk . Button ( ' S ' )
button . connect ( " clicked " , seedb , wallet )
button . set_relief ( gtk . RELIEF_NONE )
button . show ( )
self . status_bar . pack_end ( button , False , False )
2011-11-08 04:56:35 -08:00
2011-11-04 10:00:37 -07:00
settings_icon = gtk . Image ( )
settings_icon . set_from_stock ( gtk . STOCK_PREFERENCES , gtk . ICON_SIZE_MENU )
2011-11-04 16:15:02 -07:00
settings_icon . set_alignment ( 0.5 , 0.5 )
settings_icon . set_size_request ( 16 , 16 )
2011-11-04 10:00:37 -07:00
settings_icon . show ( )
prefs_button = gtk . Button ( )
2012-01-12 05:48:41 -08:00
prefs_button . connect ( " clicked " , lambda x : run_settings_dialog ( self . wallet , self . window ) )
2011-11-04 10:00:37 -07:00
prefs_button . add ( settings_icon )
prefs_button . set_tooltip_text ( " Settings " )
2011-11-04 16:15:02 -07:00
prefs_button . set_relief ( gtk . RELIEF_NONE )
2011-11-04 10:00:37 -07:00
prefs_button . show ( )
self . status_bar . pack_end ( prefs_button , False , False )
2011-11-04 16:15:02 -07:00
pw_icon = gtk . Image ( )
pw_icon . set_from_stock ( gtk . STOCK_DIALOG_AUTHENTICATION , gtk . ICON_SIZE_MENU )
pw_icon . set_alignment ( 0.5 , 0.5 )
pw_icon . set_size_request ( 16 , 16 )
pw_icon . show ( )
2012-05-13 01:54:03 -07:00
if self . wallet . seed :
password_button = gtk . Button ( )
password_button . connect ( " clicked " , lambda x : change_password_dialog ( self . wallet , self . window , pw_icon ) )
password_button . add ( pw_icon )
password_button . set_relief ( gtk . RELIEF_NONE )
password_button . show ( )
self . status_bar . pack_end ( password_button , False , False )
2011-11-04 16:15:02 -07:00
2011-11-04 10:00:37 -07:00
self . window . add ( vbox )
self . window . show_all ( )
2012-02-03 02:48:09 -08:00
#self.fee_box.hide()
2011-11-04 10:00:37 -07:00
self . context_id = self . status_bar . get_context_id ( " statusbar " )
self . update_status_bar ( )
2013-09-28 04:45:49 -07:00
self . network . register_callback ( ' updated ' , self . update_callback )
2012-10-27 10:20:50 -07:00
2011-11-04 10:00:37 -07:00
def update_status_bar_thread ( ) :
while True :
gobject . idle_add ( self . update_status_bar )
time . sleep ( 0.5 )
2012-02-03 06:45:41 -08:00
def check_recipient_thread ( ) :
old_r = ' '
while True :
time . sleep ( 0.5 )
2012-02-03 06:57:45 -08:00
if self . payto_entry . is_focus ( ) :
continue
2012-02-03 06:45:41 -08:00
r = self . payto_entry . get_text ( )
if r != old_r :
old_r = r
2012-02-03 07:08:28 -08:00
r = r . strip ( )
2012-02-03 06:45:41 -08:00
if re . match ( ' ^(|([ \ w \ - \ .]+)@)(( \ w[ \ w \ -]+ \ .)+[ \ w \ -]+)$ ' , r ) :
2012-02-03 07:39:27 -08:00
try :
2012-02-14 00:52:03 -08:00
to_address = self . wallet . get_alias ( r , interactive = False )
2012-02-03 07:39:27 -08:00
except :
continue
2012-02-03 06:45:41 -08:00
if to_address :
2012-02-03 07:08:28 -08:00
s = r + ' < ' + to_address + ' > '
2012-02-03 06:45:41 -08:00
gobject . idle_add ( lambda : self . payto_entry . set_text ( s ) )
2011-11-04 10:00:37 -07:00
thread . start_new_thread ( update_status_bar_thread , ( ) )
2012-05-13 02:05:26 -07:00
if self . wallet . seed :
thread . start_new_thread ( check_recipient_thread , ( ) )
2011-11-04 10:00:37 -07:00
self . notebook . set_current_page ( 0 )
2012-10-27 10:20:50 -07:00
def update_callback ( self ) :
self . wallet_updated = True
2011-11-04 10:00:37 -07:00
def add_tab ( self , page , name ) :
tab_label = gtk . Label ( name )
tab_label . show ( )
self . notebook . append_page ( page , tab_label )
def create_send_tab ( self ) :
2012-02-03 02:48:09 -08:00
2011-11-04 10:00:37 -07:00
page = vbox = gtk . VBox ( )
page . show ( )
payto = gtk . HBox ( )
payto_label = gtk . Label ( ' Pay to: ' )
2012-02-03 02:48:09 -08:00
payto_label . set_size_request ( 100 , - 1 )
2011-11-04 10:00:37 -07:00
payto . pack_start ( payto_label , False )
payto_entry = gtk . Entry ( )
2012-02-03 06:45:41 -08:00
payto_entry . set_size_request ( 450 , 26 )
2011-11-04 10:00:37 -07:00
payto . pack_start ( payto_entry , False )
vbox . pack_start ( payto , False , False , 5 )
2012-02-03 02:48:09 -08:00
2012-02-03 13:25:50 -08:00
message = gtk . HBox ( )
2012-02-04 04:29:46 -08:00
message_label = gtk . Label ( ' Description: ' )
2012-02-03 13:25:50 -08:00
message_label . set_size_request ( 100 , - 1 )
message . pack_start ( message_label , False )
message_entry = gtk . Entry ( )
message_entry . set_size_request ( 450 , 26 )
message . pack_start ( message_entry , False )
vbox . pack_start ( message , False , False , 5 )
2011-11-04 10:00:37 -07:00
2011-12-07 09:54:32 -08:00
amount_box = gtk . HBox ( )
2011-11-04 10:00:37 -07:00
amount_label = gtk . Label ( ' Amount: ' )
2011-12-07 09:54:32 -08:00
amount_label . set_size_request ( 100 , - 1 )
amount_box . pack_start ( amount_label , False )
2011-11-04 10:00:37 -07:00
amount_entry = gtk . Entry ( )
2011-12-07 09:54:32 -08:00
amount_entry . set_size_request ( 120 , - 1 )
amount_box . pack_start ( amount_entry , False )
vbox . pack_start ( amount_box , False , False , 5 )
2011-11-04 10:00:37 -07:00
2011-12-07 09:54:32 -08:00
self . fee_box = fee_box = gtk . HBox ( )
fee_label = gtk . Label ( ' Fee: ' )
2012-02-03 02:48:09 -08:00
fee_label . set_size_request ( 100 , - 1 )
fee_box . pack_start ( fee_label , False )
2011-12-07 09:54:32 -08:00
fee_entry = gtk . Entry ( )
2012-02-02 09:56:57 -08:00
fee_entry . set_size_request ( 60 , 26 )
2012-02-03 02:48:09 -08:00
fee_box . pack_start ( fee_entry , False )
2012-02-03 13:25:50 -08:00
vbox . pack_start ( fee_box , False , False , 5 )
2012-02-02 09:44:50 -08:00
end_box = gtk . HBox ( )
empty_label = gtk . Label ( ' ' )
2012-02-03 02:48:09 -08:00
empty_label . set_size_request ( 100 , - 1 )
2012-02-02 09:44:50 -08:00
end_box . pack_start ( empty_label , False )
send_button = gtk . Button ( " Send " )
send_button . show ( )
2012-02-03 13:25:50 -08:00
end_box . pack_start ( send_button , False , False , 0 )
2012-02-02 09:44:50 -08:00
clear_button = gtk . Button ( " Clear " )
clear_button . show ( )
2012-02-03 13:25:50 -08:00
end_box . pack_start ( clear_button , False , False , 15 )
send_button . connect ( " clicked " , self . do_send , ( payto_entry , message_entry , amount_entry , fee_entry ) )
clear_button . connect ( " clicked " , self . do_clear , ( payto_entry , message_entry , amount_entry , fee_entry ) )
2012-02-03 02:48:09 -08:00
2012-02-02 09:44:50 -08:00
vbox . pack_start ( end_box , False , False , 5 )
2011-12-07 09:54:32 -08:00
2012-02-03 02:48:09 -08:00
# display this line only if there is a signature
payto_sig = gtk . HBox ( )
payto_sig_id = gtk . Label ( ' ' )
payto_sig . pack_start ( payto_sig_id , False )
vbox . pack_start ( payto_sig , True , True , 5 )
2011-12-07 09:54:32 -08:00
self . user_fee = False
def entry_changed ( entry , is_fee ) :
2012-02-08 04:37:14 -08:00
self . funds_error = False
2011-12-07 09:54:32 -08:00
amount = numbify ( amount_entry )
fee = numbify ( fee_entry )
if not is_fee : fee = None
2012-02-08 04:37:14 -08:00
if amount is None :
2012-02-03 02:48:09 -08:00
return
2011-12-07 09:54:32 -08:00
inputs , total , fee = self . wallet . choose_tx_inputs ( amount , fee )
if not is_fee :
fee_entry . set_text ( str ( Decimal ( fee ) / 100000000 ) )
self . fee_box . show ( )
if inputs :
amount_entry . modify_text ( gtk . STATE_NORMAL , gtk . gdk . color_parse ( " #000000 " ) )
fee_entry . modify_text ( gtk . STATE_NORMAL , gtk . gdk . color_parse ( " #000000 " ) )
send_button . set_sensitive ( True )
else :
send_button . set_sensitive ( False )
amount_entry . modify_text ( gtk . STATE_NORMAL , gtk . gdk . color_parse ( " #cc0000 " ) )
fee_entry . modify_text ( gtk . STATE_NORMAL , gtk . gdk . color_parse ( " #cc0000 " ) )
2012-02-08 04:37:14 -08:00
self . funds_error = True
2011-12-07 09:54:32 -08:00
amount_entry . connect ( ' changed ' , entry_changed , False )
2012-02-03 06:45:41 -08:00
fee_entry . connect ( ' changed ' , entry_changed , True )
2011-11-04 10:00:37 -07:00
self . payto_entry = payto_entry
2012-02-02 09:44:50 -08:00
self . payto_fee_entry = fee_entry
2012-02-03 02:48:09 -08:00
self . payto_sig_id = payto_sig_id
self . payto_sig = payto_sig
2012-02-03 13:25:50 -08:00
self . amount_entry = amount_entry
self . message_entry = message_entry
2011-11-04 10:00:37 -07:00
self . add_tab ( page , ' Send ' )
2012-02-03 02:48:09 -08:00
def set_frozen ( self , entry , frozen ) :
if frozen :
entry . set_editable ( False )
entry . set_has_frame ( False )
entry . modify_base ( gtk . STATE_NORMAL , gtk . gdk . color_parse ( " #eeeeee " ) )
else :
entry . set_editable ( True )
entry . set_has_frame ( True )
entry . modify_base ( gtk . STATE_NORMAL , gtk . gdk . color_parse ( " #ffffff " ) )
2012-02-14 03:45:39 -08:00
def set_url ( self , url ) :
2012-02-14 04:49:05 -08:00
payto , amount , label , message , signature , identity , url = self . wallet . parse_url ( url , self . show_message , self . question )
2012-02-07 02:14:48 -08:00
self . notebook . set_current_page ( 1 )
2012-02-03 06:45:41 -08:00
self . payto_entry . set_text ( payto )
2012-02-03 13:25:50 -08:00
self . message_entry . set_text ( message )
self . amount_entry . set_text ( amount )
2012-02-03 02:48:09 -08:00
if identity :
self . set_frozen ( self . payto_entry , True )
2012-02-03 13:25:50 -08:00
self . set_frozen ( self . amount_entry , True )
self . set_frozen ( self . message_entry , True )
2012-02-03 02:51:57 -08:00
self . payto_sig_id . set_text ( ' The bitcoin URI was signed by ' + identity )
2012-02-03 02:48:09 -08:00
else :
self . payto_sig . set_visible ( False )
2012-01-15 09:45:30 -08:00
2011-11-04 10:00:37 -07:00
def create_about_tab ( self ) :
2012-02-14 11:16:09 -08:00
import pango
2011-11-04 10:00:37 -07:00
page = gtk . VBox ( )
page . show ( )
2011-12-06 03:39:42 -08:00
tv = gtk . TextView ( )
tv . set_editable ( False )
tv . set_cursor_visible ( False )
2012-03-30 14:44:54 -07:00
tv . modify_font ( pango . FontDescription ( MONOSPACE_FONT ) )
2012-11-16 10:18:14 -08:00
scroll = gtk . ScrolledWindow ( )
scroll . add ( tv )
page . pack_start ( scroll )
2011-12-06 03:39:42 -08:00
self . info = tv . get_buffer ( )
2011-11-13 15:25:00 -08:00
self . add_tab ( page , ' Wall ' )
2011-11-04 10:00:37 -07:00
2012-02-02 09:44:50 -08:00
def do_clear ( self , w , data ) :
2012-02-03 02:48:09 -08:00
self . payto_sig . set_visible ( False )
2012-02-07 02:14:48 -08:00
self . payto_fee_entry . set_text ( ' ' )
2012-02-03 13:25:50 -08:00
for entry in [ self . payto_entry , self . amount_entry , self . message_entry ] :
2012-02-03 02:48:09 -08:00
self . set_frozen ( entry , False )
entry . set_text ( ' ' )
2012-02-03 06:45:41 -08:00
2012-02-06 08:59:31 -08:00
def question ( self , msg ) :
dialog = gtk . MessageDialog ( self . window , gtk . DIALOG_MODAL | gtk . DIALOG_DESTROY_WITH_PARENT , gtk . MESSAGE_QUESTION , gtk . BUTTONS_OK_CANCEL , msg )
dialog . show ( )
result = dialog . run ( )
dialog . destroy ( )
return result == gtk . RESPONSE_OK
2011-11-04 10:00:37 -07:00
def do_send ( self , w , data ) :
2011-12-07 09:54:32 -08:00
payto_entry , label_entry , amount_entry , fee_entry = data
2011-11-04 10:00:37 -07:00
label = label_entry . get_text ( )
2012-02-03 06:45:41 -08:00
r = payto_entry . get_text ( )
2012-02-03 07:39:27 -08:00
r = r . strip ( )
2012-02-03 08:17:33 -08:00
m1 = re . match ( ' ^(|([ \ w \ - \ .]+)@)(( \ w[ \ w \ -]+ \ .)+[ \ w \ -]+)$ ' , r )
m2 = re . match ( ' (|([ \ w \ - \ .]+)@)(( \ w[ \ w \ -]+ \ .)+[ \ w \ -]+) \ <([1-9A-HJ-NP-Za-km-z] { 26,}) \ > ' , r )
if m1 :
2012-02-14 00:52:03 -08:00
to_address = self . wallet . get_alias ( r , True , self . show_message , self . question )
2012-02-03 08:17:33 -08:00
if not to_address :
return
2012-02-14 00:52:03 -08:00
else :
self . update_sending_tab ( )
2012-02-03 08:17:33 -08:00
elif m2 :
to_address = m2 . group ( 5 )
2012-02-03 06:45:41 -08:00
else :
to_address = r
2012-02-03 08:17:33 -08:00
2013-03-01 05:27:56 -08:00
if not is_valid ( to_address ) :
2012-02-03 06:45:41 -08:00
self . show_message ( " invalid bitcoin address: \n " + to_address )
2011-11-04 10:00:37 -07:00
return
try :
2011-12-06 12:05:46 -08:00
amount = int ( Decimal ( amount_entry . get_text ( ) ) * 100000000 )
2011-11-04 10:00:37 -07:00
except :
2011-12-07 09:54:32 -08:00
self . show_message ( " invalid amount " )
return
try :
fee = int ( Decimal ( fee_entry . get_text ( ) ) * 100000000 )
except :
self . show_message ( " invalid fee " )
2011-11-04 10:00:37 -07:00
return
2012-01-17 02:15:17 -08:00
if self . wallet . use_encryption :
2012-02-06 09:01:35 -08:00
password = password_dialog ( self . window )
2012-01-17 02:15:17 -08:00
if not password :
return
else :
password = None
2011-11-04 10:00:37 -07:00
2011-12-16 06:40:05 -08:00
try :
2013-02-27 03:47:11 -08:00
tx = self . wallet . mktx ( [ ( to_address , amount ) ] , password , fee )
2011-12-16 06:40:05 -08:00
except BaseException , e :
2012-05-16 10:20:21 -07:00
self . show_message ( str ( e ) )
2011-11-16 06:16:36 -08:00
return
2013-03-23 01:23:57 -07:00
2013-03-24 03:25:17 -07:00
if tx . requires_fee ( self . wallet . verifier ) and fee < MIN_RELAY_TX_FEE :
self . show_message ( " This transaction requires a higher fee, or it will not be propagated by the network. " )
2013-03-23 01:23:57 -07:00
return
2011-12-16 06:40:05 -08:00
2013-02-27 03:51:49 -08:00
if label :
self . wallet . labels [ tx . hash ( ) ] = label
2011-11-16 06:16:36 -08:00
status , msg = self . wallet . sendtx ( tx )
2011-11-04 10:00:37 -07:00
if status :
2011-12-07 09:54:32 -08:00
self . show_message ( " payment sent. \n " + msg )
2011-11-04 10:00:37 -07:00
payto_entry . set_text ( " " )
label_entry . set_text ( " " )
amount_entry . set_text ( " " )
2011-12-07 09:54:32 -08:00
fee_entry . set_text ( " " )
2012-02-03 02:48:09 -08:00
#self.fee_box.hide()
2011-11-05 00:32:58 -07:00
self . update_sending_tab ( )
2011-11-04 10:00:37 -07:00
else :
2011-12-07 09:54:32 -08:00
self . show_message ( msg )
2011-11-04 10:00:37 -07:00
2011-12-12 22:33:23 -08:00
def treeview_button_press ( self , treeview , event ) :
if event . type == gtk . gdk . _2BUTTON_PRESS :
c = treeview . get_cursor ( ) [ 0 ]
2012-02-07 06:00:12 -08:00
if treeview == self . history_treeview :
tx_details = self . history_list . get_value ( self . history_list . get_iter ( c ) , 8 )
self . show_message ( tx_details )
elif treeview == self . contacts_treeview :
m = self . addressbook_list . get_value ( self . addressbook_list . get_iter ( c ) , 0 )
2013-03-14 08:32:05 -07:00
#a = self.wallet.aliases.get(m)
#if a:
# if a[0] in self.wallet.authorities.keys():
# s = self.wallet.authorities.get(a[0])
# else:
# s = "self-signed"
# msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
# self.show_message(msg)
2012-02-07 06:00:12 -08:00
2011-12-12 22:33:23 -08:00
2011-11-04 10:00:37 -07:00
def treeview_key_press ( self , treeview , event ) :
c = treeview . get_cursor ( ) [ 0 ]
if event . keyval == gtk . keysyms . Up :
if c and c [ 0 ] == 0 :
treeview . parent . grab_focus ( )
treeview . set_cursor ( ( 0 , ) )
2012-02-07 06:00:12 -08:00
elif event . keyval == gtk . keysyms . Return :
if treeview == self . history_treeview :
tx_details = self . history_list . get_value ( self . history_list . get_iter ( c ) , 8 )
self . show_message ( tx_details )
elif treeview == self . contacts_treeview :
m = self . addressbook_list . get_value ( self . addressbook_list . get_iter ( c ) , 0 )
2013-03-14 08:32:05 -07:00
#a = self.wallet.aliases.get(m)
#if a:
# if a[0] in self.wallet.authorities.keys():
# s = self.wallet.authorities.get(a[0])
# else:
# s = "self"
# msg = 'Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + s + '\nSigning address:' + a[0]
# self.show_message(msg)
2012-02-07 06:00:12 -08:00
2011-11-04 10:00:37 -07:00
return False
def create_history_tab ( self ) :
2011-11-22 09:54:10 -08:00
self . history_list = gtk . ListStore ( str , str , str , str , ' gboolean ' , str , str , str , str )
2011-11-04 10:00:37 -07:00
treeview = gtk . TreeView ( model = self . history_list )
self . history_treeview = treeview
treeview . set_tooltip_column ( 7 )
treeview . show ( )
treeview . connect ( ' key-press-event ' , self . treeview_key_press )
2011-12-12 22:33:23 -08:00
treeview . connect ( ' button-press-event ' , self . treeview_button_press )
2011-11-04 10:00:37 -07:00
tvcolumn = gtk . TreeViewColumn ( ' ' )
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererPixbuf ( )
tvcolumn . pack_start ( cell , False )
tvcolumn . set_attributes ( cell , stock_id = 1 )
tvcolumn = gtk . TreeViewColumn ( ' Date ' )
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
tvcolumn . pack_start ( cell , False )
tvcolumn . add_attribute ( cell , ' text ' , 2 )
2012-02-04 04:29:46 -08:00
tvcolumn = gtk . TreeViewColumn ( ' Description ' )
2011-11-04 10:00:37 -07:00
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
cell . set_property ( ' foreground ' , ' grey ' )
2012-03-30 14:44:54 -07:00
cell . set_property ( ' family ' , MONOSPACE_FONT )
2011-11-04 10:00:37 -07:00
cell . set_property ( ' editable ' , True )
def edited_cb ( cell , path , new_text , h_list ) :
tx = h_list . get_value ( h_list . get_iter ( path ) , 0 )
self . wallet . labels [ tx ] = new_text
self . wallet . save ( )
self . update_history_tab ( )
cell . connect ( ' edited ' , edited_cb , self . history_list )
def editing_started ( cell , entry , path , h_list ) :
tx = h_list . get_value ( h_list . get_iter ( path ) , 0 )
if not self . wallet . labels . get ( tx ) : entry . set_text ( ' ' )
cell . connect ( ' editing-started ' , editing_started , self . history_list )
tvcolumn . set_expand ( True )
tvcolumn . pack_start ( cell , True )
tvcolumn . set_attributes ( cell , text = 3 , foreground_set = 4 )
tvcolumn = gtk . TreeViewColumn ( ' Amount ' )
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
cell . set_alignment ( 1 , 0.5 )
2012-03-30 14:44:54 -07:00
cell . set_property ( ' family ' , MONOSPACE_FONT )
2011-11-04 10:00:37 -07:00
tvcolumn . pack_start ( cell , False )
tvcolumn . add_attribute ( cell , ' text ' , 5 )
tvcolumn = gtk . TreeViewColumn ( ' Balance ' )
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
cell . set_alignment ( 1 , 0.5 )
2012-03-30 14:44:54 -07:00
cell . set_property ( ' family ' , MONOSPACE_FONT )
2011-11-04 10:00:37 -07:00
tvcolumn . pack_start ( cell , False )
tvcolumn . add_attribute ( cell , ' text ' , 6 )
tvcolumn = gtk . TreeViewColumn ( ' Tooltip ' )
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
tvcolumn . pack_start ( cell , False )
tvcolumn . add_attribute ( cell , ' text ' , 7 )
tvcolumn . set_visible ( False )
scroll = gtk . ScrolledWindow ( )
2011-12-13 00:40:25 -08:00
scroll . set_policy ( gtk . POLICY_NEVER , gtk . POLICY_AUTOMATIC )
2011-11-04 10:00:37 -07:00
scroll . add ( treeview )
self . add_tab ( scroll , ' History ' )
self . update_history_tab ( )
def create_recv_tab ( self ) :
self . recv_list = gtk . ListStore ( str , str , str )
self . add_tab ( self . make_address_list ( True ) , ' Receive ' )
self . update_receiving_tab ( )
def create_book_tab ( self ) :
self . addressbook_list = gtk . ListStore ( str , str , str )
self . add_tab ( self . make_address_list ( False ) , ' Contacts ' )
self . update_sending_tab ( )
def make_address_list ( self , is_recv ) :
liststore = self . recv_list if is_recv else self . addressbook_list
treeview = gtk . TreeView ( model = liststore )
treeview . connect ( ' key-press-event ' , self . treeview_key_press )
2012-02-07 06:00:12 -08:00
treeview . connect ( ' button-press-event ' , self . treeview_button_press )
2011-11-04 10:00:37 -07:00
treeview . show ( )
2012-02-07 06:00:12 -08:00
if not is_recv :
self . contacts_treeview = treeview
2011-11-04 10:00:37 -07:00
tvcolumn = gtk . TreeViewColumn ( ' Address ' )
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
2012-03-30 14:44:54 -07:00
cell . set_property ( ' family ' , MONOSPACE_FONT )
2011-11-04 10:00:37 -07:00
tvcolumn . pack_start ( cell , True )
tvcolumn . add_attribute ( cell , ' text ' , 0 )
tvcolumn = gtk . TreeViewColumn ( ' Label ' )
tvcolumn . set_expand ( True )
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
cell . set_property ( ' editable ' , True )
def edited_cb2 ( cell , path , new_text , liststore ) :
address = liststore . get_value ( liststore . get_iter ( path ) , 0 )
self . wallet . labels [ address ] = new_text
self . wallet . save ( )
self . update_receiving_tab ( )
self . update_sending_tab ( )
self . update_history_tab ( )
cell . connect ( ' edited ' , edited_cb2 , liststore )
tvcolumn . pack_start ( cell , True )
tvcolumn . add_attribute ( cell , ' text ' , 1 )
tvcolumn = gtk . TreeViewColumn ( ' Tx ' )
treeview . append_column ( tvcolumn )
cell = gtk . CellRendererText ( )
tvcolumn . pack_start ( cell , True )
tvcolumn . add_attribute ( cell , ' text ' , 2 )
scroll = gtk . ScrolledWindow ( )
scroll . set_policy ( gtk . POLICY_AUTOMATIC , gtk . POLICY_AUTOMATIC )
scroll . add ( treeview )
hbox = gtk . HBox ( )
2011-12-16 03:22:04 -08:00
if not is_recv :
2011-12-19 03:35:39 -08:00
button = gtk . Button ( " New " )
button . connect ( " clicked " , self . newaddress_dialog )
2011-12-16 03:22:04 -08:00
button . show ( )
hbox . pack_start ( button , False )
2011-11-04 10:00:37 -07:00
2011-11-22 05:41:41 -08:00
def showqrcode ( w , treeview , liststore ) :
path , col = treeview . get_cursor ( )
if not path : return
address = liststore . get_value ( liststore . get_iter ( path ) , 0 )
qr = pyqrnative . QRCode ( 4 , pyqrnative . QRErrorCorrectLevel . H )
qr . addData ( address )
qr . make ( )
boxsize = 7
size = qr . getModuleCount ( ) * boxsize
def area_expose_cb ( area , event ) :
style = area . get_style ( )
k = qr . getModuleCount ( )
for r in range ( k ) :
for c in range ( k ) :
gc = style . black_gc if qr . isDark ( r , c ) else style . white_gc
area . window . draw_rectangle ( gc , True , c * boxsize , r * boxsize , boxsize , boxsize )
area = gtk . DrawingArea ( )
area . set_size_request ( size , size )
area . connect ( " expose-event " , area_expose_cb )
area . show ( )
2011-11-22 07:24:26 -08:00
dialog = gtk . Dialog ( address , parent = self . window , flags = gtk . DIALOG_MODAL | gtk . DIALOG_NO_SEPARATOR , buttons = ( " ok " , 1 ) )
2011-11-22 05:41:41 -08:00
dialog . vbox . add ( area )
dialog . run ( )
dialog . destroy ( )
button = gtk . Button ( " QR " )
button . connect ( " clicked " , showqrcode , treeview , liststore )
button . show ( )
hbox . pack_start ( button , False )
2011-11-04 10:00:37 -07:00
button = gtk . Button ( " Copy to clipboard " )
def copy2clipboard ( w , treeview , liststore ) :
2011-11-13 15:20:49 -08:00
import platform
2011-11-04 10:00:37 -07:00
path , col = treeview . get_cursor ( )
if path :
address = liststore . get_value ( liststore . get_iter ( path ) , 0 )
2011-11-13 15:20:49 -08:00
if platform . system ( ) == ' Windows ' :
from Tkinter import Tk
r = Tk ( )
r . withdraw ( )
r . clipboard_clear ( )
r . clipboard_append ( address )
r . destroy ( )
else :
c = gtk . clipboard_get ( )
c . set_text ( address )
2011-11-04 10:00:37 -07:00
button . connect ( " clicked " , copy2clipboard , treeview , liststore )
button . show ( )
hbox . pack_start ( button , False )
if not is_recv :
button = gtk . Button ( " Pay to " )
def payto ( w , treeview , liststore ) :
path , col = treeview . get_cursor ( )
if path :
address = liststore . get_value ( liststore . get_iter ( path ) , 0 )
self . payto_entry . set_text ( address )
self . notebook . set_current_page ( 1 )
2012-02-03 13:25:50 -08:00
self . amount_entry . grab_focus ( )
2011-11-04 10:00:37 -07:00
button . connect ( " clicked " , payto , treeview , liststore )
button . show ( )
hbox . pack_start ( button , False )
vbox = gtk . VBox ( )
vbox . pack_start ( scroll , True )
vbox . pack_start ( hbox , False )
return vbox
def update_status_bar ( self ) :
2013-09-28 04:45:49 -07:00
interface = self . network . interface
2012-02-08 04:37:14 -08:00
if self . funds_error :
text = " Not enough funds "
2012-05-16 08:29:05 -07:00
elif interface and interface . is_connected :
2013-09-28 04:45:49 -07:00
self . network_button . set_tooltip_text ( " Connected to %s : %d . \n %d blocks " % ( interface . host , interface . port , self . network . blockchain . height ) )
2012-10-25 15:40:19 -07:00
if not self . wallet . up_to_date :
2012-02-08 07:55:13 -08:00
self . status_image . set_from_stock ( gtk . STOCK_REFRESH , gtk . ICON_SIZE_MENU )
2012-02-08 05:13:11 -08:00
text = " Synchronizing... "
else :
2012-02-08 04:37:14 -08:00
self . status_image . set_from_stock ( gtk . STOCK_YES , gtk . ICON_SIZE_MENU )
2013-09-28 04:45:49 -07:00
self . network_button . set_tooltip_text ( " Connected to %s : %d . \n %d blocks " % ( interface . host , interface . port , self . network . blockchain . height ) )
2012-02-08 04:37:14 -08:00
c , u = self . wallet . get_balance ( )
2013-09-11 03:05:28 -07:00
text = " Balance: %s " % ( format_satoshis ( c , False , self . num_zeros ) )
if u : text + = " [ %s unconfirmed] " % ( format_satoshis ( u , True , self . num_zeros ) . strip ( ) )
2011-11-04 10:00:37 -07:00
else :
self . status_image . set_from_stock ( gtk . STOCK_NO , gtk . ICON_SIZE_MENU )
2012-10-25 15:40:19 -07:00
self . network_button . set_tooltip_text ( " Not connected. " )
2012-02-08 04:37:14 -08:00
text = " Not connected "
2011-11-04 10:00:37 -07:00
self . status_bar . pop ( self . context_id )
2012-02-08 04:37:14 -08:00
self . status_bar . push ( self . context_id , text )
2012-10-27 10:20:50 -07:00
if self . wallet . up_to_date and self . wallet_updated :
2012-02-08 04:37:14 -08:00
self . update_history_tab ( )
self . update_receiving_tab ( )
# addressbook too...
2013-09-28 04:45:49 -07:00
self . info . set_text ( self . network . banner )
2012-10-27 10:20:50 -07:00
self . wallet_updated = False
2011-11-04 10:00:37 -07:00
def update_receiving_tab ( self ) :
self . recv_list . clear ( )
2013-03-02 02:40:17 -08:00
for address in self . wallet . addresses ( True ) :
2011-11-04 23:29:19 -07:00
if self . wallet . is_change ( address ) : continue
2011-11-04 10:00:37 -07:00
label = self . wallet . labels . get ( address )
2011-12-21 09:10:21 -08:00
h = self . wallet . history . get ( address , [ ] )
2012-11-04 02:57:12 -08:00
n = len ( h )
2011-11-04 10:00:37 -07:00
tx = " None " if n == 0 else " %d " % n
2011-12-16 03:22:04 -08:00
self . recv_list . append ( ( address , label , tx ) )
2011-11-04 10:00:37 -07:00
def update_sending_tab ( self ) :
# detect addresses that are not mine in history, add them here...
self . addressbook_list . clear ( )
2013-03-14 08:32:05 -07:00
#for alias, v in self.wallet.aliases.items():
# s, target = v
# label = self.wallet.labels.get(alias)
# self.addressbook_list.append((alias, label, '-'))
2012-02-07 06:00:12 -08:00
2011-11-04 10:00:37 -07:00
for address in self . wallet . addressbook :
label = self . wallet . labels . get ( address )
2013-03-16 10:17:50 -07:00
n = self . wallet . get_num_tx ( address )
2013-02-27 12:22:52 -08:00
self . addressbook_list . append ( ( address , label , " %d " % n ) )
2011-11-04 10:00:37 -07:00
def update_history_tab ( self ) :
cursor = self . history_treeview . get_cursor ( ) [ 0 ]
self . history_list . clear ( )
2012-11-16 05:39:31 -08:00
for item in self . wallet . get_tx_history ( ) :
tx_hash , conf , is_mine , value , fee , balance , timestamp = item
2013-04-27 08:48:27 -07:00
if conf > 0 :
2012-11-16 05:39:31 -08:00
try :
time_str = datetime . datetime . fromtimestamp ( timestamp ) . isoformat ( ' ' ) [ : - 3 ]
except :
time_str = " ------ "
2011-11-04 10:00:37 -07:00
conf_icon = gtk . STOCK_APPLY
2013-04-27 08:48:27 -07:00
elif conf == - 1 :
time_str = ' unverified '
conf_icon = None
2011-11-04 10:00:37 -07:00
else :
time_str = ' pending '
conf_icon = gtk . STOCK_EXECUTE
2012-11-16 05:39:31 -08:00
2012-11-05 02:08:16 -08:00
label , is_default_label = self . wallet . get_label ( tx_hash )
2012-11-16 05:39:31 -08:00
tooltip = tx_hash + " \n %d confirmations " % conf if tx_hash else ' '
2013-03-14 08:32:05 -07:00
details = self . get_tx_details ( tx_hash )
2011-11-22 09:54:10 -08:00
self . history_list . prepend ( [ tx_hash , conf_icon , time_str , label , is_default_label ,
2013-09-11 03:05:28 -07:00
format_satoshis ( value , True , self . num_zeros , whitespaces = True ) ,
format_satoshis ( balance , False , self . num_zeros , whitespaces = True ) , tooltip , details ] )
2011-11-04 10:00:37 -07:00
if cursor : self . history_treeview . set_cursor ( cursor )
2013-03-14 08:32:05 -07:00
def get_tx_details ( self , tx_hash ) :
import datetime
if not tx_hash : return ' '
tx = self . wallet . transactions . get ( tx_hash )
2013-04-06 12:20:23 -07:00
is_relevant , is_mine , v , fee = self . wallet . get_tx_value ( tx )
2013-03-14 08:32:05 -07:00
conf , timestamp = self . wallet . verifier . get_confirmations ( tx_hash )
if timestamp :
time_str = datetime . datetime . fromtimestamp ( timestamp ) . isoformat ( ' ' ) [ : - 3 ]
else :
time_str = ' pending '
inputs = map ( lambda x : x . get ( ' address ' ) , tx . inputs )
outputs = map ( lambda x : x . get ( ' address ' ) , tx . d [ ' outputs ' ] )
tx_details = " Transaction Details " + " \n \n " \
+ " Transaction ID: \n " + tx_hash + " \n \n " \
+ " Status: %d confirmations \n " % conf
if is_mine :
if fee :
tx_details + = " Amount sent: %s \n " % format_satoshis ( v - fee , False ) \
+ " Transaction fee: %s \n " % format_satoshis ( fee , False )
else :
tx_details + = " Amount sent: %s \n " % format_satoshis ( v , False ) \
+ " Transaction fee: unknown \n "
else :
tx_details + = " Amount received: %s \n " % format_satoshis ( v , False ) \
tx_details + = " Date: %s \n \n " % time_str \
+ " Inputs: \n - " + ' \n - ' . join ( inputs ) + " \n \n " \
+ " Outputs: \n - " + ' \n - ' . join ( outputs )
return tx_details
2011-11-04 10:00:37 -07:00
2011-12-19 03:35:39 -08:00
def newaddress_dialog ( self , w ) :
2011-11-04 10:00:37 -07:00
2012-02-12 15:00:33 -08:00
title = " New Contact "
2011-12-19 03:35:39 -08:00
dialog = gtk . Dialog ( title , parent = self . window ,
flags = gtk . DIALOG_MODAL | gtk . DIALOG_NO_SEPARATOR ,
buttons = ( " cancel " , 0 , " ok " , 1 ) )
dialog . show ( )
2011-11-04 10:00:37 -07:00
2011-12-19 03:35:39 -08:00
label = gtk . HBox ( )
label_label = gtk . Label ( ' Label: ' )
label_label . set_size_request ( 120 , 10 )
label_label . show ( )
label . pack_start ( label_label )
label_entry = gtk . Entry ( )
label_entry . show ( )
label . pack_start ( label_entry )
label . show ( )
dialog . vbox . pack_start ( label , False , True , 5 )
address = gtk . HBox ( )
address_label = gtk . Label ( ' Address: ' )
address_label . set_size_request ( 120 , 10 )
address_label . show ( )
address . pack_start ( address_label )
address_entry = gtk . Entry ( )
address_entry . show ( )
address . pack_start ( address_entry )
address . show ( )
dialog . vbox . pack_start ( address , False , True , 5 )
result = dialog . run ( )
address = address_entry . get_text ( )
label = label_entry . get_text ( )
dialog . destroy ( )
2011-11-04 10:00:37 -07:00
2011-12-19 03:35:39 -08:00
if result == 1 :
2013-03-01 05:27:56 -08:00
if is_valid ( address ) :
2013-05-02 01:10:22 -07:00
self . wallet . add_contact ( address , label )
2011-12-19 03:35:39 -08:00
self . update_sending_tab ( )
else :
errorDialog = gtk . MessageDialog (
parent = self . window ,
flags = gtk . DIALOG_MODAL ,
buttons = gtk . BUTTONS_CLOSE ,
message_format = " Invalid address " )
errorDialog . show ( )
errorDialog . run ( )
errorDialog . destroy ( )
2011-12-08 12:43:52 -08:00
2011-11-04 10:00:37 -07:00
2012-02-14 00:52:03 -08:00
class ElectrumGui ( ) :
2013-09-11 03:05:28 -07:00
def __init__ ( self , config , network ) :
self . network = network
2012-10-11 11:10:12 -07:00
self . config = config
2013-09-11 03:05:28 -07:00
2012-02-14 00:52:03 -08:00
2012-02-14 03:45:39 -08:00
def main ( self , url = None ) :
2013-09-28 04:45:49 -07:00
storage = WalletStorage ( self . config )
if not storage . file_exists :
action = self . restore_or_create ( )
if not action :
exit ( )
self . wallet = wallet = Wallet ( storage )
gap = self . config . get ( ' gap_limit ' , 5 )
if gap != 5 :
wallet . gap_limit = gap
wallet . storage . put ( ' gap_limit ' , gap , True )
self . wallet . start_threads ( self . network )
if action == ' create ' :
wallet . init_seed ( None )
wallet . save_seed ( )
wallet . create_accounts ( )
wallet . synchronize ( ) # generate first addresses offline
elif action == ' restore ' :
seed = self . seed_dialog ( )
wallet . init_seed ( seed )
wallet . save_seed ( )
self . restore_wallet ( wallet )
else :
exit ( )
else :
self . wallet = Wallet ( storage )
self . wallet . start_threads ( self . network )
w = ElectrumWindow ( self . wallet , self . config , self . network )
if url : w . set_url ( url )
2011-11-04 10:00:37 -07:00
gtk . main ( )
2012-02-14 00:52:03 -08:00
def restore_or_create ( self ) :
2013-09-28 04:45:49 -07:00
return restore_create_dialog ( )
2012-10-11 04:18:04 -07:00
2012-11-20 06:30:46 -08:00
def seed_dialog ( self ) :
2013-09-28 04:45:49 -07:00
return run_recovery_dialog ( )
2012-11-20 06:30:46 -08:00
2013-04-09 09:08:14 -07:00
def verify_seed ( self ) :
self . wallet . save_seed ( )
return True
2012-11-20 06:30:46 -08:00
def network_dialog ( self ) :
2013-09-28 04:45:49 -07:00
return run_network_dialog ( self . network , parent = None )
2012-11-20 06:30:46 -08:00
2012-11-20 12:36:06 -08:00
def show_seed ( self ) :
show_seed_dialog ( self . wallet , None , None )
def password_dialog ( self ) :
change_password_dialog ( self . wallet , None , None )
2012-11-20 06:30:46 -08:00
2013-09-28 04:45:49 -07:00
def restore_wallet ( self , wallet ) :
2012-11-20 06:30:46 -08:00
dialog = gtk . MessageDialog (
parent = None ,
flags = gtk . DIALOG_MODAL ,
buttons = gtk . BUTTONS_CANCEL ,
message_format = " Please wait... " )
dialog . show ( )
def recover_thread ( wallet , dialog ) :
2013-09-28 04:45:49 -07:00
wallet . restore ( lambda x : x )
2012-11-20 06:30:46 -08:00
gobject . idle_add ( dialog . destroy )
thread . start_new_thread ( recover_thread , ( wallet , dialog ) )
r = dialog . run ( )
dialog . destroy ( )
if r == gtk . RESPONSE_CANCEL : return False
2012-11-20 12:36:06 -08:00
if not wallet . is_found ( ) :
2012-11-20 06:30:46 -08:00
show_message ( " No transactions found for this seed " )
return True