timprove requests - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 48e53498dbb94e5e1868d2c790bd5ea8cf09b284 DIR parent fbc68d94d666b4a214f3f8abf298a42aed44851b HTML Author: ThomasV <thomasv@gitorious> Date: Sun, 7 Jun 2015 18:44:33 +0200 improve requests Diffstat: M gui/qt/main_window.py | 18 ++++++++++-------- M lib/commands.py | 71 +++++++++++++++++++++---------- M lib/paymentrequest.py | 34 +++++++++++++++++++++++++++++-- M lib/wallet.py | 50 +++++++++++++------------------ 4 files changed, 112 insertions(+), 61 deletions(-) --- DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py t@@ -41,6 +41,7 @@ from electrum import mnemonic from electrum import util, bitcoin, commands, Wallet from electrum import SimpleConfig, Wallet, WalletStorage from electrum import Imported_Wallet +from electrum import paymentrequest from amountedit import AmountEdit, BTCAmountEdit, MyLineEdit from network_dialog import NetworkDialog t@@ -721,8 +722,8 @@ class ElectrumWindow(QMainWindow): def export_payment_request(self, addr): r = self.wallet.get_payment_request(addr) - pr = self.wallet.make_bip70_request(self.config, r) - name = 'request.bip70' + pr = paymentrequest.make_request(self.config, r) + name = r['key'] + '.bip70' fileName = self.getSaveFileName(_("Select where to save your payment request"), name, "*.bip70") if fileName: with open(fileName, "wb+") as f: t@@ -804,17 +805,18 @@ class ElectrumWindow(QMainWindow): # clear the list and fill it again self.receive_list.clear() - for address, req in self.wallet.receive_requests.viewitems(): - timestamp, amount = req['time'], req['amount'] - expiration = req.get('expiration', None) - message = self.wallet.labels.get(address, '') - # only show requests for the current account + for req in self.wallet.get_sorted_requests(): + address = req['address'] if address not in domain: continue + timestamp = req['time'] + amount = req.get('amount') + expiration = req.get('expiration', None) + message = req.get('reason', '') date = format_time(timestamp) + status = req.get('status') account = self.wallet.get_account_name(self.wallet.get_account_from_address(address)) amount_str = self.format_amount(amount) if amount else "" - status = self.wallet.get_request_status(address, amount, timestamp, expiration) item = QTreeWidgetItem([date, account, address, message, amount_str, pr_tooltips.get(status,'')]) if status is not PR_UNKNOWN: item.setIcon(5, QIcon(pr_icons.get(status))) DIR diff --git a/lib/commands.py b/lib/commands.py t@@ -16,6 +16,7 @@ # 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 os import sys import datetime import time t@@ -31,7 +32,7 @@ from util import print_msg, format_satoshis, print_stderr import bitcoin from bitcoin import is_address, hash_160_to_bc_address, hash_160, COIN from transaction import Transaction - +import paymentrequest known_commands = {} t@@ -516,7 +517,7 @@ class Commands: """Decrypt a message encrypted with a public key.""" return self.wallet.decrypt_message(pubkey, encrypted, self.password) - def _format_request(self, v, show_status=False): + def _format_request(self, v): from paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED pr_str = { PR_UNKNOWN: 'Unknown', t@@ -524,42 +525,66 @@ class Commands: PR_PAID: 'Paid', PR_EXPIRED: 'Expired', } + key = v['key'] addr = v.get('address') amount = v.get('amount') timestamp = v.get('time') expiration = v.get('expiration') out = { + 'key': key, 'address': addr, 'amount': format_satoshis(amount), - 'time': timestamp, - 'reason': self.wallet.get_label(addr)[0], + 'timestamp': timestamp, + 'reason': v.get('reason'), 'expiration': expiration, + 'URI':'bitcoin:' + addr + '?amount=' + format_satoshis(amount), + 'status': pr_str[v.get('status', PR_UNKNOWN)] } - if v.get('path'): - url = 'file://' + v.get('path') - r = self.config.get('url_rewrite') - if r: - a, b = r - url = url.replace(a, b) - URI = 'bitcoin:?r=' + url - out['URI'] = URI - if show_status: - status = self.wallet.get_request_status(addr, amount, timestamp, expiration) - out['status'] = pr_str[status] + # check if bip70 file exists + rdir = self.config.get('requests_dir') + if rdir: + path = os.path.join(rdir, key + '.bip70') + if os.path.exists(path): + out['path'] = path + url = 'file://' + path + r = self.config.get('url_rewrite') + if r: + a, b = r + url = url.replace(a, b) + out['request_url'] = url + out['URI'] += '&r=' + url + return out + @command('wn') + def getrequest(self, key): + """Return a payment request""" + r = self.wallet.get_payment_request(key) + if not r: + raise BaseException("Request not found") + return self._format_request(r) + @command('w') - def listrequests(self, status=False): + def ackrequest(self, serialized): + """<Not implemented>""" + pass + + @command('w') + def listrequests(self): """List the payment requests you made, and their status""" - return map(lambda x: self._format_request(x, status), self.wallet.receive_requests.values()) + return map(self._format_request, self.wallet.get_sorted_requests()) @command('w') - def addrequest(self, requested_amount, reason='', expiration=60*60): - """Create a payment request. - """ + def addrequest(self, requested_amount, reason='', expiration=None): + """Create a payment request.""" amount = int(Decimal(requested_amount)*COIN) - key = self.wallet.add_payment_request(self.config, amount, reason, expiration) - return self._format_request(self.wallet.get_payment_request(key)) if key else False + key = self.wallet.add_payment_request(amount, reason, expiration) + if key is None: + return + # create file + req = self.wallet.get_payment_request(key) + paymentrequest.publish_request(self.config, key, req) + return self._format_request(req) @command('w') def rmrequest(self, address): t@@ -659,6 +684,8 @@ def set_default_subparser(self, name, args=None): argparse.ArgumentParser.set_default_subparser = set_default_subparser + + def add_network_options(parser): parser.add_argument("-1", "--oneserver", action="store_true", dest="oneserver", default=False, help="connect to one server only") parser.add_argument("-s", "--server", dest="server", default=None, help="set server host:port:protocol, where protocol is either t (tcp) or s (ssl)") DIR diff --git a/lib/paymentrequest.py b/lib/paymentrequest.py t@@ -262,13 +262,12 @@ class PaymentRequest: - def make_payment_request(outputs, memo, time, expires, key_path, cert_path): pd = pb2.PaymentDetails() for script, amount in outputs: pd.outputs.add(amount=amount, script=script) pd.time = time - pd.expires = expires + pd.expires = expires if expires else 0 pd.memo = memo pr = pb2.PaymentRequest() pr.serialized_payment_details = pd.SerializeToString() t@@ -294,6 +293,37 @@ def make_payment_request(outputs, memo, time, expires, key_path, cert_path): return pr.SerializeToString() +def make_request(config, req): + from transaction import Transaction + addr = req['address'] + time = req['time'] + amount = req['amount'] + expiration = req['expiration'] + message = req['reason'] + script = Transaction.pay_script('address', addr).decode('hex') + outputs = [(script, amount)] + key_path = config.get('ssl_privkey') + cert_path = config.get('ssl_chain') + return make_payment_request(outputs, message, time, time + expiration if expiration else None, key_path, cert_path) + + +def publish_request(config, key, req): + import shutil, os + rdir = config.get('requests_dir') + if not rdir: + return + if not os.path.exists(rdir): + os.mkdir(rdir) + index = os.path.join(rdir, 'index.html') + if not os.path.exists(index): + src = os.path.join(os.path.dirname(__file__), 'payrequest.html') + shutil.copy(src, index) + pr = make_request(config, req) + path = os.path.join(rdir, key + '.bip70') + with open(path, 'w') as f: + f.write(pr) + return path + class InvoiceStore(object): DIR diff --git a/lib/wallet.py b/lib/wallet.py t@@ -1231,25 +1231,22 @@ class Abstract_Wallet(object): if not self.history.get(addr) and addr not in self.receive_requests.keys(): return addr - - def make_bip70_request(self, config, req): - from paymentrequest import make_payment_request - addr = req['address'] - time = req['time'] - amount = req['amount'] - expiration = req['expiration'] - message = self.labels.get(addr, '') - script = Transaction.pay_script('address', addr).decode('hex') - outputs = [(script, amount)] - key_path = config.get('ssl_privkey') - cert_path = config.get('ssl_chain') - return make_payment_request(outputs, message, time, time + expiration, key_path, cert_path) - def get_payment_request(self, key): - return self.receive_requests.get(key) + r = self.receive_requests.get(key) + if not r: + return + r['reason'] = self.labels.get(key, '') + r['status'] = self.get_request_status(key) + r['key'] = key + return r - def get_request_status(self, address, amount, timestamp, expiration): + def get_request_status(self, key): from paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED + r = self.receive_requests[key] + address = r['address'] + amount = r.get('amount') + timestamp = r.get('time') + expiration = r.get('expiration') if amount: paid = amount <= self.get_addr_received(address) status = PR_PAID if paid else PR_UNPAID t@@ -1259,27 +1256,18 @@ class Abstract_Wallet(object): status = PR_UNKNOWN return status - def save_payment_request(self, config, addr, amount, message, expiration): - #if addr in self.receive_requests: - # self.receive_requests[addr]['amount'] = amount + def save_payment_request(self, addr, amount, message, expiration): self.set_label(addr, message) now = int(time.time()) r = {'time':now, 'amount':amount, 'expiration':expiration, 'address':addr} - rdir = config.get('requests_dir') - if rdir: - pr = self.make_bip70_request(config, r) - path = os.path.join(rdir, addr + '.bip70') - with open(path, 'w') as f: - f.write(pr) - r['path'] = path self.receive_requests[addr] = r self.storage.put('receive_requests2', self.receive_requests) - def add_payment_request(self, config, amount, message, expiration): + def add_payment_request(self, amount, message, expiration): addr = self.get_unused_address(None) if addr is None: - return False - self.save_payment_request(config, addr, amount, message, expiration) + return + self.save_payment_request(addr, amount, message, expiration) return addr def remove_payment_request(self, addr): t@@ -1289,6 +1277,10 @@ class Abstract_Wallet(object): self.storage.put('receive_requests2', self.receive_requests) return True + def get_sorted_requests(self): + return sorted(map(self.get_payment_request, self.receive_requests.keys()), key=itemgetter('time')) + + class Imported_Wallet(Abstract_Wallet): wallet_type = 'imported'