improve requests

This commit is contained in:
ThomasV 2015-06-07 18:44:33 +02:00
parent fbc68d94d6
commit 48e53498db
4 changed files with 114 additions and 63 deletions

View File

@ -41,6 +41,7 @@ from electrum import mnemonic
from electrum import util, bitcoin, commands, Wallet from electrum import util, bitcoin, commands, Wallet
from electrum import SimpleConfig, Wallet, WalletStorage from electrum import SimpleConfig, Wallet, WalletStorage
from electrum import Imported_Wallet from electrum import Imported_Wallet
from electrum import paymentrequest
from amountedit import AmountEdit, BTCAmountEdit, MyLineEdit from amountedit import AmountEdit, BTCAmountEdit, MyLineEdit
from network_dialog import NetworkDialog from network_dialog import NetworkDialog
@ -721,8 +722,8 @@ class ElectrumWindow(QMainWindow):
def export_payment_request(self, addr): def export_payment_request(self, addr):
r = self.wallet.get_payment_request(addr) r = self.wallet.get_payment_request(addr)
pr = self.wallet.make_bip70_request(self.config, r) pr = paymentrequest.make_request(self.config, r)
name = 'request.bip70' name = r['key'] + '.bip70'
fileName = self.getSaveFileName(_("Select where to save your payment request"), name, "*.bip70") fileName = self.getSaveFileName(_("Select where to save your payment request"), name, "*.bip70")
if fileName: if fileName:
with open(fileName, "wb+") as f: with open(fileName, "wb+") as f:
@ -804,17 +805,18 @@ class ElectrumWindow(QMainWindow):
# clear the list and fill it again # clear the list and fill it again
self.receive_list.clear() self.receive_list.clear()
for address, req in self.wallet.receive_requests.viewitems(): for req in self.wallet.get_sorted_requests():
timestamp, amount = req['time'], req['amount'] address = req['address']
expiration = req.get('expiration', None)
message = self.wallet.labels.get(address, '')
# only show requests for the current account
if address not in domain: if address not in domain:
continue continue
timestamp = req['time']
amount = req.get('amount')
expiration = req.get('expiration', None)
message = req.get('reason', '')
date = format_time(timestamp) date = format_time(timestamp)
status = req.get('status')
account = self.wallet.get_account_name(self.wallet.get_account_from_address(address)) account = self.wallet.get_account_name(self.wallet.get_account_from_address(address))
amount_str = self.format_amount(amount) if amount else "" 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,'')]) item = QTreeWidgetItem([date, account, address, message, amount_str, pr_tooltips.get(status,'')])
if status is not PR_UNKNOWN: if status is not PR_UNKNOWN:
item.setIcon(5, QIcon(pr_icons.get(status))) item.setIcon(5, QIcon(pr_icons.get(status)))

View File

