stdio.py (8385B)
1 from decimal import Decimal 2 import getpass 3 import datetime 4 import logging 5 6 from electrum import util 7 from electrum import WalletStorage, Wallet 8 from electrum.wallet_db import WalletDB 9 from electrum.util import format_satoshis 10 from electrum.bitcoin import is_address, COIN 11 from electrum.transaction import PartialTxOutput 12 from electrum.network import TxBroadcastError, BestEffortRequestFailed 13 from electrum.logging import console_stderr_handler 14 15 _ = lambda x:x # i18n 16 17 # minimal fdisk like gui for console usage 18 # written by rofl0r, with some bits stolen from the text gui (ncurses) 19 20 21 class ElectrumGui: 22 23 def __init__(self, config, daemon, plugins): 24 self.config = config 25 self.network = daemon.network 26 storage = WalletStorage(config.get_wallet_path()) 27 if not storage.file_exists: 28 print("Wallet not found. try 'electrum create'") 29 exit() 30 if storage.is_encrypted(): 31 password = getpass.getpass('Password:', stream=None) 32 storage.decrypt(password) 33 34 db = WalletDB(storage.read(), manual_upgrades=False) 35 36 self.done = 0 37 self.last_balance = "" 38 39 console_stderr_handler.setLevel(logging.CRITICAL) 40 41 self.str_recipient = "" 42 self.str_description = "" 43 self.str_amount = "" 44 self.str_fee = "" 45 46 self.wallet = Wallet(db, storage, config=config) 47 self.wallet.start_network(self.network) 48 self.contacts = self.wallet.contacts 49 50 util.register_callback(self.on_network, ['wallet_updated', 'network_updated', 'banner']) 51 self.commands = [_("[h] - displays this help text"), \ 52 _("[i] - display transaction history"), \ 53 _("[o] - enter payment order"), \ 54 _("[p] - print stored payment order"), \ 55 _("[s] - send stored payment order"), \ 56 _("[r] - show own receipt addresses"), \ 57 _("[c] - display contacts"), \ 58 _("[b] - print server banner"), \ 59 _("[q] - quit") ] 60 self.num_commands = len(self.commands) 61 62 def on_network(self, event, *args): 63 if event in ['wallet_updated', 'network_updated']: 64 self.updated() 65 elif event == 'banner': 66 self.print_banner() 67 68 def main_command(self): 69 self.print_balance() 70 c = input("enter command: ") 71 if c == "h" : self.print_commands() 72 elif c == "i" : self.print_history() 73 elif c == "o" : self.enter_order() 74 elif c == "p" : self.print_order() 75 elif c == "s" : self.send_order() 76 elif c == "r" : self.print_addresses() 77 elif c == "c" : self.print_contacts() 78 elif c == "b" : self.print_banner() 79 elif c == "n" : self.network_dialog() 80 elif c == "e" : self.settings_dialog() 81 elif c == "q" : self.done = 1 82 else: self.print_commands() 83 84 def updated(self): 85 s = self.get_balance() 86 if s != self.last_balance: 87 print(s) 88 self.last_balance = s 89 return True 90 91 def print_commands(self): 92 self.print_list(self.commands, "Available commands") 93 94 def print_history(self): 95 width = [20, 40, 14, 14] 96 delta = (80 - sum(width) - 4)/3 97 format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%" \ 98 + "%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s" 99 messages = [] 100 101 for hist_item in reversed(self.wallet.get_history()): 102 if hist_item.tx_mined_status.conf: 103 timestamp = hist_item.tx_mined_status.timestamp 104 try: 105 time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] 106 except Exception: 107 time_str = "unknown" 108 else: 109 time_str = 'unconfirmed' 110 111 label = self.wallet.get_label_for_txid(hist_item.txid) 112 messages.append(format_str % (time_str, label, format_satoshis(delta, whitespaces=True), 113 format_satoshis(hist_item.balance, whitespaces=True))) 114 115 self.print_list(messages[::-1], format_str%( _("Date"), _("Description"), _("Amount"), _("Balance"))) 116 117 118 def print_balance(self): 119 print(self.get_balance()) 120 121 def get_balance(self): 122 if self.wallet.network.is_connected(): 123 if not self.wallet.up_to_date: 124 msg = _( "Synchronizing..." ) 125 else: 126 c, u, x = self.wallet.get_balance() 127 msg = _("Balance")+": %f "%(Decimal(c) / COIN) 128 if u: 129 msg += " [%f unconfirmed]"%(Decimal(u) / COIN) 130 if x: 131 msg += " [%f unmatured]"%(Decimal(x) / COIN) 132 else: 133 msg = _( "Not connected" ) 134 135 return(msg) 136 137 138 def print_contacts(self): 139 messages = map(lambda x: "%20s %45s "%(x[0], x[1][1]), self.contacts.items()) 140 self.print_list(messages, "%19s %25s "%("Key", "Value")) 141 142 def print_addresses(self): 143 messages = map(lambda addr: "%30s %30s "%(addr, self.wallet.get_label(addr)), self.wallet.get_addresses()) 144 self.print_list(messages, "%19s %25s "%("Address", "Label")) 145 146 def print_order(self): 147 print("send order to " + self.str_recipient + ", amount: " + self.str_amount \ 148 + "\nfee: " + self.str_fee + ", desc: " + self.str_description) 149 150 def enter_order(self): 151 self.str_recipient = input("Pay to: ") 152 self.str_description = input("Description : ") 153 self.str_amount = input("Amount: ") 154 self.str_fee = input("Fee: ") 155 156 def send_order(self): 157 self.do_send() 158 159 def print_banner(self): 160 for i, x in enumerate( self.wallet.network.banner.split('\n') ): 161 print( x ) 162 163 def print_list(self, lst, firstline): 164 lst = list(lst) 165 self.maxpos = len(lst) 166 if not self.maxpos: return 167 print(firstline) 168 for i in range(self.maxpos): 169 msg = lst[i] if i < len(lst) else "" 170 print(msg) 171 172 173 def main(self): 174 while self.done == 0: self.main_command() 175 176 def do_send(self): 177 if not is_address(self.str_recipient): 178 print(_('Invalid Bitcoin address')) 179 return 180 try: 181 amount = int(Decimal(self.str_amount) * COIN) 182 except Exception: 183 print(_('Invalid Amount')) 184 return 185 try: 186 fee = int(Decimal(self.str_fee) * COIN) 187 except Exception: 188 print(_('Invalid Fee')) 189 return 190 191 if self.wallet.has_password(): 192 password = self.password_dialog() 193 if not password: 194 return 195 else: 196 password = None 197 198 c = "" 199 while c != "y": 200 c = input("ok to send (y/n)?") 201 if c == "n": return 202 203 try: 204 tx = self.wallet.mktx(outputs=[PartialTxOutput.from_address_and_value(self.str_recipient, amount)], 205 password=password, 206 fee=fee) 207 except Exception as e: 208 print(repr(e)) 209 return 210 211 if self.str_description: 212 self.wallet.set_label(tx.txid(), self.str_description) 213 214 print(_("Please wait...")) 215 try: 216 self.network.run_from_another_thread(self.network.broadcast_transaction(tx)) 217 except TxBroadcastError as e: 218 msg = e.get_message_for_gui() 219 print(msg) 220 except BestEffortRequestFailed as e: 221 msg = repr(e) 222 print(msg) 223 else: 224 print(_('Payment sent.')) 225 #self.do_clear() 226 #self.update_contacts_tab() 227 228 def network_dialog(self): 229 print("use 'electrum setconfig server/proxy' to change your network settings") 230 return True 231 232 233 def settings_dialog(self): 234 print("use 'electrum setconfig' to change your settings") 235 return True 236 237 def password_dialog(self): 238 return getpass.getpass() 239 240 241 # XXX unused 242 243 def run_receive_tab(self, c): 244 #if c == 10: 245 # out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"]) 246 return 247 248 def run_contacts_tab(self, c): 249 pass