commit 48e53498dbb94e5e1868d2c790bd5ea8cf09b284
parent fbc68d94d666b4a214f3f8abf298a42aed44851b
Author: ThomasV <thomasv@gitorious>
Date: Sun, 7 Jun 2015 18:44:33 +0200
improve requests
Diffstat:
4 files changed, 112 insertions(+), 61 deletions(-)
diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
@@ -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
@@ -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:
@@ -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)))
diff --git a/lib/commands.py b/lib/commands.py
@@ -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
@@ -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 = {}
@@ -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',
@@ -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):
@@ -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)")
diff --git a/lib/paymentrequest.py b/lib/paymentrequest.py
@@ -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()
@@ -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):
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -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
@@ -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):
@@ -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'