@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys import sys
import datetime import datetime
import time import time
@ -31,7 +32,7 @@ from util import print_msg, format_satoshis, print_stderr
import bitcoin import bitcoin
from bitcoin import is_address, hash_160_to_bc_address, hash_160, COIN from bitcoin import is_address, hash_160_to_bc_address, hash_160, COIN
from transaction import Transaction from transaction import Transaction
import paymentrequest
known_commands = {} known_commands = {}
@ -516,7 +517,7 @@ class Commands:
"""Decrypt a message encrypted with a public key.""" """Decrypt a message encrypted with a public key."""
return self.wallet.decrypt_message(pubkey, encrypted, self.password) 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 from paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED
pr_str = { pr_str = {
PR_UNKNOWN: 'Unknown', PR_UNKNOWN: 'Unknown',
@ -524,42 +525,66 @@ class Commands:
PR_PAID: 'Paid', PR_PAID: 'Paid',
PR_EXPIRED: 'Expired', PR_EXPIRED: 'Expired',
} }
key = v['key']
addr = v.get('address') addr = v.get('address')
amount = v.get('amount') amount = v.get('amount')
timestamp = v.get('time') timestamp = v.get('time')
expiration = v.get('expiration') expiration = v.get('expiration')
out = { out = {
'key': key,
'address': addr, 'address': addr,
'amount': format_satoshis(amount), 'amount': format_satoshis(amount),
'time': timestamp, 'timestamp': timestamp,
'reason': self.wallet.get_label(addr)[0], 'reason': v.get('reason'),
'expiration': expiration, 'expiration': expiration,
'URI':'bitcoin:' + addr + '?amount=' + format_satoshis(amount),
'status': pr_str[v.get('status', PR_UNKNOWN)]
} }
if v.get('path'): # check if bip70 file exists
url = 'file://' + v.get('path') 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') r = self.config.get('url_rewrite')
if r: if r:
a, b = r a, b = r
url = url.replace(a, b) url = url.replace(a, b)
URI = 'bitcoin:?r=' + url out['request_url'] = url
out['URI'] = URI out['URI'] += '&r=' + url
if show_status:
status = self.wallet.get_request_status(addr, amount, timestamp, expiration)
out['status'] = pr_str[status]
return out return out
@command('w') @command('wn')
def listrequests(self, status=False): def getrequest(self, key):
"""List the payment requests you made, and their status""" """Return a payment request"""
return map(lambda x: self._format_request(x, status), self.wallet.receive_requests.values()) r = self.wallet.get_payment_request(key)
if not r:
raise BaseException("Request not found")
return self._format_request(r)
@command('w') @command('w')
def addrequest(self, requested_amount, reason='', expiration=60*60): def ackrequest(self, serialized):
"""Create a payment request. """<Not implemented>"""
""" pass
@command('w')
def listrequests(self):
"""List the payment requests you made, and their status"""
return map(self._format_request, self.wallet.get_sorted_requests())
@command('w')
def addrequest(self, requested_amount, reason='', expiration=None):
"""Create a payment request."""
amount = int(Decimal(requested_amount)*COIN) amount = int(Decimal(requested_amount)*COIN)
key = self.wallet.add_payment_request(self.config, amount, reason, expiration) key = self.wallet.add_payment_request(amount, reason, expiration)
return self._format_request(self.wallet.get_payment_request(key)) if key else False 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') @command('w')
def rmrequest(self, address): def rmrequest(self, address):
@ -659,6 +684,8 @@ def set_default_subparser(self, name, args=None):
argparse.ArgumentParser.set_default_subparser = set_default_subparser argparse.ArgumentParser.set_default_subparser = set_default_subparser
def add_network_options(parser): 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("-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)") parser.add_argument("-s", "--server", dest="server", default=None, help="set server host:port:protocol, where protocol is either t (tcp) or s (ssl)")

View File

@ -262,13 +262,12 @@ class PaymentRequest:
def make_payment_request(outputs, memo, time, expires, key_path, cert_path): def make_payment_request(outputs, memo, time, expires, key_path, cert_path):
pd = pb2.PaymentDetails() pd = pb2.PaymentDetails()
for script, amount in outputs: for script, amount in outputs:
pd.outputs.add(amount=amount, script=script) pd.outputs.add(amount=amount, script=script)
pd.time = time pd.time = time
pd.expires = expires pd.expires = expires if expires else 0
pd.memo = memo pd.memo = memo
pr = pb2.PaymentRequest() pr = pb2.PaymentRequest()
pr.serialized_payment_details = pd.SerializeToString() 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() 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): class InvoiceStore(object):

View File

@ -1231,25 +1231,22 @@ class Abstract_Wallet(object):
if not self.history.get(addr) and addr not in self.receive_requests.keys(): if not self.history.get(addr) and addr not in self.receive_requests.keys():
return addr 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): 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 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: if amount:
paid = amount <= self.get_addr_received(address) paid = amount <= self.get_addr_received(address)
status = PR_PAID if paid else PR_UNPAID status = PR_PAID if paid else PR_UNPAID
@ -1259,27 +1256,18 @@ class Abstract_Wallet(object):
status = PR_UNKNOWN status = PR_UNKNOWN
return status return status
def save_payment_request(self, config, addr, amount, message, expiration): def save_payment_request(self, addr, amount, message, expiration):
#if addr in self.receive_requests:
# self.receive_requests[addr]['amount'] = amount
self.set_label(addr, message) self.set_label(addr, message)
now = int(time.time()) now = int(time.time())
r = {'time':now, 'amount':amount, 'expiration':expiration, 'address':addr} 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.receive_requests[addr] = r
self.storage.put('receive_requests2', self.receive_requests) 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) addr = self.get_unused_address(None)
if addr is None: if addr is None:
return False return
self.save_payment_request(config, addr, amount, message, expiration) self.save_payment_request(addr, amount, message, expiration)
return addr return addr
def remove_payment_request(self, addr): def remove_payment_request(self, addr):
@ -1289,6 +1277,10 @@ class Abstract_Wallet(object):
self.storage.put('receive_requests2', self.receive_requests) self.storage.put('receive_requests2', self.receive_requests)
return True 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): class Imported_Wallet(Abstract_Wallet):
wallet_type = 'imported' wallet_type = 'imported'