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-11-22 05:41:41 -08:00
import pyqrnative
2011-12-06 12:05:46 -08:00
from decimal import Decimal
2011-11-04 10:00:37 -07:00
gtk . gdk . threads_init ( )
APP_NAME = " Electrum "
2012-02-05 22:48:52 -08:00
from wallet import format_satoshis
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 ) :
2011-11-08 04:56:35 -08:00
import mnemonic
try :
seed = wallet . pw_decode ( wallet . seed , password )
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 ( )
2011-11-04 10:00:37 -07:00
def init_wallet ( wallet ) :
2011-12-06 06:40:04 -08:00
try :
found = wallet . read ( )
except BaseException , e :
2011-12-16 06:40:05 -08:00
show_message ( e . message )
exit ( 1 )
2011-11-06 02:13:58 -08:00
2011-12-06 06:40:04 -08:00
if not found :
2011-11-06 02:13:58 -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 ( )
if r == 2 :
2011-11-10 00:34:27 -08:00
sys . exit ( 1 )
2011-11-06 02:13:58 -08:00
is_recovery = ( r == 1 )
if not is_recovery :
wallet . new_seed ( None )
# ask for the server.
2012-01-12 06:42:13 -08:00
wallet . interface . get_servers ( )
2012-01-12 05:48:41 -08:00
run_network_dialog ( wallet , parent = None )
2011-11-06 02:13:58 -08:00
# generate first key
2011-12-18 14:02:59 -08:00
wallet . synchronize ( )
2011-11-06 02:13:58 -08:00
# run a dialog indicating the seed, ask the user to remember it
2011-12-06 03:39:42 -08:00
show_seed_dialog ( wallet , None , None )
2011-11-04 10:00:37 -07:00
2011-11-06 02:13:58 -08:00
#ask for password
2011-12-06 03:39:42 -08:00
change_password_dialog ( wallet , None , None )
2011-11-06 02:13:58 -08:00
else :
2012-01-12 05:48:41 -08:00
# ask for the server.
2012-01-21 00:44:15 -08:00
wallet . interface . get_servers ( )
2012-01-12 05:48:41 -08:00
run_network_dialog ( wallet , parent = None )
# ask for seed and gap.
run_recovery_dialog ( wallet )
2011-11-06 02:13:58 -08:00
dialog = gtk . MessageDialog (
parent = None ,
flags = gtk . DIALOG_MODAL ,
buttons = gtk . BUTTONS_CANCEL ,
message_format = " Please wait... " )
2011-11-04 10:00:37 -07:00
dialog . show ( )
2011-11-06 02:13:58 -08:00
2011-12-15 06:41:50 -08:00
def recover_thread ( wallet , dialog ) :
wallet . init_mpk ( wallet . seed ) # not encrypted at this point
2011-12-19 04:18:19 -08:00
wallet . synchronize ( )
if wallet . is_found ( ) :
# history and addressbook
wallet . update_tx_history ( )
wallet . fill_addressbook ( )
print " recovery successful "
2011-11-06 02:13:58 -08:00
wallet . save ( )
2011-12-19 04:18:19 -08:00
2011-11-06 02:13:58 -08:00
gobject . idle_add ( dialog . destroy )
2011-12-15 06:41:50 -08:00
thread . start_new_thread ( recover_thread , ( wallet , dialog ) )
2011-11-04 10:00:37 -07:00
r = dialog . run ( )
dialog . destroy ( )
2011-11-10 00:34:27 -08:00
if r == gtk . RESPONSE_CANCEL : sys . exit ( 1 )
2011-11-06 02:13:58 -08:00
if not wallet . is_found :
show_message ( " No transactions found for this seed " )
2011-11-04 10:00:37 -07:00
2012-01-12 05:48:41 -08:00
def run_recovery_dialog ( wallet ) :
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 )
gap = gtk . HBox ( )
gap_label = gtk . Label ( ' Gap limit: ' )
gap_label . set_size_request ( 150 , 10 )
gap_label . show ( )
gap . pack_start ( gap_label , False , False , 10 )
gap_entry = gtk . Entry ( )
gap_entry . set_text ( " %d " % wallet . gap_limit )
gap_entry . connect ( ' changed ' , numbify , True )
gap_entry . show ( )
gap . pack_start ( gap_entry , False , False , 10 )
add_help_button ( gap , ' The maximum gap that is allowed between unused addresses in your wallet. During wallet recovery, this parameter is used to decide when to stop the recovery process. If you increase this value, you will need to remember it in order to be able to recover your wallet from seed. ' )
gap . show ( )
vbox . pack_start ( gap , False , False , 5 )
2011-11-04 10:00:37 -07:00
2012-01-12 05:48:41 -08:00
dialog . show ( )
r = dialog . run ( )
gap = gap_entry . get_text ( )
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 :
sys . exit ( 1 )
try :
gap = int ( gap )
except :
show_message ( " error " )
sys . exit ( 1 )
try :
seed . decode ( ' hex ' )
except :
import mnemonic
print " not hex, trying decode "
seed = mnemonic . mn_decode ( seed . split ( ' ' ) )
2012-01-12 06:42:13 -08:00
if not seed :
show_message ( " no seed " )
sys . exit ( 1 )
2012-01-12 05:48:41 -08:00
wallet . seed = seed
wallet . gap_limit = gap
wallet . save ( )
def run_settings_dialog ( wallet , parent ) :
message = " These 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 )
add_help_button ( fee , ' Fee per transaction input. Transactions involving multiple inputs tend to have a higher fee. Recommended value:0.0005 ' )
fee . show ( )
vbox . pack_start ( fee , False , False , 5 )
2011-11-06 02:13:58 -08:00
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 ( )
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
2012-01-12 05:48:41 -08:00
wallet . fee = fee
2011-12-02 07:40:48 -08:00
wallet . save ( )
2011-11-04 10:00:37 -07:00
2012-01-12 05:48:41 -08:00
def run_network_dialog ( wallet , parent ) :
image = gtk . Image ( )
image . set_from_stock ( gtk . STOCK_NETWORK , gtk . ICON_SIZE_DIALOG )
2012-01-12 06:42:13 -08:00
if parent :
if wallet . interface . is_connected :
status = " Connected to %s . \n %d blocks \n response time: %f " % ( wallet . interface . host , wallet . interface . blocks , wallet . interface . rtime )
else :
status = " Not connected "
2012-01-13 02:52:14 -08:00
host = wallet . interface . host
port = wallet . interface . port
2012-01-12 05:48:41 -08:00
else :
2012-01-13 02:52:14 -08:00
import random
2012-01-12 06:42:13 -08:00
status = " Please choose a server. "
2012-01-13 02:52:14 -08:00
host = random . choice ( wallet . interface . servers )
port = 50000
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-01-13 02:52:14 -08:00
host_entry . set_text ( host + " : %d " % port )
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-01-12 05:48:41 -08:00
server_list = gtk . ListStore ( str )
for item in wallet . interface . servers :
server_list . append ( [ item ] )
treeview = gtk . TreeView ( model = server_list )
treeview . show ( )
tvcolumn = gtk . TreeViewColumn ( ' Active servers ' )
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-01-12 05:48:41 -08:00
vbox . pack_start ( scroll )
def my_treeview_cb ( treeview ) :
path , view_column = treeview . get_cursor ( )
host = server_list . get_value ( server_list . get_iter ( path ) , 0 )
host_entry . set_text ( host + " :50000 " )
treeview . connect ( ' cursor-changed ' , my_treeview_cb )
dialog . show ( )
r = dialog . run ( )
hh = host_entry . get_text ( )
dialog . destroy ( )
2012-01-12 06:42:13 -08:00
2012-01-12 05:48:41 -08:00
if r == gtk . RESPONSE_CANCEL :
2012-01-12 06:42:13 -08:00
if parent == None :
sys . exit ( 1 )
else :
return
2012-01-12 05:48:41 -08:00
try :
if ' : ' in hh :
host , port = hh . split ( ' : ' )
port = int ( port )
else :
host = hh
port = 50000
except :
2012-01-12 06:42:13 -08:00
show_message ( " error " )
if parent == None :
sys . exit ( 1 )
else :
return
2012-01-12 05:48:41 -08:00
if host != wallet . interface . host or port != wallet . interface . port :
wallet . interface . host = host
wallet . interface . set_port ( port )
wallet . interface . is_connected = False
2012-01-21 00:44:15 -08:00
if parent :
wallet . save ( )
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 ) :
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 :
2011-11-05 07:06:43 -07:00
seed = wallet . pw_decode ( wallet . 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
wallet . use_encryption = ( new_password != ' ' )
2011-11-05 07:06:43 -07:00
wallet . seed = wallet . pw_encode ( seed , new_password )
2011-12-20 03:37:48 -08:00
for k in wallet . imported_keys . keys ( ) :
a = wallet . imported_keys [ k ]
b = wallet . pw_decode ( a , password )
c = wallet . pw_encode ( b , new_password )
wallet . imported_keys [ k ] = c
2011-11-04 10:00:37 -07:00
wallet . save ( )
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 ' )
class BitcoinGUI :
2011-12-07 09:54:32 -08:00
def show_message ( self , msg ) :
show_message ( msg , self . window )
2011-11-04 10:00:37 -07:00
def __init__ ( self , wallet ) :
self . error = ' '
self . wallet = wallet
2012-01-12 05:48:41 -08:00
self . wallet . interface . is_connected = False
2011-11-10 13:21:40 -08:00
self . period = 5
2011-11-04 10:00:37 -07:00
self . window = MyWindow ( gtk . WINDOW_TOPLEVEL )
2011-12-09 10:01:53 -08:00
self . window . set_title ( APP_NAME + " " + self . wallet . electrum_version )
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 )
2011-11-04 10:00:37 -07:00
vbox = gtk . VBox ( )
self . notebook = gtk . Notebook ( )
self . create_history_tab ( )
self . create_send_tab ( )
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 ( )
2012-01-12 05:48:41 -08:00
self . network_button . connect ( " clicked " , lambda x : run_network_dialog ( self . wallet , 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
def seedb ( w , wallet ) :
if wallet . use_encryption :
2012-02-06 09:01:35 -08:00
password = password_dialog ( self . window )
2011-11-08 04:56:35 -08:00
if not password : return
else : password = None
2011-12-06 03:39:42 -08:00
show_seed_dialog ( wallet , password , self . window )
2011-11-08 04:56:35 -08:00
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-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 ( )
password_button = gtk . Button ( )
2011-12-06 03:39:42 -08:00
password_button . connect ( " clicked " , lambda x : change_password_dialog ( self . wallet , self . window , pw_icon ) )
2011-11-04 16:15:02 -07:00
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 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 ( )
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-06 09:55:25 -08:00
to_address = self . 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
def update_wallet_thread ( ) :
while True :
try :
2012-01-12 05:48:41 -08:00
self . wallet . interface . is_connected = False
2012-01-11 07:50:24 -08:00
self . wallet . interface . new_session ( self . wallet . all_addresses ( ) , self . wallet . electrum_version )
2011-12-08 12:43:52 -08:00
self . update_session = False
2012-01-11 07:50:24 -08:00
self . info . set_text ( self . wallet . interface . message )
2011-11-04 10:00:37 -07:00
except :
2011-12-02 08:22:49 -08:00
traceback . print_exc ( file = sys . stdout )
2011-11-10 13:21:40 -08:00
time . sleep ( self . period )
2011-11-04 10:00:37 -07:00
continue
2011-11-10 13:21:40 -08:00
2011-12-05 05:47:12 -08:00
get_servers_time = 0
2011-11-04 10:00:37 -07:00
while True :
2011-12-13 07:58:42 -08:00
try :
2012-01-12 05:48:41 -08:00
if self . wallet . interface . is_connected and self . update_session :
2012-01-11 09:30:39 -08:00
self . wallet . interface . update_session ( self . wallet . all_addresses ( ) )
2011-12-13 07:58:42 -08:00
self . update_session = False
2011-12-08 12:43:52 -08:00
2011-12-13 07:58:42 -08:00
if time . time ( ) - get_servers_time > 5 * 60 :
2012-01-11 07:50:24 -08:00
wallet . interface . get_servers ( )
2011-12-05 05:47:12 -08:00
get_servers_time = time . time ( )
2012-01-11 07:50:24 -08:00
self . period = 15 if self . wallet . interface . use_http ( ) else 5
2011-12-19 04:18:19 -08:00
if self . wallet . update ( ) :
2012-01-11 09:31:21 -08:00
self . wallet . interface . update_session ( self . wallet . all_addresses ( ) )
2011-12-13 07:58:42 -08:00
gobject . idle_add ( self . update_history_tab )
2011-12-16 03:22:04 -08:00
gobject . idle_add ( self . update_receiving_tab )
# addressbook too...
2011-12-13 07:58:42 -08:00
time . sleep ( self . period )
2011-12-03 02:31:00 -08:00
except BaseException :
2012-01-11 07:50:24 -08:00
traceback . print_exc ( file = sys . stdout )
2011-12-03 02:31:00 -08:00
print " starting new session "
break
2011-11-08 04:56:35 -08:00
except socket . gaierror :
2012-01-12 05:48:41 -08:00
self . wallet . interface . is_connected = False
2011-11-08 04:56:35 -08:00
break
2011-11-04 10:00:37 -07:00
except :
2012-01-12 05:48:41 -08:00
self . wallet . interface . is_connected = False
2011-11-04 10:00:37 -07:00
print " error "
traceback . print_exc ( file = sys . stdout )
break
2012-01-12 05:48:41 -08:00
self . error = ' ' if self . wallet . interface . is_connected else " Not connected "
2011-11-04 10:00:37 -07:00
thread . start_new_thread ( update_wallet_thread , ( ) )
thread . start_new_thread ( update_status_bar_thread , ( ) )
2012-02-03 06:45:41 -08:00
thread . start_new_thread ( check_recipient_thread , ( ) )
2011-11-04 10:00:37 -07:00
self . notebook . set_current_page ( 0 )
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 ) :
amount = numbify ( amount_entry )
fee = numbify ( fee_entry )
if not is_fee : fee = None
2011-12-07 13:11:35 -08:00
if amount is None :
2012-02-03 02:48:09 -08:00
#self.fee_box.hide();
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 )
2011-12-07 13:11:35 -08:00
self . error = ' '
2011-12-07 09:54:32 -08:00
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 " ) )
self . error = ' Not enough funds '
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-03 13:25:50 -08:00
def set_send_tab ( self , payto , amount , message , label , identity , signature , cmd ) :
2012-02-07 02:14:48 -08:00
self . notebook . set_current_page ( 1 )
2012-02-03 06:45:41 -08:00
if signature :
2012-02-06 14:45:21 -08:00
if re . match ( ' ^(|([ \ w \ - \ .]+)@)(( \ w[ \ w \ -]+ \ .)+[ \ w \ -]+)$ ' , identity ) :
signing_address = self . get_alias ( identity , interactive = True )
elif self . wallet . is_valid ( identity ) :
signing_address = identity
else :
signing_address = None
2012-02-03 08:17:33 -08:00
if not signing_address :
return
2012-02-03 07:39:27 -08:00
try :
2012-02-03 06:45:41 -08:00
self . wallet . verify_message ( signing_address , signature , cmd )
2012-02-03 08:38:43 -08:00
self . wallet . receipt = ( signing_address , signature , cmd )
2012-02-03 06:45:41 -08:00
except :
2012-02-03 08:17:33 -08:00
self . show_message ( ' Warning: the URI contains a bad signature. \n The identity of the recipient cannot be verified. ' )
2012-02-07 02:14:48 -08:00
payto = amount = label = identity = message = ' '
2012-02-03 06:45:41 -08:00
2012-02-03 13:27:41 -08:00
# redundant with aliases
#if label and payto:
# self.labels[payto] = label
2012-02-07 03:50:28 -08:00
if re . match ( ' ^(|([ \ w \ - \ .]+)@)(( \ w[ \ w \ -]+ \ .)+[ \ w \ -]+)$ ' , payto ) :
payto_address = self . get_alias ( payto , interactive = True )
if payto_address :
payto = payto + ' < ' + payto_address + ' > '
2012-02-03 13:25:50 -08:00
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 ) :
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 )
page . pack_start ( tv )
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-02 09:44:50 -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
2012-02-06 09:55:25 -08:00
def get_alias ( self , alias , interactive = False ) :
2012-02-03 08:17:33 -08:00
try :
2012-02-06 09:55:25 -08:00
target , signing_address , auth_name = self . wallet . read_alias ( alias )
2012-02-03 08:17:33 -08:00
except BaseException , e :
2012-02-06 08:59:31 -08:00
# raise exception if verify fails (verify the chain)
2012-02-07 06:27:36 -08:00
if interactive :
self . show_message ( " Alias error: " + e . message )
2012-02-06 08:59:31 -08:00
return
2012-02-03 08:17:33 -08:00
2012-02-06 09:55:25 -08:00
print target , signing_address , auth_name
2012-02-06 08:59:31 -08:00
if auth_name is None :
2012-02-06 09:55:25 -08:00
a = self . wallet . aliases . get ( alias )
2012-02-06 08:59:31 -08:00
if not a :
2012-02-07 06:37:27 -08:00
msg = " Warning: the alias ' %s ' is self-signed. The signing address is %s . Do you want to trust this alias? " % ( alias , signing_address )
2012-02-07 04:22:54 -08:00
if interactive and self . question ( msg ) :
2012-02-07 06:00:12 -08:00
self . wallet . aliases [ alias ] = ( signing_address , target )
2012-02-06 08:59:31 -08:00
else :
target = None
else :
2012-02-07 06:00:12 -08:00
if signing_address != a [ 0 ] :
2012-02-07 04:22:54 -08:00
msg = " Warning: the key of alias ' %s ' has changed since your last visit! It is possible that someone is trying to do something nasty!!! \n Do you accept to change your trusted key? " % alias
if interactive and self . question ( msg ) :
2012-02-07 06:00:12 -08:00
self . wallet . aliases [ alias ] = ( signing_address , target )
2012-02-06 08:59:31 -08:00
else :
target = None
else :
if signing_address not in self . wallet . authorities . keys ( ) :
2012-02-07 04:22:54 -08:00
msg = " The alias: ' %s ' links to %s \n \n Warning: this alias was signed by an unknown key. \n Signing authority: %s \n Signing address: %s \n \n Do you want to add this key to your list of trusted keys? " % ( alias , target , auth_name , signing_address )
if interactive and self . question ( msg ) :
2012-02-06 08:59:31 -08:00
self . wallet . authorities [ signing_address ] = auth_name
else :
target = None
2012-02-07 04:22:54 -08:00
if target :
2012-02-07 06:00:12 -08:00
self . wallet . aliases [ alias ] = ( signing_address , target )
self . update_sending_tab ( )
2012-02-06 08:59:31 -08:00
return target
2012-02-03 08:17:33 -08:00
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-06 09:55:25 -08:00
to_address = self . get_alias ( r , interactive = True )
2012-02-03 08:17:33 -08:00
if not to_address :
return
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
2011-11-04 10:00:37 -07:00
if not self . wallet . 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 :
tx = self . wallet . mktx ( to_address , amount , label , password , fee )
except BaseException , e :
self . show_message ( e . message )
2011-11-16 06:16:36 -08:00
return
2011-12-16 06:40:05 -08:00
self . update_session = True # we created a new change address
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 )
a = self . wallet . aliases . get ( m )
2012-02-07 06:19:48 -08:00
if a :
if a [ 0 ] in self . wallet . authorities . keys ( ) :
s = self . wallet . authorities . get ( a [ 0 ] )
else :
s = " self "
msg = ' Alias: ' + m + ' \n \n Target: ' + a [ 1 ] + ' \n Signed by: ' + s + ' \n Signing 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 )
a = self . wallet . aliases . get ( m )
2012-02-07 06:19:48 -08:00
if a :
if a [ 0 ] in self . wallet . authorities . keys ( ) :
s = self . wallet . authorities . get ( a [ 0 ] )
else :
s = " self "
msg = ' Alias: ' + m + ' \n \n Target: ' + a [ 1 ] + ' \n Signed by: ' + s + ' \n Signing 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 ' )
cell . set_property ( ' family ' , ' monospace ' )
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 )
2011-12-09 11:41:21 -08:00
cell . set_property ( ' family ' , ' monospace ' )
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 )
2011-12-09 11:41:21 -08:00
cell . set_property ( ' family ' , ' monospace ' )
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 ( )
cell . set_property ( ' family ' , ' monospace ' )
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 . wallet . update_tx_labels ( )
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 ) :
c , u = self . wallet . get_balance ( )
2012-01-12 05:48:41 -08:00
if self . wallet . interface . is_connected :
2011-11-04 10:00:37 -07:00
self . status_image . set_from_stock ( gtk . STOCK_YES , gtk . ICON_SIZE_MENU )
2012-01-11 08:41:38 -08:00
self . network_button . set_tooltip_text ( " Connected to %s . \n %d blocks \n response time: %f " % ( self . wallet . interface . host , self . wallet . interface . blocks , self . wallet . interface . rtime ) )
2011-11-04 10:00:37 -07:00
else :
self . status_image . set_from_stock ( gtk . STOCK_NO , gtk . ICON_SIZE_MENU )
2012-01-11 08:41:38 -08:00
self . network_button . set_tooltip_text ( " Trying to contact %s . \n %d blocks " % ( self . wallet . interface . host , self . wallet . interface . blocks ) )
2011-11-04 10:00:37 -07:00
text = " Balance: %s " % ( format_satoshis ( c ) )
2012-02-05 22:48:52 -08:00
if u : text + = " [ %s unconfirmed] " % ( format_satoshis ( u , True ) )
2011-11-04 10:00:37 -07:00
if self . error : text = self . error
self . status_bar . pop ( self . context_id )
self . status_bar . push ( self . context_id , text )
def update_receiving_tab ( self ) :
self . recv_list . clear ( )
2011-12-21 09:10:21 -08:00
for address in self . wallet . all_addresses ( ) :
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 )
n = 0
2011-12-21 09:10:21 -08:00
h = self . wallet . history . get ( address , [ ] )
for item in h :
if not item [ ' is_in ' ] : n = n + 1
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 ( )
2012-02-07 06:00:12 -08:00
for alias , v in self . wallet . aliases . items ( ) :
s , target = v
label = self . wallet . labels . get ( alias )
self . addressbook_list . append ( ( alias , label , ' - ' ) )
2011-11-04 10:00:37 -07:00
for address in self . wallet . addressbook :
label = self . wallet . labels . get ( address )
n = 0
for item in self . wallet . tx_history . values ( ) :
if address in item [ ' outputs ' ] : n = n + 1
tx = " None " if n == 0 else " %d " % n
self . addressbook_list . append ( ( address , label , tx ) )
def update_history_tab ( self ) :
cursor = self . history_treeview . get_cursor ( ) [ 0 ]
self . history_list . clear ( )
balance = 0
for tx in self . wallet . get_tx_history ( ) :
tx_hash = tx [ ' tx_hash ' ]
if tx [ ' height ' ] :
2012-01-11 08:41:38 -08:00
conf = self . wallet . interface . blocks - tx [ ' height ' ] + 1
2011-11-04 10:00:37 -07:00
time_str = datetime . datetime . fromtimestamp ( tx [ ' nTime ' ] ) . isoformat ( ' ' ) [ : - 3 ]
conf_icon = gtk . STOCK_APPLY
else :
conf = 0
time_str = ' pending '
conf_icon = gtk . STOCK_EXECUTE
v = tx [ ' value ' ]
balance + = v
label = self . wallet . labels . get ( tx_hash )
is_default_label = ( label == ' ' ) or ( label is None )
if is_default_label : label = tx [ ' default_label ' ]
tooltip = tx_hash + " \n %d confirmations " % conf
2011-11-22 09:54:10 -08:00
2012-02-07 08:09:14 -08:00
# tx = self.wallet.tx_history.get(tx_hash)
details = " Transaction Details: \n \n " \
+ " Transaction ID: \n " + tx_hash + " \n \n " \
+ " Status: %d confirmations \n \n " % conf \
+ " Date: %s \n \n " % time_str \
+ " Inputs: \n - " + ' \n - ' . join ( tx [ ' inputs ' ] ) + " \n \n " \
+ " Outputs: \n - " + ' \n - ' . join ( tx [ ' outputs ' ] )
r = self . wallet . receipts . get ( tx_hash )
if r :
details + = " \n _______________________________________ " \
2012-02-07 08:12:05 -08:00
+ " \n \n URI signed by: " + r [ 0 ] \
+ ' \n \n Signed data: ' + r [ 2 ] \
+ ' \n \n Signature: ' + r [ 1 ]
2011-11-22 09:54:10 -08:00
self . history_list . prepend ( [ tx_hash , conf_icon , time_str , label , is_default_label ,
2012-02-05 22:48:52 -08:00
format_satoshis ( v , True ) , format_satoshis ( balance ) , tooltip , details ] )
2011-11-04 10:00:37 -07:00
if cursor : self . history_treeview . set_cursor ( cursor )
2011-12-19 03:35:39 -08:00
def newaddress_dialog ( self , w ) :
2011-11-04 10:00:37 -07:00
2011-12-19 03:35:39 -08:00
title = " New sending address "
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 :
if self . wallet . is_valid ( address ) :
self . wallet . addressbook . append ( address )
if label : self . wallet . labels [ address ] = label
self . wallet . save ( )
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
def main ( self ) :
gtk . main ( )