Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c26cc844f5 | ||
|
|
2be9d0a25a | ||
|
|
d0f9e20cf7 | ||
|
|
64d998fae8 | ||
|
|
387f6db3b6 | ||
|
|
af0715e476 |
@ -1,3 +1,7 @@
|
|||||||
|
# Release 2.9.4 (security update)
|
||||||
|
* Backport security fixes from 3.0.5 after vulnerability was
|
||||||
|
discovered in JSONRPC interface.
|
||||||
|
|
||||||
# Release 2.9.3
|
# Release 2.9.3
|
||||||
* fix configuration file issue #2719
|
* fix configuration file issue #2719
|
||||||
* fix ledger signing of non-RBF transactions
|
* fix ledger signing of non-RBF transactions
|
||||||
|
|||||||
4
electrum
4
electrum
@ -372,7 +372,7 @@ if __name__ == '__main__':
|
|||||||
fd, server = daemon.get_fd_or_server(config)
|
fd, server = daemon.get_fd_or_server(config)
|
||||||
if fd is not None:
|
if fd is not None:
|
||||||
plugins = init_plugins(config, config.get('gui', 'qt'))
|
plugins = init_plugins(config, config.get('gui', 'qt'))
|
||||||
d = daemon.Daemon(config, fd)
|
d = daemon.Daemon(config, fd, True)
|
||||||
d.start()
|
d.start()
|
||||||
d.init_gui(config, plugins)
|
d.init_gui(config, plugins)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -393,7 +393,7 @@ if __name__ == '__main__':
|
|||||||
print_stderr("starting daemon (PID %d)" % pid)
|
print_stderr("starting daemon (PID %d)" % pid)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
init_plugins(config, 'cmdline')
|
init_plugins(config, 'cmdline')
|
||||||
d = daemon.Daemon(config, fd)
|
d = daemon.Daemon(config, fd, False)
|
||||||
d.start()
|
d.start()
|
||||||
if config.get('websocket_server'):
|
if config.get('websocket_server'):
|
||||||
from electrum import websockets
|
from electrum import websockets
|
||||||
|
|||||||
@ -168,7 +168,12 @@ class ElectrumGui:
|
|||||||
w.bring_to_top()
|
w.bring_to_top()
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
wallet = self.daemon.load_wallet(path, None)
|
try:
|
||||||
|
wallet = self.daemon.load_wallet(path, None)
|
||||||
|
except BaseException as e:
|
||||||
|
d = QMessageBox(QMessageBox.Warning, _('Error'), 'Cannot load wallet:\n' + str(e))
|
||||||
|
d.exec_()
|
||||||
|
return
|
||||||
if not wallet:
|
if not wallet:
|
||||||
storage = WalletStorage(path)
|
storage = WalletStorage(path)
|
||||||
wizard = InstallWizard(self.config, self.app, self.plugins, storage)
|
wizard = InstallWizard(self.config, self.app, self.plugins, storage)
|
||||||
|
|||||||
@ -29,12 +29,12 @@ import sys
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import jsonrpclib
|
import jsonrpclib
|
||||||
from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer, SimpleJSONRPCRequestHandler
|
from .jsonrpc import VerifyingJSONRPCServer
|
||||||
|
|
||||||
from version import ELECTRUM_VERSION
|
from version import ELECTRUM_VERSION
|
||||||
from network import Network
|
from network import Network
|
||||||
from util import json_decode, DaemonThread
|
from util import (json_decode, DaemonThread, print_msg, print_error,
|
||||||
from util import print_msg, print_error, print_stderr, UserCancelled
|
print_stderr, UserCancelled, to_string, int_to_bytes)
|
||||||
from wallet import Wallet
|
from wallet import Wallet
|
||||||
from storage import WalletStorage
|
from storage import WalletStorage
|
||||||
from commands import known_commands, Commands
|
from commands import known_commands, Commands
|
||||||
@ -73,7 +73,14 @@ def get_server(config):
|
|||||||
try:
|
try:
|
||||||
with open(lockfile) as f:
|
with open(lockfile) as f:
|
||||||
(host, port), create_time = ast.literal_eval(f.read())
|
(host, port), create_time = ast.literal_eval(f.read())
|
||||||
server = jsonrpclib.Server('http://%s:%d' % (host, port))
|
rpc_user, rpc_password = get_rpc_credentials(config)
|
||||||
|
if rpc_password == '':
|
||||||
|
# authentication disabled
|
||||||
|
server_url = 'http://%s:%d' % (host, port)
|
||||||
|
else:
|
||||||
|
server_url = 'http://%s:%s@%s:%d' % (
|
||||||
|
rpc_user, rpc_password, host, port)
|
||||||
|
server = jsonrpclib.Server(server_url)
|
||||||
# Test daemon is running
|
# Test daemon is running
|
||||||
server.ping()
|
server.ping()
|
||||||
return server
|
return server
|
||||||
@ -85,23 +92,29 @@ def get_server(config):
|
|||||||
time.sleep(1.0)
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def get_rpc_credentials(config):
|
||||||
class RequestHandler(SimpleJSONRPCRequestHandler):
|
rpc_user = config.get('rpcuser', None)
|
||||||
|
rpc_password = config.get('rpcpassword', None)
|
||||||
def do_OPTIONS(self):
|
if rpc_user is None or rpc_password is None:
|
||||||
self.send_response(200)
|
rpc_user = 'user'
|
||||||
self.end_headers()
|
import ecdsa, base64
|
||||||
|
bits = 128
|
||||||
def end_headers(self):
|
nbytes = bits // 8 + (bits % 8 > 0)
|
||||||
self.send_header("Access-Control-Allow-Headers",
|
pw_int = ecdsa.util.randrange(pow(2, bits))
|
||||||
"Origin, X-Requested-With, Content-Type, Accept")
|
pw_b64 = base64.b64encode(
|
||||||
self.send_header("Access-Control-Allow-Origin", "*")
|
int_to_bytes(pw_int, nbytes, 'big'), b'-_')
|
||||||
SimpleJSONRPCRequestHandler.end_headers(self)
|
rpc_password = to_string(pw_b64, 'ascii')
|
||||||
|
config.set_key('rpcuser', rpc_user)
|
||||||
|
config.set_key('rpcpassword', rpc_password, save=True)
|
||||||
|
elif rpc_password == '':
|
||||||
|
from .util import print_stderr
|
||||||
|
print_stderr('WARNING: RPC authentication is disabled.')
|
||||||
|
return rpc_user, rpc_password
|
||||||
|
|
||||||
|
|
||||||
class Daemon(DaemonThread):
|
class Daemon(DaemonThread):
|
||||||
|
|
||||||
def __init__(self, config, fd):
|
def __init__(self, config, fd, is_gui):
|
||||||
DaemonThread.__init__(self)
|
DaemonThread.__init__(self)
|
||||||
self.config = config
|
self.config = config
|
||||||
if config.get('offline'):
|
if config.get('offline'):
|
||||||
@ -116,30 +129,34 @@ class Daemon(DaemonThread):
|
|||||||
self.gui = None
|
self.gui = None
|
||||||
self.wallets = {}
|
self.wallets = {}
|
||||||
# Setup JSONRPC server
|
# Setup JSONRPC server
|
||||||
self.cmd_runner = Commands(self.config, None, self.network)
|
self.init_server(config, fd, is_gui)
|
||||||
self.init_server(config, fd)
|
|
||||||
|
|
||||||
def init_server(self, config, fd):
|
def init_server(self, config, fd, is_gui):
|
||||||
host = config.get('rpchost', '127.0.0.1')
|
host = config.get('rpchost', '127.0.0.1')
|
||||||
port = config.get('rpcport', 0)
|
port = config.get('rpcport', 0)
|
||||||
|
|
||||||
|
rpc_user, rpc_password = get_rpc_credentials(config)
|
||||||
try:
|
try:
|
||||||
server = SimpleJSONRPCServer((host, port), logRequests=False,
|
server = VerifyingJSONRPCServer((host, port), logRequests=False,
|
||||||
requestHandler=RequestHandler)
|
rpc_user=rpc_user, rpc_password=rpc_password)
|
||||||
except:
|
except Exception as e:
|
||||||
self.print_error('Warning: cannot initialize RPC server on host', host)
|
self.print_error('Warning: cannot initialize RPC server on host', host, e)
|
||||||
self.server = None
|
self.server = None
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
return
|
return
|
||||||
os.write(fd, repr((server.socket.getsockname(), time.time())))
|
os.write(fd, repr((server.socket.getsockname(), time.time())))
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
server.timeout = 0.1
|
|
||||||
for cmdname in known_commands:
|
|
||||||
server.register_function(getattr(self.cmd_runner, cmdname), cmdname)
|
|
||||||
server.register_function(self.run_cmdline, 'run_cmdline')
|
|
||||||
server.register_function(self.ping, 'ping')
|
|
||||||
server.register_function(self.run_daemon, 'daemon')
|
|
||||||
server.register_function(self.run_gui, 'gui')
|
|
||||||
self.server = server
|
self.server = server
|
||||||
|
server.timeout = 0.1
|
||||||
|
server.register_function(self.ping, 'ping')
|
||||||
|
if is_gui:
|
||||||
|
server.register_function(self.run_gui, 'gui')
|
||||||
|
else:
|
||||||
|
self.cmd_runner = Commands(self.config, None, self.network)
|
||||||
|
for cmdname in known_commands:
|
||||||
|
server.register_function(getattr(self.cmd_runner, cmdname), cmdname)
|
||||||
|
server.register_function(self.run_cmdline, 'run_cmdline')
|
||||||
|
server.register_function(self.run_daemon, 'daemon')
|
||||||
|
|
||||||
def ping(self):
|
def ping(self):
|
||||||
return True
|
return True
|
||||||
|
|||||||
97
lib/jsonrpc.py
Normal file
97
lib/jsonrpc.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
#
|
||||||
|
# Electrum - lightweight Bitcoin client
|
||||||
|
# Copyright (C) 2018 Thomas Voegtlin
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person
|
||||||
|
# obtaining a copy of this software and associated documentation files
|
||||||
|
# (the "Software"), to deal in the Software without restriction,
|
||||||
|
# including without limitation the rights to use, copy, modify, merge,
|
||||||
|
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
# and to permit persons to whom the Software is furnished to do so,
|
||||||
|
# subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer, SimpleJSONRPCRequestHandler
|
||||||
|
from base64 import b64decode
|
||||||
|
import time
|
||||||
|
|
||||||
|
from . import util
|
||||||
|
|
||||||
|
|
||||||
|
class RPCAuthCredentialsInvalid(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return 'Authentication failed (bad credentials)'
|
||||||
|
|
||||||
|
|
||||||
|
class RPCAuthCredentialsMissing(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return 'Authentication failed (missing credentials)'
|
||||||
|
|
||||||
|
|
||||||
|
class RPCAuthUnsupportedType(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return 'Authentication failed (only basic auth is supported)'
|
||||||
|
|
||||||
|
|
||||||
|
# based on http://acooke.org/cute/BasicHTTPA0.html by andrew cooke
|
||||||
|
class VerifyingJSONRPCServer(SimpleJSONRPCServer):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kargs):
|
||||||
|
|
||||||
|
self.rpc_user = kargs['rpc_user']
|
||||||
|
self.rpc_password = kargs['rpc_password']
|
||||||
|
del kargs['rpc_user']
|
||||||
|
del kargs['rpc_password']
|
||||||
|
|
||||||
|
class VerifyingRequestHandler(SimpleJSONRPCRequestHandler):
|
||||||
|
def parse_request(myself):
|
||||||
|
# first, call the original implementation which returns
|
||||||
|
# True if all OK so far
|
||||||
|
if SimpleJSONRPCRequestHandler.parse_request(myself):
|
||||||
|
try:
|
||||||
|
self.authenticate(myself.headers)
|
||||||
|
return True
|
||||||
|
except (RPCAuthCredentialsInvalid, RPCAuthCredentialsMissing,
|
||||||
|
RPCAuthUnsupportedType) as e:
|
||||||
|
myself.send_error(401, str(e))
|
||||||
|
except BaseException as e:
|
||||||
|
import traceback, sys
|
||||||
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
myself.send_error(500, str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
SimpleJSONRPCServer.__init__(
|
||||||
|
self, requestHandler=VerifyingRequestHandler, *args, **kargs)
|
||||||
|
|
||||||
|
def authenticate(self, headers):
|
||||||
|
if self.rpc_password == '':
|
||||||
|
# RPC authentication is disabled
|
||||||
|
return
|
||||||
|
|
||||||
|
auth_string = headers.get('Authorization', None)
|
||||||
|
if auth_string is None:
|
||||||
|
raise RPCAuthCredentialsMissing()
|
||||||
|
|
||||||
|
(basic, _, encoded) = auth_string.partition(' ')
|
||||||
|
if basic != 'Basic':
|
||||||
|
raise RPCAuthUnsupportedType()
|
||||||
|
|
||||||
|
encoded = util.to_bytes(encoded, 'utf8')
|
||||||
|
credentials = util.to_string(b64decode(encoded), 'utf8')
|
||||||
|
(username, _, password) = credentials.partition(':')
|
||||||
|
if not (util.constant_time_compare(username, self.rpc_user)
|
||||||
|
and util.constant_time_compare(password, self.rpc_password)):
|
||||||
|
time.sleep(0.050)
|
||||||
|
raise RPCAuthCredentialsInvalid()
|
||||||
@ -75,6 +75,9 @@ class WalletStorage(PrintError):
|
|||||||
self.raw = f.read()
|
self.raw = f.read()
|
||||||
if not self.is_encrypted():
|
if not self.is_encrypted():
|
||||||
self.load_data(self.raw)
|
self.load_data(self.raw)
|
||||||
|
else:
|
||||||
|
# avoid new wallets getting 'upgraded'
|
||||||
|
self.put('seed_version', FINAL_SEED_VERSION)
|
||||||
|
|
||||||
def load_data(self, s):
|
def load_data(self, s):
|
||||||
try:
|
try:
|
||||||
@ -161,8 +164,6 @@ class WalletStorage(PrintError):
|
|||||||
|
|
||||||
@profiler
|
@profiler
|
||||||
def write(self):
|
def write(self):
|
||||||
# this ensures that previous versions of electrum won't open the wallet
|
|
||||||
self.put('seed_version', FINAL_SEED_VERSION)
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self._write()
|
self._write()
|
||||||
|
|
||||||
@ -244,12 +245,14 @@ class WalletStorage(PrintError):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def requires_upgrade(self):
|
def requires_upgrade(self):
|
||||||
return self.file_exists() and self.get_seed_version() != FINAL_SEED_VERSION
|
return self.file_exists() and self.get_seed_version() < FINAL_SEED_VERSION
|
||||||
|
|
||||||
def upgrade(self):
|
def upgrade(self):
|
||||||
self.convert_imported()
|
self.convert_imported()
|
||||||
self.convert_wallet_type()
|
self.convert_wallet_type()
|
||||||
self.convert_account()
|
self.convert_account()
|
||||||
|
|
||||||
|
self.put('seed_version', FINAL_SEED_VERSION)
|
||||||
self.write()
|
self.write()
|
||||||
|
|
||||||
def convert_wallet_type(self):
|
def convert_wallet_type(self):
|
||||||
@ -379,6 +382,8 @@ class WalletStorage(PrintError):
|
|||||||
seed_version = self.get('seed_version')
|
seed_version = self.get('seed_version')
|
||||||
if not seed_version:
|
if not seed_version:
|
||||||
seed_version = OLD_SEED_VERSION if len(self.get('master_public_key','')) == 128 else NEW_SEED_VERSION
|
seed_version = OLD_SEED_VERSION if len(self.get('master_public_key','')) == 128 else NEW_SEED_VERSION
|
||||||
|
if seed_version > FINAL_SEED_VERSION:
|
||||||
|
raise BaseException('This version of Electrum is too old to open this wallet')
|
||||||
if seed_version >=12:
|
if seed_version >=12:
|
||||||
return seed_version
|
return seed_version
|
||||||
if seed_version not in [OLD_SEED_VERSION, NEW_SEED_VERSION]:
|
if seed_version not in [OLD_SEED_VERSION, NEW_SEED_VERSION]:
|
||||||
|
|||||||
41
lib/util.py
41
lib/util.py
@ -34,6 +34,8 @@ import urlparse
|
|||||||
import urllib
|
import urllib
|
||||||
import threading
|
import threading
|
||||||
from i18n import _
|
from i18n import _
|
||||||
|
import hmac
|
||||||
|
|
||||||
|
|
||||||
base_units = {'BTC':8, 'mBTC':5, 'uBTC':2}
|
base_units = {'BTC':8, 'mBTC':5, 'uBTC':2}
|
||||||
fee_levels = [_('Within 25 blocks'), _('Within 10 blocks'), _('Within 5 blocks'), _('Within 2 blocks'), _('In the next block')]
|
fee_levels = [_('Within 25 blocks'), _('Within 10 blocks'), _('Within 5 blocks'), _('Within 2 blocks'), _('In the next block')]
|
||||||
@ -191,6 +193,13 @@ def json_decode(x):
|
|||||||
except:
|
except:
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
# taken from Django Source Code
|
||||||
|
def constant_time_compare(val1, val2):
|
||||||
|
"""Return True if the two strings are equal, False otherwise."""
|
||||||
|
return hmac.compare_digest(to_bytes(val1, 'utf8'), to_bytes(val2, 'utf8'))
|
||||||
|
|
||||||
|
|
||||||
# decorator that prints execution time
|
# decorator that prints execution time
|
||||||
def profiler(func):
|
def profiler(func):
|
||||||
def do_profile(func, args, kw_args):
|
def do_profile(func, args, kw_args):
|
||||||
@ -238,6 +247,38 @@ def android_check_data_dir():
|
|||||||
def get_headers_dir(config):
|
def get_headers_dir(config):
|
||||||
return android_headers_dir() if 'ANDROID_DATA' in os.environ else config.path
|
return android_headers_dir() if 'ANDROID_DATA' in os.environ else config.path
|
||||||
|
|
||||||
|
def to_string(x, enc):
|
||||||
|
if isinstance(x, (bytes, bytearray)):
|
||||||
|
return x.decode(enc)
|
||||||
|
if isinstance(x, str):
|
||||||
|
return x
|
||||||
|
else:
|
||||||
|
raise TypeError("Not a string or bytes like object")
|
||||||
|
|
||||||
|
def to_bytes(something, encoding='utf8'):
|
||||||
|
"""
|
||||||
|
cast string to bytes() like object, but for python2 support it's bytearray copy
|
||||||
|
"""
|
||||||
|
if isinstance(something, bytes):
|
||||||
|
return something
|
||||||
|
if isinstance(something, str) or isinstance(something, unicode):
|
||||||
|
return something.encode(encoding)
|
||||||
|
elif isinstance(something, bytearray):
|
||||||
|
return bytes(something)
|
||||||
|
else:
|
||||||
|
raise TypeError("Not a string or bytes like object")
|
||||||
|
|
||||||
|
# based on https://stackoverflow.com/questions/16022556/has-python-3-to-bytes-been-back-ported-to-python-2-7
|
||||||
|
def int_to_bytes(n, length, endianess='big'):
|
||||||
|
hex_n = '%x' % n
|
||||||
|
hex_n2 = '0'*(len(hex_n) % 2) + hex_n
|
||||||
|
left_padded_hex_n = hex_n2.zfill(length*2)
|
||||||
|
if len(left_padded_hex_n) > length*2:
|
||||||
|
raise OverflowError()
|
||||||
|
assert len(left_padded_hex_n) == length*2
|
||||||
|
bytes_n = left_padded_hex_n.decode('hex')
|
||||||
|
return bytes_n if endianess == 'big' else bytes_n[::-1]
|
||||||
|
|
||||||
def user_dir():
|
def user_dir():
|
||||||
if 'ANDROID_DATA' in os.environ:
|
if 'ANDROID_DATA' in os.environ:
|
||||||
return android_check_data_dir()
|
return android_check_data_dir()
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
ELECTRUM_VERSION = '2.9.3' # version of the client package
|
ELECTRUM_VERSION = '2.9.4' # version of the client package
|
||||||
PROTOCOL_VERSION = '0.10' # protocol version requested
|
PROTOCOL_VERSION = '0.10' # protocol version requested
|
||||||
|
|
||||||
# The hash of the mnemonic seed must begin with this
|
# The hash of the mnemonic seed must begin with this
|
||||||
|
|||||||
@ -229,7 +229,7 @@ class TrezorClientBase(GuiMixin, PrintError):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def wrap_methods(cls):
|
def wrap_methods(cls):
|
||||||
for method in ['apply_settings', 'change_pin', 'decrypt_message',
|
for method in ['apply_settings', 'change_pin',
|
||||||
'get_address', 'get_public_node',
|
'get_address', 'get_public_node',
|
||||||
'load_device_by_mnemonic', 'load_device_by_xprv',
|
'load_device_by_mnemonic', 'load_device_by_xprv',
|
||||||
'recovery_device', 'reset_device', 'sign_message',
|
'recovery_device', 'reset_device', 'sign_message',
|
||||||
|
|||||||
@ -29,14 +29,7 @@ class TrezorCompatibleKeyStore(Hardware_KeyStore):
|
|||||||
return self.plugin.get_client(self, force_pair)
|
return self.plugin.get_client(self, force_pair)
|
||||||
|
|
||||||
def decrypt_message(self, sequence, message, password):
|
def decrypt_message(self, sequence, message, password):
|
||||||
raise RuntimeError(_('Electrum and %s encryption and decryption are currently incompatible') % self.device)
|
raise RuntimeError(_('Encryption and decryption are not implemented by %s') % self.device)
|
||||||
client = self.get_client()
|
|
||||||
address_path = self.get_derivation() + "/%d/%d"%sequence
|
|
||||||
address_n = client.expand_path(address_path)
|
|
||||||
payload = base64.b64decode(message)
|
|
||||||
nonce, message, msg_hmac = payload[:33], payload[33:-8], payload[-8:]
|
|
||||||
result = client.decrypt_message(address_n, nonce, message, msg_hmac)
|
|
||||||
return result.message
|
|
||||||
|
|
||||||
def sign_message(self, sequence, message, password):
|
def sign_message(self, sequence, message, password):
|
||||||
client = self.get_client()
|
client = self.get_client()
|
||||||
@ -55,7 +48,7 @@ class TrezorCompatibleKeyStore(Hardware_KeyStore):
|
|||||||
for txin in tx.inputs():
|
for txin in tx.inputs():
|
||||||
pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin)
|
pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin)
|
||||||
tx_hash = txin['prevout_hash']
|
tx_hash = txin['prevout_hash']
|
||||||
prev_tx[tx_hash] = txin['prev_tx']
|
prev_tx[tx_hash] = txin['prev_tx']
|
||||||
for x_pubkey in x_pubkeys:
|
for x_pubkey in x_pubkeys:
|
||||||
if not is_xpubkey(x_pubkey):
|
if not is_xpubkey(x_pubkey):
|
||||||
continue
|
continue
|
||||||
@ -96,7 +89,7 @@ class TrezorCompatiblePlugin(HW_PluginBase):
|
|||||||
# raise
|
# raise
|
||||||
self.print_error("cannot connect at", device.path, str(e))
|
self.print_error("cannot connect at", device.path, str(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _try_bridge(self, device):
|
def _try_bridge(self, device):
|
||||||
self.print_error("Trying to connect over Trezor Bridge...")
|
self.print_error("Trying to connect over Trezor Bridge...")
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user