Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
c41fe53fd2
|
@ -14,3 +14,4 @@ locale/
|
|||
.devlocaltmp/
|
||||
*_trial_temp
|
||||
packages
|
||||
env/
|
||||
|
|
2
electrum
2
electrum
|
@ -120,7 +120,7 @@ def arg_parser():
|
|||
parser.add_option("-p", "--proxy", dest="proxy", default=None, help="set proxy [type:]host[:port], where type is socks4,socks5 or http")
|
||||
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="show debugging information")
|
||||
parser.add_option("-P", "--portable", action="store_true", dest="portable", default=False, help="portable wallet")
|
||||
parser.add_option("-L", "--lang", dest="language", default=None, help="defaut language used in GUI")
|
||||
parser.add_option("-L", "--lang", dest="language", default=None, help="default language used in GUI")
|
||||
parser.add_option("-G", "--gap", dest="gap_limit", default=None, help="gap limit")
|
||||
parser.add_option("-W", "--password", dest="password", default=None, help="set password for usage with commands (currently only implemented for create command, do not use it for longrunning gui session since the password is visible in /proc)")
|
||||
parser.add_option("-1", "--oneserver", action="store_true", dest="oneserver", default=False, help="connect to one server only")
|
||||
|
|
|
@ -17,8 +17,8 @@ else
|
|||
python setup.py install
|
||||
fi
|
||||
|
||||
export PYTHONPATH=/usr/local/lib/python2.7/site-packages:$PYTHONPATH
|
||||
export PYTHONPATH="/usr/local/lib/python2.7/site-packages:$PYTHONPATH"
|
||||
|
||||
./electrum
|
||||
./electrum "$@"
|
||||
|
||||
deactivate
|
||||
|
|
|
@ -13,5 +13,5 @@ Categories=Network;
|
|||
StartupNotify=false
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=x-scheme-handler/bitcoin
|
||||
MimeType=x-scheme-handler/bitcoin;
|
||||
|
||||
|
|
|
@ -296,6 +296,8 @@ class ElectrumWindow(QMainWindow):
|
|||
wallet.start_threads(self.network)
|
||||
# load new wallet in gui
|
||||
self.load_wallet(wallet)
|
||||
# save path
|
||||
self.config.set_key('default_wallet_path', filename)
|
||||
|
||||
|
||||
|
||||
|
@ -577,7 +579,7 @@ class ElectrumWindow(QMainWindow):
|
|||
elif be == 'Insight.is':
|
||||
block_explorer = 'http://live.insight.is/tx/'
|
||||
elif be == "Blocktrail.com":
|
||||
block_explorer = 'https://www.blocktrail.com/tx/'
|
||||
block_explorer = 'https://www.blocktrail.com/BTC/tx/'
|
||||
|
||||
if not item: return
|
||||
tx_hash = str(item.data(0, Qt.UserRole).toString())
|
||||
|
@ -1907,39 +1909,44 @@ class ElectrumWindow(QMainWindow):
|
|||
dialog.setModal(1)
|
||||
dialog.setWindowTitle(_("Master Public Keys"))
|
||||
|
||||
main_layout = QGridLayout()
|
||||
mpk_dict = self.wallet.get_master_public_keys()
|
||||
# filter out the empty keys (PendingAccount)
|
||||
mpk_dict = {acc:mpk for acc,mpk in mpk_dict.items() if mpk}
|
||||
|
||||
vbox = QVBoxLayout()
|
||||
# only show the combobox in case multiple accounts are available
|
||||
if len(mpk_dict) > 1:
|
||||
combobox = QComboBox()
|
||||
for name in mpk_dict:
|
||||
combobox.addItem(name)
|
||||
combobox.setCurrentIndex(0)
|
||||
main_layout.addWidget(combobox, 1, 0)
|
||||
gb = QGroupBox(_("Master Public Keys"))
|
||||
vbox.addWidget(gb)
|
||||
group = QButtonGroup()
|
||||
first_button = None
|
||||
for name in sorted(mpk_dict.keys()):
|
||||
b = QRadioButton(gb)
|
||||
b.setText(name)
|
||||
group.addButton(b)
|
||||
vbox.addWidget(b)
|
||||
if not first_button:
|
||||
first_button = b
|
||||
|
||||
account = unicode(combobox.currentText())
|
||||
mpk_text = ShowQRTextEdit(text=mpk_dict[account])
|
||||
mpk_text = ShowQRTextEdit()
|
||||
mpk_text.setMaximumHeight(170)
|
||||
mpk_text.selectAll() # for easy copying
|
||||
main_layout.addWidget(mpk_text, 2, 0)
|
||||
vbox.addWidget(mpk_text)
|
||||
|
||||
def show_mpk(account):
|
||||
mpk = mpk_dict.get(unicode(account), "")
|
||||
def show_mpk(b):
|
||||
name = str(b.text())
|
||||
mpk = mpk_dict.get(name, "")
|
||||
mpk_text.setText(mpk)
|
||||
mpk_text.selectAll() # for easy copying
|
||||
|
||||
combobox.currentIndexChanged[str].connect(lambda acc: show_mpk(acc))
|
||||
group.buttonReleased.connect(show_mpk)
|
||||
first_button.setChecked(True)
|
||||
show_mpk(first_button)
|
||||
|
||||
#combobox.currentIndexChanged[str].connect(lambda acc: show_mpk(acc))
|
||||
elif len(mpk_dict) == 1:
|
||||
mpk = mpk_dict.values()[0]
|
||||
mpk_text = ShowQRTextEdit(text=mpk)
|
||||
mpk_text.setMaximumHeight(170)
|
||||
mpk_text.selectAll() # for easy copying
|
||||
main_layout.addWidget(mpk_text, 2, 0)
|
||||
vbox.addWidget(mpk_text)
|
||||
|
||||
vbox = QVBoxLayout()
|
||||
vbox.addLayout(main_layout)
|
||||
vbox.addLayout(close_button(dialog))
|
||||
|
||||
dialog.setLayout(vbox)
|
||||
|
|
|
@ -201,9 +201,12 @@ class NetworkDialog(QDialog):
|
|||
def change_server(self, host, protocol):
|
||||
|
||||
pp = self.servers.get(host, DEFAULT_PORTS)
|
||||
if protocol and protocol not in protocol_letters:
|
||||
protocol = None
|
||||
if protocol:
|
||||
port = pp.get(protocol)
|
||||
if not port: protocol = None
|
||||
if port is None:
|
||||
protocol = None
|
||||
|
||||
if not protocol:
|
||||
if 's' in pp.keys():
|
||||
|
@ -217,15 +220,6 @@ class NetworkDialog(QDialog):
|
|||
self.server_port.setText( port )
|
||||
self.server_protocol.setCurrentIndex(protocol_letters.index(protocol))
|
||||
|
||||
if not self.servers: return
|
||||
for p in protocol_letters:
|
||||
i = protocol_letters.index(p)
|
||||
j = self.server_protocol.model().index(i,0)
|
||||
#if p not in pp.keys(): # and self.interface.is_connected:
|
||||
# self.server_protocol.model().setData(j, QVariant(0), Qt.UserRole-1)
|
||||
#else:
|
||||
# self.server_protocol.model().setData(j, QVariant(33), Qt.UserRole-1)
|
||||
|
||||
|
||||
def do_exec(self):
|
||||
|
||||
|
|
|
@ -368,7 +368,7 @@ class Network(threading.Thread):
|
|||
out['result'] = f(*params)
|
||||
except BaseException as e:
|
||||
out['error'] = str(e)
|
||||
traceback.print_exc(file=sys.stout)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print_error("network error", str(e))
|
||||
|
||||
self.response_queue.put(out)
|
||||
|
|
|
@ -43,42 +43,8 @@ import x509
|
|||
REQUEST_HEADERS = {'Accept': 'application/bitcoin-paymentrequest', 'User-Agent': 'Electrum'}
|
||||
ACK_HEADERS = {'Content-Type':'application/bitcoin-payment','Accept':'application/bitcoin-paymentack','User-Agent':'Electrum'}
|
||||
|
||||
|
||||
ca_list = {}
|
||||
ca_path = requests.certs.where()
|
||||
|
||||
|
||||
|
||||
|
||||
def load_certificates():
|
||||
try:
|
||||
ca_f = open(ca_path, 'r')
|
||||
except Exception:
|
||||
print "ERROR: Could not open %s"%ca_path
|
||||
print "ca-bundle.crt file should be placed in ~/.electrum/ca/ca-bundle.crt"
|
||||
print "Documentation on how to download or create the file here: http://curl.haxx.se/docs/caextract.html"
|
||||
print "Payment will continue with manual verification."
|
||||
return False
|
||||
c = ""
|
||||
for line in ca_f:
|
||||
if line == "-----BEGIN CERTIFICATE-----\n":
|
||||
c = line
|
||||
else:
|
||||
c += line
|
||||
if line == "-----END CERTIFICATE-----\n":
|
||||
x = x509.X509()
|
||||
try:
|
||||
x.parse(c)
|
||||
except Exception as e:
|
||||
util.print_error("cannot parse cert:", e)
|
||||
continue
|
||||
ca_list[x.getFingerprint()] = x
|
||||
ca_f.close()
|
||||
util.print_error("%d certificates"%len(ca_list))
|
||||
return True
|
||||
|
||||
load_certificates()
|
||||
|
||||
ca_list = x509.load_certificates(ca_path)
|
||||
|
||||
|
||||
class PaymentRequest:
|
||||
|
@ -190,8 +156,13 @@ class PaymentRequest:
|
|||
verify = pubkey.hashAndVerify(sig, data)
|
||||
elif algo.getComponentByName('algorithm') == x509.ALGO_RSA_SHA256:
|
||||
hashBytes = bytearray(hashlib.sha256(data).digest())
|
||||
prefixBytes = bytearray([0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20])
|
||||
verify = pubkey.verify(sig, prefixBytes + hashBytes)
|
||||
verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA256 + hashBytes)
|
||||
elif algo.getComponentByName('algorithm') == x509.ALGO_RSA_SHA384:
|
||||
hashBytes = bytearray(hashlib.sha384(data).digest())
|
||||
verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA384 + hashBytes)
|
||||
elif algo.getComponentByName('algorithm') == x509.ALGO_RSA_SHA512:
|
||||
hashBytes = bytearray(hashlib.sha512(data).digest())
|
||||
verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA512 + hashBytes)
|
||||
else:
|
||||
self.error = "Algorithm not supported"
|
||||
util.print_error(self.error, algo.getComponentByName('algorithm'))
|
||||
|
@ -226,8 +197,7 @@ class PaymentRequest:
|
|||
|
||||
if paymntreq.pki_type == "x509+sha256":
|
||||
hashBytes = bytearray(hashlib.sha256(msgBytes).digest())
|
||||
prefixBytes = bytearray([0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20])
|
||||
verify = pubkey0.verify(sigBytes, prefixBytes + hashBytes)
|
||||
verify = pubkey0.verify(sigBytes, x509.PREFIX_RSA_SHA256 + hashBytes)
|
||||
elif paymntreq.pki_type == "x509+sha1":
|
||||
verify = pubkey0.hashAndVerify(sigBytes, msgBytes)
|
||||
else:
|
||||
|
@ -321,7 +291,6 @@ class PaymentRequest:
|
|||
if __name__ == "__main__":
|
||||
|
||||
util.set_verbosity(True)
|
||||
load_certificates()
|
||||
|
||||
try:
|
||||
uri = sys.argv[1]
|
||||
|
|
|
@ -40,7 +40,7 @@ def hook(func):
|
|||
|
||||
|
||||
def run_hook(name, *args):
|
||||
SPECIAL_HOOKS = ['get_wizard_action']
|
||||
SPECIAL_HOOKS = ['get_wizard_action','installwizard_restore']
|
||||
results = []
|
||||
f_list = hooks.get(name,[])
|
||||
for p, f in f_list:
|
||||
|
@ -71,6 +71,7 @@ class BasePlugin:
|
|||
def __init__(self, config, name):
|
||||
self.name = name
|
||||
self.config = config
|
||||
self.wallet = None
|
||||
# add self to hooks
|
||||
for k in dir(self):
|
||||
if k in hook_names:
|
||||
|
|
|
@ -67,7 +67,7 @@ class WalletStorage(object):
|
|||
|
||||
# path in config file
|
||||
path = config.get('default_wallet_path')
|
||||
if path:
|
||||
if path and os.path.exists(path):
|
||||
return path
|
||||
|
||||
# default path
|
||||
|
@ -1494,7 +1494,7 @@ class Wallet_2of2(BIP32_Wallet, Mnemonic):
|
|||
def get_master_public_keys(self):
|
||||
xpub1 = self.master_public_keys.get("x1/")
|
||||
xpub2 = self.master_public_keys.get("x2/")
|
||||
return {'x1':xpub1, 'x2':xpub2}
|
||||
return { 'Self':xpub1, 'Cosigner':xpub2 }
|
||||
|
||||
def get_action(self):
|
||||
xpub1 = self.master_public_keys.get("x1/")
|
||||
|
|
29
lib/x509.py
29
lib/x509.py
|
@ -23,6 +23,7 @@ import sys
|
|||
import pyasn1
|
||||
import pyasn1_modules
|
||||
import tlslite
|
||||
import util
|
||||
|
||||
# workaround https://github.com/trevp/tlslite/issues/15
|
||||
tlslite.utils.cryptomath.pycryptoLoaded = False
|
||||
|
@ -41,8 +42,18 @@ from pyasn1_modules.rfc2459 import id_at_organizationalUnitName as OU_NAME
|
|||
from pyasn1_modules.rfc2459 import id_ce_basicConstraints, BasicConstraints
|
||||
XMPP_ADDR = ObjectIdentifier('1.3.6.1.5.5.7.8.5')
|
||||
SRV_NAME = ObjectIdentifier('1.3.6.1.5.5.7.8.7')
|
||||
|
||||
# algo OIDs
|
||||
ALGO_RSA_SHA1 = ObjectIdentifier('1.2.840.113549.1.1.5')
|
||||
ALGO_RSA_SHA256 = ObjectIdentifier('1.2.840.113549.1.1.11')
|
||||
ALGO_RSA_SHA384 = ObjectIdentifier('1.2.840.113549.1.1.12')
|
||||
ALGO_RSA_SHA512 = ObjectIdentifier('1.2.840.113549.1.1.13')
|
||||
|
||||
# prefixes, see http://stackoverflow.com/questions/3713774/c-sharp-how-to-calculate-asn-1-der-encoding-of-a-particular-hash-algorithm
|
||||
PREFIX_RSA_SHA256 = bytearray([0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20])
|
||||
PREFIX_RSA_SHA384 = bytearray([0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30])
|
||||
PREFIX_RSA_SHA512 = bytearray([0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40])
|
||||
|
||||
|
||||
class CertificateError(Exception):
|
||||
pass
|
||||
|
@ -214,3 +225,21 @@ class X509(tlslite.X509):
|
|||
|
||||
class X509CertChain(tlslite.X509CertChain):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
def load_certificates(ca_path):
|
||||
ca_list = {}
|
||||
with open(ca_path, 'r') as f:
|
||||
s = f.read()
|
||||
bList = tlslite.utils.pem.dePemList(s, "CERTIFICATE")
|
||||
for b in bList:
|
||||
x = X509()
|
||||
try:
|
||||
x.parseBinary(b)
|
||||
except Exception as e:
|
||||
util.print_error("cannot parse cert:", e)
|
||||
continue
|
||||
ca_list[x.getFingerprint()] = x
|
||||
return ca_list
|
||||
|
|
|
@ -15,8 +15,7 @@ import platform
|
|||
|
||||
try:
|
||||
import amodem.audio
|
||||
import amodem.recv
|
||||
import amodem.send
|
||||
import amodem.main
|
||||
import amodem.config
|
||||
print_error('Audio MODEM is available.')
|
||||
amodem.log.addHandler(amodem.logging.StreamHandler(sys.stderr))
|
||||
|
@ -115,7 +114,7 @@ class Plugin(BasePlugin):
|
|||
with self._audio_interface() as interface:
|
||||
src = BytesIO(blob)
|
||||
dst = interface.player()
|
||||
amodem.send.main(config=self.modem_config, src=src, dst=dst)
|
||||
amodem.main.send(config=self.modem_config, src=src, dst=dst)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
|
@ -132,7 +131,7 @@ class Plugin(BasePlugin):
|
|||
with self._audio_interface() as interface:
|
||||
src = interface.recorder()
|
||||
dst = BytesIO()
|
||||
amodem.recv.main(config=self.modem_config, src=src, dst=dst)
|
||||
amodem.main.recv(config=self.modem_config, src=src, dst=dst)
|
||||
return dst.getvalue()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
|
|
@ -17,6 +17,7 @@ from electrum_gui.qt.amountedit import AmountEdit
|
|||
|
||||
EXCHANGES = ["BitcoinAverage",
|
||||
"BitcoinVenezuela",
|
||||
"BTCParalelo",
|
||||
"Bitcurex",
|
||||
"Bitmarket",
|
||||
"BitPay",
|
||||
|
@ -63,6 +64,26 @@ class Exchanger(threading.Thread):
|
|||
except Exception:
|
||||
raise
|
||||
return json_resp
|
||||
|
||||
def get_json_insecure(self, site, get_string):
|
||||
""" get_json_insecure shouldn't be used in production releases
|
||||
It doesn't use SSL, and so prices could be manipulated by a middle man
|
||||
This should be used ONLY when developing plugins when you don't have a
|
||||
SSL certificate that validates against HTTPSConnection
|
||||
"""
|
||||
try:
|
||||
connection = httplib.HTTPConnection(site)
|
||||
connection.request("GET", get_string, headers={"User-Agent":"Electrum"})
|
||||
except Exception:
|
||||
raise
|
||||
resp = connection.getresponse()
|
||||
if resp.reason == httplib.responses[httplib.NOT_FOUND]:
|
||||
raise
|
||||
try:
|
||||
json_resp = json.loads(resp.read())
|
||||
except Exception:
|
||||
raise
|
||||
return json_resp
|
||||
|
||||
|
||||
def exchange(self, btc_amount, quote_currency):
|
||||
|
@ -82,6 +103,7 @@ class Exchanger(threading.Thread):
|
|||
update_rates = {
|
||||
"BitcoinAverage": self.update_ba,
|
||||
"BitcoinVenezuela": self.update_bv,
|
||||
"BTCParalelo": self.update_bpl,
|
||||
"Bitcurex": self.update_bx,
|
||||
"Bitmarket": self.update_bm,
|
||||
"BitPay": self.update_bp,
|
||||
|
@ -110,6 +132,9 @@ class Exchanger(threading.Thread):
|
|||
def update_cd(self):
|
||||
try:
|
||||
resp_currencies = self.get_json('api.coindesk.com', "/v1/bpi/supported-currencies.json")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing coindesk")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
@ -138,6 +163,9 @@ class Exchanger(threading.Thread):
|
|||
try:
|
||||
resp_rate = self.get_json('api.itbit.com', "/v1/markets/XBT" + str(current_cur) + "/ticker")
|
||||
quote_currencies[str(current_cur)] = decimal.Decimal(str(resp_rate["lastPrice"]))
|
||||
except SSLError:
|
||||
print("SSL Error when accesing itbit")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
with self.lock:
|
||||
|
@ -147,6 +175,9 @@ class Exchanger(threading.Thread):
|
|||
def update_wd(self):
|
||||
try:
|
||||
winkresp = self.get_json('winkdex.com', "/api/v0/price")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing winkdex")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {"USD": 0.0}
|
||||
|
@ -162,6 +193,9 @@ class Exchanger(threading.Thread):
|
|||
def update_cv(self):
|
||||
try:
|
||||
jsonresp = self.get_json('www.cavirtex.com', "/api/CAD/ticker.json")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing cavirtex")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {"CAD": 0.0}
|
||||
|
@ -177,6 +211,9 @@ class Exchanger(threading.Thread):
|
|||
def update_bm(self):
|
||||
try:
|
||||
jsonresp = self.get_json('www.bitmarket.pl', "/json/BTCPLN/ticker.json")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing bitmarket")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {"PLN": 0.0}
|
||||
|
@ -192,6 +229,9 @@ class Exchanger(threading.Thread):
|
|||
def update_bx(self):
|
||||
try:
|
||||
jsonresp = self.get_json('pln.bitcurex.com', "/data/ticker.json")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing bitcurex")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {"PLN": 0.0}
|
||||
|
@ -207,6 +247,9 @@ class Exchanger(threading.Thread):
|
|||
def update_CNY(self):
|
||||
try:
|
||||
jsonresp = self.get_json('data.btcchina.com', "/data/ticker")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing btcchina")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {"CNY": 0.0}
|
||||
|
@ -222,6 +265,9 @@ class Exchanger(threading.Thread):
|
|||
def update_bp(self):
|
||||
try:
|
||||
jsonresp = self.get_json('bitpay.com', "/api/rates")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing bitpay")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {}
|
||||
|
@ -237,6 +283,9 @@ class Exchanger(threading.Thread):
|
|||
def update_cb(self):
|
||||
try:
|
||||
jsonresp = self.get_json('coinbase.com', "/api/v1/currencies/exchange_rates")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing coinbase")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
@ -255,6 +304,9 @@ class Exchanger(threading.Thread):
|
|||
def update_bc(self):
|
||||
try:
|
||||
jsonresp = self.get_json('blockchain.info', "/ticker")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing blockchain")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {}
|
||||
|
@ -270,6 +322,9 @@ class Exchanger(threading.Thread):
|
|||
def update_lb(self):
|
||||
try:
|
||||
jsonresp = self.get_json('localbitcoins.com', "/bitcoinaverage/ticker-all-currencies/")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing localbitcoins")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {}
|
||||
|
@ -285,23 +340,52 @@ class Exchanger(threading.Thread):
|
|||
|
||||
def update_bv(self):
|
||||
try:
|
||||
jsonresp = self.get_json('api.bitcoinvenezuela.com', "/")
|
||||
jsonresp = self.get_json_insecure('api.bitcoinvenezuela.com', "/")
|
||||
print("**WARNING**: update_bv is using an insecure connection, shouldn't be used on production")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing bitcoinvenezuela")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
|
||||
quote_currencies = {}
|
||||
try:
|
||||
for r in jsonresp["BTC"]:
|
||||
quote_currencies[r] = Decimal(jsonresp["BTC"][r])
|
||||
|
||||
with self.lock:
|
||||
self.quote_currencies = quote_currencies
|
||||
except KeyError:
|
||||
pass
|
||||
print ("KeyError")
|
||||
self.parent.set_currencies(quote_currencies)
|
||||
|
||||
|
||||
|
||||
def update_bpl(self):
|
||||
try:
|
||||
jsonresp = self.get_json_insecure('btcparalelo.com', "/api/price")
|
||||
print("**WARNING**: update_bpl is using an insecure connection, shouldn't be used on production")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing btcparalelo")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
quote_currencies = {}
|
||||
try:
|
||||
quote_currencies = {"VEF": Decimal(jsonresp["price"])}
|
||||
with self.lock:
|
||||
self.quote_currencies = quote_currencies
|
||||
except KeyError:
|
||||
print ("KeyError")
|
||||
self.parent.set_currencies(quote_currencies)
|
||||
|
||||
def update_ba(self):
|
||||
try:
|
||||
jsonresp = self.get_json('api.bitcoinaverage.com', "/ticker/global/all")
|
||||
except SSLError:
|
||||
print("SSL Error when accesing bitcoinaverage")
|
||||
return
|
||||
except Exception:
|
||||
return
|
||||
quote_currencies = {}
|
||||
|
@ -411,7 +495,6 @@ class Plugin(BasePlugin):
|
|||
|
||||
@hook
|
||||
def load_wallet(self, wallet):
|
||||
self.wallet = wallet
|
||||
tx_list = {}
|
||||
for item in self.wallet.get_tx_history(self.wallet.storage.get("current_account", None)):
|
||||
tx_hash, conf, is_mine, value, fee, balance, timestamp = item
|
||||
|
@ -471,6 +554,8 @@ class Plugin(BasePlugin):
|
|||
return
|
||||
if not self.resp_hist:
|
||||
return
|
||||
if not self.wallet:
|
||||
return
|
||||
|
||||
self.win.is_edit = True
|
||||
self.win.history_list.setColumnCount(6)
|
||||
|
|
|
@ -96,6 +96,7 @@ class Plugin(BasePlugin):
|
|||
print_error("trezor: clear session")
|
||||
if self.wallet and self.wallet.client:
|
||||
self.wallet.client.clear_session()
|
||||
self.wallet.client.transport.close()
|
||||
|
||||
@hook
|
||||
def load_wallet(self, wallet):
|
||||
|
@ -131,6 +132,10 @@ class Plugin(BasePlugin):
|
|||
self.wallet.trezor_sign(tx)
|
||||
except Exception as e:
|
||||
tx.error = str(e)
|
||||
@hook
|
||||
def receive_menu(self, menu, addrs):
|
||||
if not self.wallet.is_watching_only() and self.wallet.atleast_version(1, 3) and len(addrs) == 1:
|
||||
menu.addAction(_("Show on TREZOR"), lambda: self.wallet.show_address(addrs[0]))
|
||||
|
||||
def settings_widget(self, window):
|
||||
return EnterButton(_('Settings'), self.settings_dialog)
|
||||
|
@ -212,15 +217,22 @@ class TrezorWallet(BIP32_HD_Wallet):
|
|||
except:
|
||||
give_error('Could not connect to your Trezor. Please verify the cable is connected and that no other app is using it.')
|
||||
self.client = QtGuiTrezorClient(self.transport)
|
||||
if (self.client.features.major_version == 1 and self.client.features.minor_version < 2) or (self.client.features.major_version == 1 and self.client.features.minor_version == 2 and self.client.features.patch_version < 1):
|
||||
give_error('Outdated Trezor firmware. Please update the firmware from https://www.mytrezor.com')
|
||||
self.client.set_tx_api(self)
|
||||
#self.client.clear_session()# TODO Doesn't work with firmware 1.1, returns proto.Failure
|
||||
self.client.bad = False
|
||||
self.device_checked = False
|
||||
self.proper_device = False
|
||||
if not self.atleast_version(1, 2, 1):
|
||||
give_error('Outdated Trezor firmware. Please update the firmware from https://www.mytrezor.com')
|
||||
return self.client
|
||||
|
||||
def compare_version(self, major, minor=0, patch=0):
|
||||
features = self.get_client().features
|
||||
return cmp([features.major_version, features.minor_version, features.patch_version], [major, minor, patch])
|
||||
|
||||
def atleast_version(self, major, minor=0, patch=0):
|
||||
return self.compare_version(major, minor, patch) >= 0
|
||||
|
||||
def address_id(self, address):
|
||||
account_id, (change, address_index) = self.get_address_index(address)
|
||||
return "44'/0'/%s'/%d/%d" % (account_id, change, address_index)
|
||||
|
@ -277,6 +289,21 @@ class TrezorWallet(BIP32_HD_Wallet):
|
|||
# twd.emit(SIGNAL('trezor_done'))
|
||||
#return str(decrypted_msg)
|
||||
|
||||
def show_address(self, address):
|
||||
if not self.check_proper_device():
|
||||
give_error('Wrong device or password')
|
||||
try:
|
||||
address_path = self.address_id(address)
|
||||
address_n = self.get_client().expand_path(address_path)
|
||||
except Exception, e:
|
||||
give_error(e)
|
||||
try:
|
||||
self.get_client().get_address('Bitcoin', address_n, True)
|
||||
except Exception, e:
|
||||
give_error(e)
|
||||
finally:
|
||||
twd.emit(SIGNAL('trezor_done'))
|
||||
|
||||
def sign_message(self, address, message, password):
|
||||
if not self.check_proper_device():
|
||||
give_error('Wrong device or password')
|
||||
|
@ -426,6 +453,8 @@ class TrezorQtGuiMixin(object):
|
|||
message = "Confirm transaction fee on Trezor device to continue"
|
||||
elif msg.code == 7:
|
||||
message = "Confirm message to sign on Trezor device to continue"
|
||||
elif msg.code == 10:
|
||||
message = "Confirm address on Trezor device to continue"
|
||||
else:
|
||||
message = "Check Trezor device to continue"
|
||||
twd.start(message)
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# create a BIP70 payment request signed with a certificate
|
||||
|
||||
import tlslite
|
||||
import time
|
||||
import hashlib
|
||||
|
||||
from electrum import paymentrequest_pb2 as pb2
|
||||
from electrum.transaction import Transaction
|
||||
from electrum import bitcoin
|
||||
from electrum import x509
|
||||
|
||||
|
||||
chain_file = 'mychain.pem'
|
||||
cert_file = 'mycert.pem'
|
||||
amount = 1000000
|
||||
address = "18U5kpCAU4s8weFF8Ps5n8HAfpdUjDVF64"
|
||||
memo = "blah"
|
||||
out_file = "payreq"
|
||||
|
||||
|
||||
with open(chain_file, 'r') as f:
|
||||
chain = tlslite.X509CertChain()
|
||||
chain.parsePemList(f.read())
|
||||
|
||||
certificates = pb2.X509Certificates()
|
||||
certificates.certificate.extend(map(lambda x: str(x.bytes), chain.x509List))
|
||||
|
||||
with open(cert_file, 'r') as f:
|
||||
rsakey = tlslite.utils.python_rsakey.Python_RSAKey.parsePEM(f.read())
|
||||
|
||||
|
||||
def make_payment_request(amount, script, memo):
|
||||
"""Generates a http PaymentRequest object"""
|
||||
pd = pb2.PaymentDetails()
|
||||
pd.outputs.add(amount=amount, script=script)
|
||||
now = int(time.time())
|
||||
pd.time = now
|
||||
pd.expires = now + 15*60
|
||||
pd.memo = memo
|
||||
pd.payment_url = 'http://payment_ack.url'
|
||||
pr = pb2.PaymentRequest()
|
||||
pr.serialized_payment_details = pd.SerializeToString()
|
||||
pr.pki_type = 'x509+sha256'
|
||||
pr.pki_data = certificates.SerializeToString()
|
||||
pr.signature = ''
|
||||
msgBytes = bytearray(pr.SerializeToString())
|
||||
hashBytes = bytearray(hashlib.sha256(msgBytes).digest())
|
||||
sig = rsakey.sign(x509.PREFIX_RSA_SHA256 + hashBytes)
|
||||
pr.signature = bytes(sig)
|
||||
return pr.SerializeToString()
|
||||
|
||||
|
||||
script = Transaction.pay_script('address', address).decode('hex')
|
||||
|
||||
pr_string = make_payment_request(amount, script, memo)
|
||||
with open(out_file,'wb') as f:
|
||||
f.write(pr_string)
|
||||
|
||||
print "Payment request was written to file '%s'"%out_file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
import util, json
|
||||
peers = util.get_peers()
|
||||
results = util.send_request(peers, {'method':'blockchain.estimatefee','params':[1]})
|
||||
print json.dumps(results, indent=4)
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue