Compare commits
27 Commits
Author | SHA1 | Date |
---|---|---|
Chris Sulmone | 524c308844 | |
Roman Zeyde | ab5e4eee34 | |
matejcik | 6b51228090 | |
matejcik | 5edcea9ba6 | |
Tomas Susanka | 89eac8f157 | |
Tomas Susanka | 869af4498b | |
Pavol Rusnak | 683f383e90 | |
matejcik | d8c9c970f5 | |
matejcik | dd052d07b0 | |
matejcik | c0d2af557c | |
matejcik | a1dba05a46 | |
matejcik | 2c15a861dc | |
matejcik | 5422c40451 | |
matejcik | e1e419485f | |
matejcik | 9f2583f893 | |
matejcik | f75b90d260 | |
matejcik | dc8eec1656 | |
matejcik | 967d479a19 | |
matejcik | ff80ca1b82 | |
matejcik | 2f1c15b588 | |
matejcik | 513e6aae08 | |
matejcik | 2a706a751a | |
matejcik | 6519657808 | |
matejcik | d2913c20bd | |
matejcik | 49790d7bfe | |
matejcik | bc8120230a | |
matejcik | 473ea19570 |
|
@ -15,7 +15,7 @@ addons:
|
|||
- libusb-1.0-0-dev
|
||||
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
|
|
1
setup.py
1
setup.py
|
@ -33,6 +33,7 @@ setup(
|
|||
url='https://github.com/trezor/python-trezor',
|
||||
packages=[
|
||||
'trezorlib',
|
||||
'trezorlib.transport',
|
||||
'trezorlib.messages',
|
||||
'trezorlib.qt',
|
||||
'trezorlib.tests.device_tests',
|
||||
|
|
|
@ -16,21 +16,15 @@ import hashlib
|
|||
import binascii
|
||||
|
||||
from trezorlib.client import TrezorClient
|
||||
from trezorlib.device import TrezorDevice
|
||||
|
||||
# Python2 vs Python3
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
from trezorlib.transport import enumerate_devices
|
||||
|
||||
|
||||
def wait_for_devices():
|
||||
devices = TrezorDevice.enumerate()
|
||||
devices = enumerate_devices()
|
||||
while not len(devices):
|
||||
sys.stderr.write("Please connect TREZOR to computer and press Enter...")
|
||||
input()
|
||||
devices = TrezorDevice.enumerate()
|
||||
devices = enumerate_devices()
|
||||
|
||||
return devices
|
||||
|
||||
|
|
|
@ -1,21 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
|
||||
from trezorlib.client import TrezorClient
|
||||
from trezorlib.device import TrezorDevice
|
||||
from trezorlib.transport import get_transport
|
||||
|
||||
|
||||
def main():
|
||||
# List all connected TREZORs on USB/UDP
|
||||
devices = TrezorDevice.enumerate()
|
||||
|
||||
# Check whether we found any
|
||||
if len(devices) == 0:
|
||||
print('No TREZOR found')
|
||||
return
|
||||
|
||||
# Use first connected device
|
||||
transport = devices[0]
|
||||
transport = get_transport()
|
||||
|
||||
# Creates object for manipulating TREZOR
|
||||
client = TrezorClient(transport)
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
|
||||
from trezorlib.debuglink import DebugLink
|
||||
from trezorlib.client import TrezorClient
|
||||
from trezorlib.transport_hid import HidTransport
|
||||
from trezorlib.transport import enumerate_devices
|
||||
import binascii
|
||||
import sys
|
||||
|
||||
|
@ -16,8 +14,8 @@ sectorlens = [0x4000, 0x4000, 0x4000, 0x4000,
|
|||
|
||||
|
||||
def main():
|
||||
# List all connected TREZORs on USB
|
||||
devices = HidTransport.enumerate()
|
||||
# List all debuggable TREZORs
|
||||
devices = [device for device in enumerate_devices() if hasattr(device, 'find_debug')]
|
||||
|
||||
# Check whether we found any
|
||||
if len(devices) == 0:
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
|
||||
from trezorlib.debuglink import DebugLink
|
||||
from trezorlib.client import TrezorClient
|
||||
from trezorlib.transport_hid import HidTransport
|
||||
from trezorlib.transport import enumerate_devices
|
||||
import sys
|
||||
|
||||
# usage examples
|
||||
|
@ -16,8 +14,8 @@ import sys
|
|||
|
||||
|
||||
def main():
|
||||
# List all connected TREZORs on USB
|
||||
devices = HidTransport.enumerate()
|
||||
# List all debuggable TREZORs
|
||||
devices = [device for device in enumerate_devices() if hasattr(device, 'find_debug')]
|
||||
|
||||
# Check whether we found any
|
||||
if len(devices) == 0:
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
|
||||
from trezorlib.debuglink import DebugLink
|
||||
from trezorlib.client import TrezorClient
|
||||
from trezorlib.transport_hid import HidTransport
|
||||
from trezorlib.transport import enumerate_devices
|
||||
import binascii
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
# List all connected TREZORs on USB
|
||||
devices = HidTransport.enumerate()
|
||||
# List all debuggable TREZORs
|
||||
devices = [device for device in enumerate_devices() if hasattr(device, 'find_debug')]
|
||||
|
||||
# Check whether we found any
|
||||
if len(devices) == 0:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
import binascii
|
||||
import hashlib
|
||||
import mnemonic
|
||||
|
@ -16,12 +15,6 @@ __doc__ = '''
|
|||
without an internet connection).
|
||||
'''
|
||||
|
||||
# Python2 vs Python3
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
|
||||
def generate_entropy(strength, internal_entropy, external_entropy):
|
||||
'''
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from binascii import hexlify, unhexlify
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
@ -9,14 +6,10 @@ import hmac
|
|||
import hashlib
|
||||
import json
|
||||
import os
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
except:
|
||||
from urlparse import urlparse
|
||||
input = raw_input
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from trezorlib.client import TrezorClient
|
||||
from trezorlib.transport_hid import HidTransport
|
||||
from trezorlib.transport import get_transport
|
||||
|
||||
|
||||
# Return path by BIP-32
|
||||
|
@ -132,12 +125,13 @@ def printEntries(entries):
|
|||
|
||||
|
||||
def main():
|
||||
devices = HidTransport.enumerate()
|
||||
if not devices:
|
||||
print('TREZOR is not plugged in. Please, connect TREZOR and retry.')
|
||||
try:
|
||||
transport = get_transport()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return
|
||||
|
||||
client = TrezorClient(devices[0])
|
||||
client = TrezorClient(transport)
|
||||
|
||||
print()
|
||||
print('Confirm operation on TREZOR')
|
||||
|
|
|
@ -8,21 +8,14 @@ from __future__ import print_function
|
|||
import io
|
||||
import sys
|
||||
from trezorlib.client import TrezorClient
|
||||
from trezorlib.device import TrezorDevice
|
||||
|
||||
|
||||
def get_client():
|
||||
devices = TrezorDevice.enumerate() # list all connected TREZORs on USB
|
||||
if len(devices) == 0: # check whether we found any
|
||||
return None
|
||||
transport = devices[0] # use first connected device
|
||||
return TrezorClient(transport) # creates object for communicating with TREZOR
|
||||
from trezorlib.transport import get_transport
|
||||
|
||||
|
||||
def main():
|
||||
client = get_client()
|
||||
if not client:
|
||||
print('No TREZOR connected')
|
||||
try:
|
||||
client = TrezorClient(get_transport())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return
|
||||
|
||||
arg1 = sys.argv[1] # output file
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
import binascii
|
||||
import os
|
||||
import random
|
||||
|
@ -12,8 +11,7 @@ import hashlib
|
|||
from trezorlib.client import TrezorClient
|
||||
from trezorlib.tx_api import TxApiTestnet
|
||||
from trezorlib.tx_api import TxApiBitcoin
|
||||
from trezorlib.transport_hid import HidTransport
|
||||
from trezorlib.transport_bridge import BridgeTransport
|
||||
from trezorlib.transport import get_transport
|
||||
|
||||
|
||||
def hash160(x):
|
||||
|
@ -152,16 +150,13 @@ def main():
|
|||
numinputs = 100
|
||||
sizeinputtx = 10
|
||||
|
||||
# List all connected TREZORs on USB
|
||||
devices = HidTransport.enumerate()
|
||||
|
||||
# Check whether we found any
|
||||
if len(devices) == 0:
|
||||
print('No TREZOR found')
|
||||
# Use first connected device
|
||||
try:
|
||||
transport = get_transport()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return
|
||||
|
||||
# Use first connected device
|
||||
transport = devices[0]
|
||||
print(transport)
|
||||
|
||||
txstore = MyTxApiBitcoin()
|
||||
|
|
3
tox.ini
3
tox.ini
|
@ -1,6 +1,6 @@
|
|||
[tox]
|
||||
envlist =
|
||||
py27,
|
||||
py33,
|
||||
py34,
|
||||
py35,
|
||||
py36,
|
||||
|
@ -9,6 +9,7 @@ envlist =
|
|||
deps =
|
||||
-rrequirements.txt
|
||||
pytest
|
||||
mock
|
||||
commands =
|
||||
python -m compileall trezorlib/
|
||||
python trezorctl --help
|
||||
|
|
23
trezorctl
23
trezorctl
|
@ -29,7 +29,7 @@ import os
|
|||
import sys
|
||||
|
||||
from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException, format_protobuf
|
||||
from trezorlib.device import TrezorDevice
|
||||
from trezorlib.transport import get_transport, enumerate_devices, TransportException
|
||||
from trezorlib import messages as proto
|
||||
from trezorlib import protobuf
|
||||
from trezorlib.coins import coins_txapi
|
||||
|
@ -72,9 +72,24 @@ CHOICE_OUTPUT_SCRIPT_TYPE = ChoiceType({
|
|||
def cli(ctx, path, verbose, is_json):
|
||||
if ctx.invoked_subcommand != 'list':
|
||||
if verbose:
|
||||
ctx.obj = lambda: TrezorClientVerbose(TrezorDevice.find_by_path(path))
|
||||
cls = TrezorClientVerbose
|
||||
else:
|
||||
ctx.obj = lambda: TrezorClient(TrezorDevice.find_by_path(path))
|
||||
cls = TrezorClient
|
||||
|
||||
def get_device():
|
||||
try:
|
||||
device = get_transport(path, prefix_search=False)
|
||||
except:
|
||||
try:
|
||||
device = get_transport(path, prefix_search=True)
|
||||
except:
|
||||
click.echo("Failed to find a TREZOR device.")
|
||||
if path is not None:
|
||||
click.echo("Using path: {}".format(path))
|
||||
sys.exit(1)
|
||||
return cls(device)
|
||||
|
||||
ctx.obj = get_device
|
||||
|
||||
|
||||
@cli.resultcallback()
|
||||
|
@ -108,7 +123,7 @@ def print_result(res, path, verbose, is_json):
|
|||
|
||||
@cli.command(name='list', help='List connected TREZOR devices.')
|
||||
def ls():
|
||||
return TrezorDevice.enumerate()
|
||||
return enumerate_devices()
|
||||
|
||||
|
||||
@cli.command(help='Show version of trezorctl/trezorlib.')
|
||||
|
|
|
@ -32,20 +32,13 @@ from . import messages as proto
|
|||
|
||||
PRIME_DERIVATION_FLAG = 0x80000000
|
||||
|
||||
if sys.version_info < (3,):
|
||||
def byteindex(data, index):
|
||||
return ord(data[index])
|
||||
else:
|
||||
def byteindex(data, index):
|
||||
return data[index]
|
||||
|
||||
|
||||
def point_to_pubkey(point):
|
||||
order = SECP256k1.order
|
||||
x_str = number_to_string(point.x(), order)
|
||||
y_str = number_to_string(point.y(), order)
|
||||
vk = x_str + y_str
|
||||
return struct.pack('B', (byteindex(vk, 63) & 1) + 2) + vk[0:32] # To compressed key
|
||||
return struct.pack('B', (vk[63] & 1) + 2) + vk[0:32] # To compressed key
|
||||
|
||||
|
||||
def sec_to_public_pair(pubkey):
|
||||
|
@ -152,7 +145,7 @@ def deserialize(xpub):
|
|||
node.chain_code = data[13:45]
|
||||
|
||||
key = data[45:-4]
|
||||
if byteindex(key, 0) == 0:
|
||||
if key[0] == 0:
|
||||
node.private_key = key[1:]
|
||||
else:
|
||||
node.public_key = key
|
||||
|
|
|
@ -38,15 +38,9 @@ from .coins import coins_slip44
|
|||
from .debuglink import DebugLink
|
||||
from .protobuf import MessageType
|
||||
|
||||
# Python2 vs Python3
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
|
||||
if sys.version_info.major < 3:
|
||||
warnings.warn("Trezorlib will stop supporting Python2 in next versions.", DeprecationWarning)
|
||||
raise Exception("Trezorlib does not support Python 2 anymore.")
|
||||
|
||||
# try:
|
||||
# from PIL import Image
|
||||
|
@ -57,19 +51,14 @@ if sys.version_info.major < 3:
|
|||
SCREENSHOT = False
|
||||
|
||||
|
||||
def getch():
|
||||
try:
|
||||
# make a getch function
|
||||
try:
|
||||
import termios
|
||||
except ImportError:
|
||||
# Non-POSIX. Return msvcrt's (Windows') getch.
|
||||
import msvcrt
|
||||
return msvcrt.getch()
|
||||
|
||||
# POSIX system. Create and return a getch that manipulates the tty.
|
||||
import sys
|
||||
import tty
|
||||
# POSIX system. Create and return a getch that manipulates the tty.
|
||||
# On Windows, termios will fail to import.
|
||||
|
||||
def _getch():
|
||||
def getch():
|
||||
fd = sys.stdin.fileno()
|
||||
old_settings = termios.tcgetattr(fd)
|
||||
try:
|
||||
|
@ -79,7 +68,19 @@ def getch():
|
|||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||
return ch
|
||||
|
||||
return _getch()
|
||||
except ImportError:
|
||||
# Windows system.
|
||||
# Use msvcrt's getch function.
|
||||
import msvcrt
|
||||
|
||||
def getch():
|
||||
while True:
|
||||
key = msvcrt.getch()
|
||||
if key in (0x00, 0xe0):
|
||||
# skip special keys: read the scancode and repeat
|
||||
msvcrt.getch()
|
||||
continue
|
||||
return key.decode('latin1')
|
||||
|
||||
|
||||
def get_buttonrequest_value(code):
|
||||
|
@ -112,7 +113,7 @@ def format_protobuf(pb, indent=0, sep=' ' * 4):
|
|||
lines.append(level + '}')
|
||||
return '\n'.join(lines)
|
||||
if isinstance(value, bytearray):
|
||||
return 'bytearray(0x{})'.format(value.hex())
|
||||
return 'bytearray(0x{})'.format(binascii.hexlify(value).decode('ascii'))
|
||||
|
||||
return repr(value)
|
||||
|
||||
|
@ -194,17 +195,13 @@ def session(f):
|
|||
|
||||
|
||||
def normalize_nfc(txt):
|
||||
if sys.version_info[0] < 3:
|
||||
if isinstance(txt, unicode):
|
||||
return unicodedata.normalize('NFC', txt)
|
||||
if isinstance(txt, str):
|
||||
return unicodedata.normalize('NFC', txt.decode('utf-8'))
|
||||
else:
|
||||
'''
|
||||
Normalize message to NFC and return bytes suitable for protobuf.
|
||||
This seems to be bitcoin-qt standard of doing things.
|
||||
'''
|
||||
if isinstance(txt, bytes):
|
||||
return unicodedata.normalize('NFC', txt.decode('utf-8'))
|
||||
if isinstance(txt, str):
|
||||
return unicodedata.normalize('NFC', txt)
|
||||
raise ValueError('unicode/str or bytes/str expected')
|
||||
txt = txt.decode('utf-8')
|
||||
return unicodedata.normalize('NFC', txt).encode('utf-8')
|
||||
|
||||
|
||||
class BaseClient(object):
|
||||
|
@ -614,13 +611,11 @@ class ProtocolMixin(object):
|
|||
@expect(proto.EthereumMessageSignature)
|
||||
def ethereum_sign_message(self, n, message):
|
||||
n = self._convert_prime(n)
|
||||
# Convert message to UTF8 NFC (seems to be a bitcoin-qt standard)
|
||||
message = normalize_nfc(message).encode('utf-8')
|
||||
message = normalize_nfc(message)
|
||||
return self.call(proto.EthereumSignMessage(address_n=n, message=message))
|
||||
|
||||
def ethereum_verify_message(self, address, signature, message):
|
||||
# Convert message to UTF8 NFC (seems to be a bitcoin-qt standard)
|
||||
message = normalize_nfc(message).encode('utf-8')
|
||||
message = normalize_nfc(message)
|
||||
try:
|
||||
resp = self.call(proto.EthereumVerifyMessage(address=address, signature=signature, message=message))
|
||||
except CallException as e:
|
||||
|
@ -685,8 +680,7 @@ class ProtocolMixin(object):
|
|||
@expect(proto.MessageSignature)
|
||||
def sign_message(self, coin_name, n, message, script_type=proto.InputScriptType.SPENDADDRESS):
|
||||
n = self._convert_prime(n)
|
||||
# Convert message to UTF8 NFC (seems to be a bitcoin-qt standard)
|
||||
message = normalize_nfc(message).encode('utf-8')
|
||||
message = normalize_nfc(message)
|
||||
return self.call(proto.SignMessage(coin_name=coin_name, address_n=n, message=message, script_type=script_type))
|
||||
|
||||
@expect(proto.SignedIdentity)
|
||||
|
@ -839,8 +833,7 @@ class ProtocolMixin(object):
|
|||
return self.call(msg)
|
||||
|
||||
def verify_message(self, coin_name, address, signature, message):
|
||||
# Convert message to UTF8 NFC (seems to be a bitcoin-qt standard)
|
||||
message = normalize_nfc(message).encode('utf-8')
|
||||
message = normalize_nfc(message)
|
||||
try:
|
||||
resp = self.call(proto.VerifyMessage(address=address, signature=signature, message=message, coin_name=coin_name))
|
||||
except CallException as e:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .tx_api import TxApiBitcoin, TxApiTestnet, TxApiLitecoin, TxApiZcash, TxApiDash, TxApiBcash, TxApiDecredTestnet, TxApiDogecoin, TxApiMonacoin
|
||||
from .tx_api import TxApiBitcoin, TxApiTestnet, TxApiLitecoin, TxApiZcash, TxApiDash, TxApiBcash, TxApiDecredTestnet, TxApiDogecoin, TxApiMonacoin, TxApiBitcoinGold, TxApiBitcoinPrivate
|
||||
|
||||
coins_slip44 = {
|
||||
'Bitcoin': 0,
|
||||
|
@ -14,6 +14,8 @@ coins_slip44 = {
|
|||
'EtherClassic': 61,
|
||||
'Zcash': 133,
|
||||
'Bcash': 145,
|
||||
'Bitcoin Gold': 156,
|
||||
'Bitcoin Private': 183,
|
||||
}
|
||||
|
||||
coins_txapi = {
|
||||
|
@ -26,4 +28,6 @@ coins_txapi = {
|
|||
'Decred Testnet': TxApiDecredTestnet,
|
||||
'Dogecoin': TxApiDogecoin,
|
||||
'Monacoin': TxApiMonacoin,
|
||||
'Bitcoin Gold': TxApiBitcoinGold,
|
||||
'Bitcoin Private': TxApiBitcoinPrivate,
|
||||
}
|
||||
|
|
|
@ -16,53 +16,24 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import warnings
|
||||
|
||||
from .transport_bridge import BridgeTransport
|
||||
from .transport_hid import HidTransport
|
||||
from .transport_udp import UdpTransport
|
||||
from .transport_webusb import WebUsbTransport
|
||||
from .transport import enumerate_devices, get_transport
|
||||
|
||||
|
||||
class TrezorDevice(object):
|
||||
class TrezorDevice:
|
||||
'''
|
||||
This class is deprecated. (There is no reason for it to exist in the first
|
||||
place, it is nothing but a collection of two functions.)
|
||||
Instead, please use functions from the ``trezorlib.transport`` module.
|
||||
'''
|
||||
|
||||
@classmethod
|
||||
def enumerate(cls):
|
||||
devices = []
|
||||
|
||||
for d in BridgeTransport.enumerate():
|
||||
devices.append(d)
|
||||
|
||||
for d in UdpTransport.enumerate():
|
||||
devices.append(d)
|
||||
|
||||
for d in HidTransport.enumerate():
|
||||
devices.append(d)
|
||||
|
||||
for d in WebUsbTransport.enumerate():
|
||||
devices.append(d)
|
||||
|
||||
return devices
|
||||
warnings.warn('TrezorDevice is deprecated.', DeprecationWarning)
|
||||
return enumerate_devices()
|
||||
|
||||
@classmethod
|
||||
def find_by_path(cls, path):
|
||||
if path is None:
|
||||
try:
|
||||
return cls.enumerate()[0]
|
||||
except IndexError:
|
||||
raise Exception("No TREZOR device found")
|
||||
|
||||
prefix = path.split(':')[0]
|
||||
|
||||
if prefix == BridgeTransport.PATH_PREFIX:
|
||||
return BridgeTransport.find_by_path(path)
|
||||
|
||||
if prefix == UdpTransport.PATH_PREFIX:
|
||||
return UdpTransport.find_by_path(path)
|
||||
|
||||
if prefix == WebUsbTransport.PATH_PREFIX:
|
||||
return WebUsbTransport.find_by_path(path)
|
||||
|
||||
if prefix == HidTransport.PATH_PREFIX:
|
||||
return HidTransport.find_by_path(path)
|
||||
|
||||
raise Exception("Unknown path prefix '%s'" % prefix)
|
||||
warnings.warn('TrezorDevice is deprecated.', DeprecationWarning)
|
||||
return get_transport(path, prefix_search=False)
|
||||
|
|
|
@ -23,86 +23,23 @@ import pytest
|
|||
import os
|
||||
|
||||
from trezorlib.client import TrezorClient, TrezorClientDebugLink
|
||||
from trezorlib.transport import get_transport
|
||||
from trezorlib import tx_api
|
||||
|
||||
tests_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
tx_api.cache_dir = os.path.join(tests_dir, '../txcache')
|
||||
|
||||
|
||||
try:
|
||||
from trezorlib.transport_hid import HidTransport
|
||||
HID_ENABLED = True
|
||||
except ImportError as e:
|
||||
print('HID transport disabled:', e)
|
||||
HID_ENABLED = False
|
||||
|
||||
try:
|
||||
from trezorlib.transport_webusb import WebUsbTransport
|
||||
WEBUSB_ENABLED = True
|
||||
except ImportError as e:
|
||||
print('WebUSB transport disabled:', e)
|
||||
WEBUSB_ENABLED = False
|
||||
|
||||
try:
|
||||
from trezorlib.transport_pipe import PipeTransport
|
||||
PIPE_ENABLED = True
|
||||
except ImportError as e:
|
||||
print('PIPE transport disabled:', e)
|
||||
PIPE_ENABLED = False
|
||||
|
||||
try:
|
||||
from trezorlib.transport_udp import UdpTransport
|
||||
UDP_ENABLED = True
|
||||
except ImportError as e:
|
||||
print('UDP transport disabled:', e)
|
||||
UDP_ENABLED = False
|
||||
def get_device():
|
||||
path = os.environ.get('TREZOR_PATH')
|
||||
return get_transport(path)
|
||||
|
||||
|
||||
def pipe_exists(path):
|
||||
import os
|
||||
import stat
|
||||
try:
|
||||
return stat.S_ISFIFO(os.stat(path).st_mode)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def get_transport():
|
||||
if HID_ENABLED and HidTransport.enumerate():
|
||||
devices = HidTransport.enumerate()
|
||||
wirelink = devices[0]
|
||||
debuglink = devices[0].find_debug()
|
||||
|
||||
elif WEBUSB_ENABLED and WebUsbTransport.enumerate():
|
||||
devices = WebUsbTransport.enumerate()
|
||||
wirelink = devices[0]
|
||||
debuglink = devices[0].find_debug()
|
||||
|
||||
elif PIPE_ENABLED and pipe_exists('/tmp/pipe.trezor.to'):
|
||||
wirelink = PipeTransport('/tmp/pipe.trezor', False)
|
||||
debuglink = PipeTransport('/tmp/pipe.trezor_debug', False)
|
||||
|
||||
elif UDP_ENABLED:
|
||||
wirelink = UdpTransport('127.0.0.1:21324')
|
||||
debuglink = UdpTransport('127.0.0.1:21325')
|
||||
|
||||
return wirelink, debuglink
|
||||
|
||||
|
||||
if HID_ENABLED and HidTransport.enumerate():
|
||||
print('Using TREZOR')
|
||||
elif WEBUSB_ENABLED and WebUsbTransport.enumerate():
|
||||
print('Using TREZOR via WebUSB')
|
||||
elif PIPE_ENABLED and pipe_exists('/tmp/pipe.trezor.to'):
|
||||
print('Using Emulator (v1=pipe)')
|
||||
elif UDP_ENABLED:
|
||||
print('Using Emulator (v2=udp)')
|
||||
|
||||
|
||||
class TrezorTest(object):
|
||||
class TrezorTest:
|
||||
|
||||
def setup_method(self, method):
|
||||
wirelink, debuglink = get_transport()
|
||||
wirelink = get_device()
|
||||
debuglink = wirelink.find_debug()
|
||||
self.client = TrezorClientDebugLink(wirelink)
|
||||
self.client.set_debuglink(debuglink)
|
||||
self.client.set_tx_api(tx_api.TxApiBitcoin)
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# This file is part of the TREZOR project.
|
||||
#
|
||||
# Copyright (C) 2012-2016 Marek Palatinus <slush@satoshilabs.com>
|
||||
# Copyright (C) 2012-2016 Pavol Rusnak <stick@satoshilabs.com>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .common import *
|
||||
from trezorlib import messages as proto
|
||||
|
||||
|
||||
class TestMsgGetECDHSessionKey(TrezorTest):
|
||||
|
||||
def test_ecdh(self):
|
||||
self.setup_mnemonic_nopin_nopassphrase()
|
||||
|
||||
# URI : gpg://Satoshi Nakamoto <satoshi@bitcoin.org>
|
||||
identity = proto.IdentityType(proto='gpg', user='', host='Satoshi Nakamoto <satoshi@bitcoin.org>', port='', path='', index=0)
|
||||
|
||||
peer_public_key = unhexlify('0407f2c6e5becf3213c1d07df0cfbe8e39f70a8c643df7575e5c56859ec52c45ca950499c019719dae0fda04248d851e52cf9d66eeb211d89a77be40de22b6c89d')
|
||||
result = self.client.get_ecdh_session_key(identity=identity, peer_public_key=peer_public_key, ecdsa_curve_name='secp256k1')
|
||||
assert result.session_key == unhexlify('0495e5d8c9e5cc09e7cf4908774f52decb381ce97f2fc9ba56e959c13f03f9f47a03dd151cbc908bc1db84d46e2c33e7bbb9daddc800f985244c924fd64adf6647')
|
||||
|
||||
peer_public_key = unhexlify('04811a6c2bd2a547d0dd84747297fec47719e7c3f9b0024f027c2b237be99aac39a9230acbd163d0cb1524a0f5ea4bfed6058cec6f18368f72a12aa0c4d083ff64')
|
||||
result = self.client.get_ecdh_session_key(identity=identity, peer_public_key=peer_public_key, ecdsa_curve_name='nist256p1')
|
||||
assert result.session_key == unhexlify('046d1f5c48af2cf2c57076ac2c9d7808db2086f614cb7b8107119ff2c6270cd209749809efe0196f01a0cc633788cef1f4a2bd650c99570d06962f923fca6d8fdf')
|
||||
|
||||
peer_public_key = unhexlify('40a8cf4b6a64c4314e80f15a8ea55812bd735fbb365936a48b2d78807b575fa17a')
|
||||
result = self.client.get_ecdh_session_key(identity=identity, peer_public_key=peer_public_key, ecdsa_curve_name='curve25519')
|
||||
assert result.session_key == unhexlify('04e24516669e0b7d3d72e5129fddd07b6644c30915f5c8b7f1f62324afb3624311')
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
from .common import *
|
||||
import trezorlib.ckd_public as bip32
|
||||
from trezorlib.client import CallException
|
||||
|
||||
|
||||
class TestMsgGetpublickeyCurve(TrezorTest):
|
||||
|
@ -39,5 +40,9 @@ class TestMsgGetpublickeyCurve(TrezorTest):
|
|||
|
||||
def test_ed25519_curve(self):
|
||||
self.setup_mnemonic_nopin_nopassphrase()
|
||||
assert hexlify(self.client.get_public_node([0x80000000 | 111, 42], ecdsa_curve_name='ed25519').node.public_key).decode() == '001d9a1e56f69828d44ec96dad345678411976d3ea6d290fe3ae8032c47699ce15'
|
||||
# ed25519 curve does not support public derivation, so test only private derivation paths
|
||||
assert hexlify(self.client.get_public_node([0x80000000 | 111, 0x80000000 | 42], ecdsa_curve_name='ed25519').node.public_key).decode() == '0069a14b478e508eab6e93303f4e6f5c50b8136627830f2ed5c3a835fc6c0ea2b7'
|
||||
assert hexlify(self.client.get_public_node([0x80000000 | 111, 0x80000000 | 65535], ecdsa_curve_name='ed25519').node.public_key).decode() == '00514f73a05184458611b14c348fee4fd988d36cf3aee7207737861bac611de991'
|
||||
# test failure when using public derivation
|
||||
with pytest.raises(CallException):
|
||||
self.client.get_public_node([0x80000000 | 111, 42], ecdsa_curve_name='ed25519')
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
# This file is part of the TREZOR project.
|
||||
#
|
||||
# Copyright (C) 2012-2016 Marek Palatinus <slush@satoshilabs.com>
|
||||
# Copyright (C) 2012-2016 Pavol Rusnak <stick@satoshilabs.com>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .common import *
|
||||
from trezorlib import messages as proto
|
||||
from trezorlib.tx_api import TxApiBitcoinGold
|
||||
from trezorlib.ckd_public import deserialize
|
||||
from trezorlib.client import CallException
|
||||
|
||||
|
||||
# All data taken from T1
|
||||
class TestMsgSigntxBitcoinGold(TrezorTest):
|
||||
|
||||
def test_send_bitcoin_gold_change(self):
|
||||
self.setup_mnemonic_allallall()
|
||||
self.client.set_tx_api(TxApiBitcoinGold)
|
||||
inp1 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("44'/156'/0'/0/0"),
|
||||
amount=1995344,
|
||||
prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'),
|
||||
prev_index=0,
|
||||
script_type=proto.InputScriptType.SPENDADDRESS,
|
||||
)
|
||||
out1 = proto.TxOutputType(
|
||||
address_n=self.client.expand_path("44'/156'/0'/1/0"),
|
||||
amount=1896050,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
out2 = proto.TxOutputType(
|
||||
address='GfDB1tvjfm3bukeoBTtfNqrJVFohS2kCTe',
|
||||
amount=73452,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
with self.client:
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXFINISHED),
|
||||
])
|
||||
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin Gold', [inp1], [out1, out2])
|
||||
|
||||
assert hexlify(serialized_tx) == b'010000000185c9dd4ae1071affd77d90b9d03c1b5fdd7c62cf30a9bb8230ad766cf06b5225000000006b483045022100963904da0731b71ce468afd45366dd80fbff566ec0d39c1161ab85d17459c7ca02202f5c24a7a7272d98b14a3f5bc000c7cde8ac0eb773f20f4c3131518186cc98854121023bd0ec4022d12d0106c5b7308a25572953ba1951f576f691354a7b147ee0cc1fffffffff0272ee1c00000000001976a9141c82b9c11f193ad82413caadc0955730572b50ae88acec1e0100000000001976a914ea5f904d195079a350b534db4446433b3cec222e88ac00000000'
|
||||
|
||||
def test_send_bitcoin_gold_nochange(self):
|
||||
self.setup_mnemonic_allallall()
|
||||
self.client.set_tx_api(TxApiBitcoinGold)
|
||||
inp1 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("44'/156'/0'/1/0"),
|
||||
amount=1896050,
|
||||
prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'),
|
||||
prev_index=0,
|
||||
script_type=proto.InputScriptType.SPENDADDRESS,
|
||||
)
|
||||
inp2 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("44'/156'/0'/0/1"),
|
||||
# 1LRspCZNFJcbuNKQkXgHMDucctFRQya5a3
|
||||
amount=73452,
|
||||
prev_hash=unhexlify('db77c2461b840e6edbe7f9280043184a98e020d9795c1b65cb7cef2551a8fb18'),
|
||||
prev_index=1,
|
||||
script_type=proto.InputScriptType.SPENDADDRESS,
|
||||
)
|
||||
out1 = proto.TxOutputType(
|
||||
address='GfDB1tvjfm3bukeoBTtfNqrJVFohS2kCTe',
|
||||
amount=1934960,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
with self.client:
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXFINISHED),
|
||||
])
|
||||
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin Gold', [inp1, inp2], [out1])
|
||||
|
||||
assert hexlify(serialized_tx) == b'010000000285c9dd4ae1071affd77d90b9d03c1b5fdd7c62cf30a9bb8230ad766cf06b5225000000006b483045022100928852076c9fab160c07564cd54691af1cbc37fb28f0b7bee7299c7925ef62f0022058856387afecc6508f2f04ecdfd292a13026a5b2107ebdd2cc789bdf8820d552412102a6c3998d0d4e5197ff41aab5c53580253b3b91f583f4c31f7624be7dc83ce15fffffffff18fba85125ef7ccb651b5c79d920e0984a18430028f9e7db6e0e841b46c277db010000006b483045022100faa2f4f01cc95e680349a093923aae0aa2ea01429873555aa8a84bf630ef33a002204c3f4bf567e2d20540c0f71dc278481d6ccb6b95acda2a2f87ce521c79d6b872412102d54a7e5733b1635e5e9442943f48179b1700206b2d1925250ba10f1c86878be8ffffffff0170861d00000000001976a914ea5f904d195079a350b534db4446433b3cec222e88ac00000000'
|
||||
|
||||
def test_attack_change_input(self):
|
||||
self.setup_mnemonic_allallall()
|
||||
self.client.set_tx_api(TxApiBitcoinGold)
|
||||
inp1 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("44'/156'/1000'/0/0"),
|
||||
# 1MH9KKcvdCTY44xVDC2k3fjBbX5Cz29N1q
|
||||
amount=1995344,
|
||||
prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'),
|
||||
prev_index=0,
|
||||
script_type=proto.InputScriptType.SPENDADDRESS,
|
||||
)
|
||||
out1 = proto.TxOutputType(
|
||||
address_n=self.client.expand_path("44'/156'/1000'/1/0"),
|
||||
amount=1896050,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
out2 = proto.TxOutputType(
|
||||
address='GfDB1tvjfm3bukeoBTtfNqrJVFohS2kCTe',
|
||||
amount=73452,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
|
||||
global attack_ctr
|
||||
attack_ctr = 0
|
||||
|
||||
def attack_processor(req, msg):
|
||||
import sys
|
||||
global attack_ctr
|
||||
|
||||
if req.details.tx_hash is not None:
|
||||
return msg
|
||||
|
||||
if req.request_type != proto.RequestType.TXINPUT:
|
||||
return msg
|
||||
|
||||
attack_ctr += 1
|
||||
if attack_ctr <= 1:
|
||||
return msg
|
||||
|
||||
msg.inputs[0].address_n[2] = 1 + 0x80000000
|
||||
return msg
|
||||
|
||||
with self.client:
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.Failure(code=proto.FailureType.ProcessError),
|
||||
])
|
||||
with pytest.raises(CallException):
|
||||
self.client.sign_tx('Bitcoin Gold', [inp1], [out1, out2], debug_processor=attack_processor)
|
||||
|
||||
def test_send_bch_multisig_change(self):
|
||||
self.setup_mnemonic_allallall()
|
||||
self.client.set_tx_api(TxApiBitcoinGold)
|
||||
xpubs = []
|
||||
for n in map(lambda index: self.client.get_public_node(self.client.expand_path("44'/156'/" + str(index) + "'")), range(1, 4)):
|
||||
xpubs.append(n.xpub)
|
||||
|
||||
def getmultisig(chain, nr, signatures=[b'', b'', b''], xpubs=xpubs):
|
||||
return proto.MultisigRedeemScriptType(
|
||||
pubkeys=list(map(lambda xpub: proto.HDNodePathType(node=deserialize(xpub), address_n=[chain, nr]), xpubs)),
|
||||
signatures=signatures,
|
||||
m=2,
|
||||
)
|
||||
inp1 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("44'/156'/3'/0/0"),
|
||||
multisig=getmultisig(0, 0),
|
||||
# 33Ju286QvonBz5N1V754ZekQv4GLJqcc5R
|
||||
amount=48490,
|
||||
prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'),
|
||||
prev_index=0,
|
||||
script_type=proto.InputScriptType.SPENDMULTISIG,
|
||||
)
|
||||
out1 = proto.TxOutputType(
|
||||
address='GfDB1tvjfm3bukeoBTtfNqrJVFohS2kCTe',
|
||||
amount=24000,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
out2 = proto.TxOutputType(
|
||||
address_n=self.client.expand_path("44'/156'/3'/1/0"),
|
||||
multisig=getmultisig(1, 0),
|
||||
script_type=proto.OutputScriptType.PAYTOMULTISIG,
|
||||
amount=24000
|
||||
)
|
||||
with self.client:
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXFINISHED),
|
||||
])
|
||||
(signatures1, serialized_tx) = self.client.sign_tx('Bitcoin Gold', [inp1], [out1, out2])
|
||||
|
||||
assert hexlify(signatures1[0]) == b'3045022100b1594f3b186d0dedbf61e53a1c407b1e0747098b7375941df85af045040f578e022013ba1893eb9e2fd854dd07073a83b261cf4beba76f66b07742e462b4088a7e4a'
|
||||
|
||||
inp1 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("44'/156'/1'/0/0"),
|
||||
multisig=getmultisig(0, 0, [b'', b'', signatures1[0]]),
|
||||
# 33Ju286QvonBz5N1V754ZekQv4GLJqcc5R
|
||||
amount=48490,
|
||||
prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'),
|
||||
prev_index=0,
|
||||
script_type=proto.InputScriptType.SPENDMULTISIG,
|
||||
)
|
||||
out2.address_n[2] = 1 + 0x80000000
|
||||
|
||||
with self.client:
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXFINISHED),
|
||||
])
|
||||
(signatures1, serialized_tx) = self.client.sign_tx('Bitcoin Gold', [inp1], [out1, out2])
|
||||
|
||||
assert hexlify(signatures1[0]) == b'3044022006da8dbd14e6656ac8dcb956f4c0498574e88680eaeceb2cbafd8d2b2329d8cc02200972d076d444c5ff8f2ab18e14d8249ab661cb9c53335039bedcde037a40d747'
|
||||
assert hexlify(serialized_tx) == b'010000000185c9dd4ae1071affd77d90b9d03c1b5fdd7c62cf30a9bb8230ad766cf06b522500000000fdfd0000473044022006da8dbd14e6656ac8dcb956f4c0498574e88680eaeceb2cbafd8d2b2329d8cc02200972d076d444c5ff8f2ab18e14d8249ab661cb9c53335039bedcde037a40d74741483045022100b1594f3b186d0dedbf61e53a1c407b1e0747098b7375941df85af045040f578e022013ba1893eb9e2fd854dd07073a83b261cf4beba76f66b07742e462b4088a7e4a414c69522102290e6649574d17938c1ecb959ae92954f9ee48e1bd5b73f35ea931a3ab8a6087210379e0107b173e2c143426760627128c5eea3f862e8df92f3c2558eeeae4e347842103ff1746ca7dcf9e5c2eea9a73779b7c5bafed549f45cf3638a94cdf1e89c7f28f53aeffffffff02c05d0000000000001976a914ea5f904d195079a350b534db4446433b3cec222e88acc05d00000000000017a91445e917e46815d2b38d3f1cf072e63dd4f3b7a7e38700000000'
|
||||
|
||||
def test_send_p2sh(self):
|
||||
self.setup_mnemonic_allallall()
|
||||
self.client.set_tx_api(TxApiBitcoinGold)
|
||||
inp1 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("49'/156'/0'/1/0"),
|
||||
amount=123456789,
|
||||
prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'),
|
||||
prev_index=0,
|
||||
script_type=proto.InputScriptType.SPENDP2SHWITNESS,
|
||||
)
|
||||
out1 = proto.TxOutputType(
|
||||
address='GfDB1tvjfm3bukeoBTtfNqrJVFohS2kCTe',
|
||||
amount=12300000,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
out2 = proto.TxOutputType(
|
||||
address='GZFLExxrvWFuFT1xRzhfwQWSE2bPDedBfn',
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
amount=123456789 - 11000 - 12300000,
|
||||
)
|
||||
with self.client:
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXFINISHED),
|
||||
])
|
||||
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin Gold', [inp1], [out1, out2])
|
||||
|
||||
assert hexlify(serialized_tx) == b'0100000000010185c9dd4ae1071affd77d90b9d03c1b5fdd7c62cf30a9bb8230ad766cf06b52250000000017160014b5355d001e720d8f4513da00ff2bba4dcf9d39fcffffffff02e0aebb00000000001976a914ea5f904d195079a350b534db4446433b3cec222e88ac3df39f06000000001976a914a8f757819ec6779409f45788f7b4a0e8f51ec50488ac02473044022073fcbf2876f073f78923ab427f14de5b2a0fbeb313a9b2b650b3567061f242a702202f45fc22c501108ff6222afe3aca7da9d8c7dc860f9cda335bef31fa184e7bef412102ecea08b559fc5abd009acf77cfae13fa8a3b1933e3e031956c65c12cec8ca3e300000000'
|
||||
|
||||
def test_send_p2sh_witness_change(self):
|
||||
self.setup_mnemonic_allallall()
|
||||
self.client.set_tx_api(TxApiBitcoinGold)
|
||||
inp1 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("49'/156'/0'/1/0"),
|
||||
amount=123456789,
|
||||
prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'),
|
||||
prev_index=0,
|
||||
script_type=proto.InputScriptType.SPENDP2SHWITNESS,
|
||||
)
|
||||
out1 = proto.TxOutputType(
|
||||
address='GfDB1tvjfm3bukeoBTtfNqrJVFohS2kCTe',
|
||||
amount=12300000,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
out2 = proto.TxOutputType(
|
||||
address_n=self.client.expand_path("49'/156'/0'/1/0"),
|
||||
script_type=proto.OutputScriptType.PAYTOP2SHWITNESS,
|
||||
amount=123456789 - 11000 - 12300000,
|
||||
)
|
||||
with self.client:
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=1)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXFINISHED),
|
||||
])
|
||||
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin Gold', [inp1], [out1, out2])
|
||||
|
||||
# print(hexlify(serialized_tx))
|
||||
# assert False
|
||||
assert hexlify(serialized_tx) == b'0100000000010185c9dd4ae1071affd77d90b9d03c1b5fdd7c62cf30a9bb8230ad766cf06b52250000000017160014b5355d001e720d8f4513da00ff2bba4dcf9d39fcffffffff02e0aebb00000000001976a914ea5f904d195079a350b534db4446433b3cec222e88ac3df39f060000000017a9140cd03822b799a452c106d1b3771844a067b17f118702483045022100d79b33384c686d8dd40ad5f84f46691d30994992c1cb42e934c2a625d86cb2f902206859805a9a98ba140b71a9d4b9a6b8df94a9424f9c40f3bd804149fd6e278d63412102ecea08b559fc5abd009acf77cfae13fa8a3b1933e3e031956c65c12cec8ca3e300000000'
|
||||
|
||||
def test_send_multisig_1(self):
|
||||
self.setup_mnemonic_allallall()
|
||||
self.client.set_tx_api(TxApiBitcoinGold)
|
||||
nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4))
|
||||
multisig = proto.MultisigRedeemScriptType(
|
||||
pubkeys=list(map(lambda n: proto.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 0]), nodes)),
|
||||
signatures=[b'', b'', b''],
|
||||
m=2,
|
||||
)
|
||||
|
||||
inp1 = proto.TxInputType(
|
||||
address_n=self.client.expand_path("999'/1'/1'/2/0"),
|
||||
prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'),
|
||||
prev_index=1,
|
||||
script_type=proto.InputScriptType.SPENDP2SHWITNESS,
|
||||
multisig=multisig,
|
||||
amount=1610436
|
||||
)
|
||||
|
||||
out1 = proto.TxOutputType(address='GfDB1tvjfm3bukeoBTtfNqrJVFohS2kCTe',
|
||||
amount=1605000,
|
||||
script_type=proto.OutputScriptType.PAYTOADDRESS)
|
||||
|
||||
with self.client:
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXFINISHED),
|
||||
])
|
||||
(signatures1, _) = self.client.sign_tx('Bitcoin Gold', [inp1], [out1])
|
||||
# store signature
|
||||
inp1.multisig.signatures[0] = signatures1[0]
|
||||
# sign with third key
|
||||
inp1.address_n[2] = 0x80000003
|
||||
self.client.set_expected_responses([
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput),
|
||||
proto.ButtonRequest(code=proto.ButtonRequestType.SignTx),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXOUTPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0)),
|
||||
proto.TxRequest(request_type=proto.RequestType.TXFINISHED),
|
||||
])
|
||||
(signatures2, serialized_tx) = self.client.sign_tx('Bitcoin Gold', [inp1], [out1])
|
||||
|
||||
assert hexlify(serialized_tx) == b'0100000000010185c9dd4ae1071affd77d90b9d03c1b5fdd7c62cf30a9bb8230ad766cf06b522501000000232200201e8dda334f11171190b3da72e526d441491464769679a319a2f011da5ad312a1ffffffff01887d1800000000001976a914ea5f904d195079a350b534db4446433b3cec222e88ac0400483045022100e728485c8337f9a09ebbf36edc0fef10f8bcf5c1ba601b7d8ba43a9250a898f002206b9e3401c297f9ab9afb7f1be59bb342db53b5b65aff7c557e3109679697df0f41473044022062ea69ecdc07d0dadc1971fbda50a629a56dd30f431db26327428f4992601ce602204a1c8ab9c7d81c36cb6f819109a26f9baaa9607b8d37bff5e24eee6fab4a04e441695221038e81669c085a5846e68e03875113ddb339ecbb7cb11376d4163bca5dc2e2a0c1210348c5c3be9f0e6cf1954ded1c0475beccc4d26aaa9d0cce2dd902538ff1018a112103931140ebe0fbbb7df0be04ed032a54e9589e30339ba7bbb8b0b71b15df1294da53ae00000000'
|
|
@ -0,0 +1 @@
|
|||
{"txid": "25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985", "version": 1, "locktime": 0, "vin": [{"coinbase": "03b4e407005a2d4e4f4d50212068747470733a2f2f6769746875622e636f6d2f6a6f7368756179616275742f7a2d6e6f6d70", "sequence": 4294967295, "n": 0}], "vout": [{"value": "12.52382934", "n": 0, "scriptPubKey": {"hex": "76a9140cb60a52559620e5de9a297612d49f55f7fd14ea88ac", "asm": "OP_DUP OP_HASH160 0cb60a52559620e5de9a297612d49f55f7fd14ea OP_EQUALVERIFY OP_CHECKSIG", "addresses": ["GK18bp4UzC6wqYKKNLkaJ3hzQazTc3TWBw"], "type": "pubkeyhash"}, "spentTxId": null, "spentIndex": null, "spentHeight": null}, {"value": "0.00000000", "n": 1, "scriptPubKey": {"hex": "6a24aa21a9eddb3ac2bba12721c8db157ba6b522196093d3a27a8083591a2b785a230a1d254f", "asm": "OP_RETURN aa21a9eddb3ac2bba12721c8db157ba6b522196093d3a27a8083591a2b785a230a1d254f"}, "spentTxId": null, "spentIndex": null, "spentHeight": null}], "blockhash": "000000000b9f4d15e03603463f536b7b9da695580ae8b8bcdac5970195b586f4", "blockheight": 517300, "confirmations": 3, "time": 1520433267, "blocktime": 1520433267, "isCoinBase": true, "valueOut": "12.52382934", "size": 191}
|
|
@ -0,0 +1 @@
|
|||
{"txid": "db77c2461b840e6edbe7f9280043184a98e020d9795c1b65cb7cef2551a8fb18", "version": 1, "locktime": 0, "vin": [{"txid": "52fb172f86926a89a16edf55bc9baec3929149b7cd2d2389be3c7d08d744d300", "vout": 1, "sequence": 4294967295, "n": 0, "scriptSig": {"hex": "4830450221008bff524a092086372a19b924f41fa7fa2a5523bf42a4801b9503fcdfff2094e8022000f223a032bd0d7fee31d5663cd5cf86b82533bda6871366d519a68deae1042341210222c6760cc54de6fd7f2a40207a13137d497c7cdb472376523700d8ea88275a96", "asm": "30450221008bff524a092086372a19b924f41fa7fa2a5523bf42a4801b9503fcdfff2094e8022000f223a032bd0d7fee31d5663cd5cf86b82533bda6871366d519a68deae10423[ALL|FORKID] 0222c6760cc54de6fd7f2a40207a13137d497c7cdb472376523700d8ea88275a96"}, "addr": "GgdFx96JSR3nbyhtgxqsUessZLarxLWA3J", "valueSat": 2128176, "value": "0.02128176", "doubleSpentTxID": null}, {"txid": "371eb4feaa4085b378bb825f3c1b457867c24211ee838584b1adac226bba654b", "vout": 0, "sequence": 4294967295, "n": 1, "scriptSig": {"hex": "47304402206aee1d853479782029755dd3c360dbd963e6390da12ddf2c2c38314692510385022040c9c01253a77bc33ac11ce0e8c187ab4f2d78346c0b222a87b1f00fea6b212941210222c6760cc54de6fd7f2a40207a13137d497c7cdb472376523700d8ea88275a96", "asm": "304402206aee1d853479782029755dd3c360dbd963e6390da12ddf2c2c38314692510385022040c9c01253a77bc33ac11ce0e8c187ab4f2d78346c0b222a87b1f00fea6b2129[ALL|FORKID] 0222c6760cc54de6fd7f2a40207a13137d497c7cdb472376523700d8ea88275a96"}, "addr": "GgdFx96JSR3nbyhtgxqsUessZLarxLWA3J", "valueSat": 36323851, "value": "0.36323851", "doubleSpentTxID": null}], "vout": [{"value": "0.38448607", "n": 0, "scriptPubKey": {"hex": "76a914b79bbff2766286a99129642d70912c6a4223c62b88ac", "asm": "OP_DUP OP_HASH160 b79bbff2766286a99129642d70912c6a4223c62b OP_EQUALVERIFY OP_CHECKSIG", "addresses": ["GaakevAd8FJuJQootpkdcS2ocizaXMnFdt"], "type": "pubkeyhash"}, "spentTxId": null, "spentIndex": null, "spentHeight": null}], "blockhash": "000000000b9f4d15e03603463f536b7b9da695580ae8b8bcdac5970195b586f4", "blockheight": 517300, "confirmations": 3, "time": 1520433267, "blocktime": 1520433267, "valueOut": "0.38448607", "size": 339, "valueIn": "0.38452027", "fees": "0.0000342"}
|
|
@ -0,0 +1,13 @@
|
|||
import mock
|
||||
|
||||
from trezorlib.transport import all_transports
|
||||
|
||||
|
||||
def test_all_transports_without_hid():
|
||||
# import all transports, assume this doesn't fail
|
||||
transports_ref = all_transports()
|
||||
# also shouldn't fail when bridge transport is missing
|
||||
with mock.patch.dict('sys.modules', {'trezorlib.transport.bridge': None}):
|
||||
transports = all_transports()
|
||||
# there should now be less transports
|
||||
assert len(transports_ref) > len(transports)
|
|
@ -22,17 +22,6 @@ import binascii
|
|||
import struct
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,):
|
||||
def byteindex(data, index):
|
||||
return ord(data[index])
|
||||
|
||||
def iterbytes(data):
|
||||
return (ord(char) for char in data)
|
||||
else:
|
||||
def byteindex(data, index):
|
||||
return data[index]
|
||||
iterbytes = iter
|
||||
|
||||
|
||||
def Hash(data):
|
||||
return hashlib.sha256(hashlib.sha256(data).digest()).digest()
|
||||
|
@ -52,8 +41,8 @@ def hash_160_to_bc_address(h160, address_type):
|
|||
|
||||
|
||||
def compress_pubkey(public_key):
|
||||
if byteindex(public_key, 0) == 4:
|
||||
return bytes((byteindex(public_key, 64) & 1) + 2) + public_key[1:33]
|
||||
if public_key[0] == 4:
|
||||
return bytes((public_key[64] & 1) + 2) + public_key[1:33]
|
||||
raise ValueError("Pubkey is already compressed")
|
||||
|
||||
|
||||
|
@ -73,7 +62,7 @@ def b58encode(v):
|
|||
""" encode v, which is a string of bytes, to base58."""
|
||||
|
||||
long_value = 0
|
||||
for c in iterbytes(v):
|
||||
for c in v:
|
||||
long_value = long_value * 256 + c
|
||||
|
||||
result = ''
|
||||
|
@ -86,7 +75,7 @@ def b58encode(v):
|
|||
# Bitcoin does a little leading-zero-compression:
|
||||
# leading 0-bytes in the input become leading-1s
|
||||
nPad = 0
|
||||
for c in iterbytes(v):
|
||||
for c in v:
|
||||
if c == 0:
|
||||
nPad += 1
|
||||
else:
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
# This file is part of the TREZOR project.
|
||||
#
|
||||
# Copyright (C) 2012-2016 Marek Palatinus <slush@satoshilabs.com>
|
||||
# Copyright (C) 2012-2016 Pavol Rusnak <stick@satoshilabs.com>
|
||||
# Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.com>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
||||
class TransportException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Transport(object):
|
||||
|
||||
def __init__(self):
|
||||
self.session_counter = 0
|
||||
|
||||
def session_begin(self):
|
||||
if self.session_counter == 0:
|
||||
self.open()
|
||||
self.session_counter += 1
|
||||
|
||||
def session_end(self):
|
||||
self.session_counter = max(self.session_counter - 1, 0)
|
||||
if self.session_counter == 0:
|
||||
self.close()
|
||||
|
||||
def open(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def close(self):
|
||||
raise NotImplementedError
|
|
@ -0,0 +1,118 @@
|
|||
# This file is part of the TREZOR project.
|
||||
#
|
||||
# Copyright (C) 2012-2016 Marek Palatinus <slush@satoshilabs.com>
|
||||
# Copyright (C) 2012-2016 Pavol Rusnak <stick@satoshilabs.com>
|
||||
# Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.com>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
class TransportException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Transport(object):
|
||||
|
||||
def __init__(self):
|
||||
self.session_counter = 0
|
||||
|
||||
def __str__(self):
|
||||
return self.get_path()
|
||||
|
||||
def get_path(self):
|
||||
return '{}:{}'.format(self.PATH_PREFIX, self.device)
|
||||
|
||||
def session_begin(self):
|
||||
if self.session_counter == 0:
|
||||
self.open()
|
||||
self.session_counter += 1
|
||||
|
||||
def session_end(self):
|
||||
self.session_counter = max(self.session_counter - 1, 0)
|
||||
if self.session_counter == 0:
|
||||
self.close()
|
||||
|
||||
def open(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def close(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def enumerate(cls):
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def find_by_path(cls, path, prefix_search=False):
|
||||
for device in cls.enumerate():
|
||||
if path is None or device.get_path() == path \
|
||||
or (prefix_search and device.get_path().startswith(path)):
|
||||
return device
|
||||
|
||||
raise TransportException('{} device not found: {}'.format(cls.PATH_PREFIX, path))
|
||||
|
||||
|
||||
def all_transports():
|
||||
transports = []
|
||||
try:
|
||||
from .bridge import BridgeTransport
|
||||
transports.append(BridgeTransport)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
from .hid import HidTransport
|
||||
transports.append(HidTransport)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
from .udp import UdpTransport
|
||||
transports.append(UdpTransport)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
from .webusb import WebUsbTransport
|
||||
transports.append(WebUsbTransport)
|
||||
except:
|
||||
pass
|
||||
|
||||
return transports
|
||||
|
||||
|
||||
def enumerate_devices():
|
||||
return [device
|
||||
for transport in all_transports()
|
||||
for device in transport.enumerate()]
|
||||
|
||||
|
||||
def get_transport(path=None, prefix_search=False):
|
||||
if path is None:
|
||||
try:
|
||||
return enumerate_devices()[0]
|
||||
except IndexError:
|
||||
raise Exception("No TREZOR device found") from None
|
||||
|
||||
# Find whether B is prefix of A (transport name is part of the path)
|
||||
# or A is prefix of B (path is a prefix, or a name, of transport).
|
||||
# This naively expects that no two transports have a common prefix.
|
||||
def match_prefix(a, b):
|
||||
return a.startswith(b) or b.startswith(a)
|
||||
|
||||
transports = [t for t in all_transports() if match_prefix(path, t.PATH_PREFIX)]
|
||||
if transports:
|
||||
return transports[0].find_by_path(path, prefix_search=prefix_search)
|
||||
|
||||
raise Exception("Unknown path prefix '%s'" % prefix)
|
|
@ -17,17 +17,15 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import requests
|
||||
import binascii
|
||||
from io import BytesIO
|
||||
import struct
|
||||
|
||||
from . import mapping
|
||||
from . import messages
|
||||
from . import protobuf
|
||||
from .transport import Transport, TransportException
|
||||
from .. import mapping
|
||||
from .. import messages
|
||||
from .. import protobuf
|
||||
from . import Transport, TransportException
|
||||
|
||||
TREZORD_HOST = 'http://127.0.0.1:21325'
|
||||
|
||||
|
@ -45,16 +43,13 @@ class BridgeTransport(Transport):
|
|||
HEADERS = {'Origin': 'https://python.trezor.io'}
|
||||
|
||||
def __init__(self, device):
|
||||
super(BridgeTransport, self).__init__()
|
||||
super().__init__()
|
||||
|
||||
self.device = device
|
||||
self.conn = requests.Session()
|
||||
self.session = None
|
||||
self.response = None
|
||||
|
||||
def __str__(self):
|
||||
return self.get_path()
|
||||
|
||||
def get_path(self):
|
||||
return '%s:%s' % (self.PATH_PREFIX, self.device['path'])
|
||||
|
||||
|
@ -68,17 +63,6 @@ class BridgeTransport(Transport):
|
|||
except:
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def find_by_path(cls, path):
|
||||
if isinstance(path, bytes):
|
||||
path = path.decode()
|
||||
path = path.replace('%s:' % cls.PATH_PREFIX, '')
|
||||
|
||||
for transport in BridgeTransport.enumerate():
|
||||
if path is None or transport.device['path'] == path:
|
||||
return transport
|
||||
raise TransportException('Bridge device not found')
|
||||
|
||||
def open(self):
|
||||
r = self.conn.post(TREZORD_HOST + '/acquire/%s/null' % self.device['path'], headers=self.HEADERS)
|
||||
if r.status_code != 200:
|
|
@ -16,22 +16,20 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import time
|
||||
import hid
|
||||
import os
|
||||
|
||||
from .protocol_v1 import ProtocolV1
|
||||
from .protocol_v2 import ProtocolV2
|
||||
from .transport import Transport, TransportException
|
||||
from ..protocol_v1 import ProtocolV1
|
||||
from ..protocol_v2 import ProtocolV2
|
||||
from . import Transport, TransportException
|
||||
|
||||
DEV_TREZOR1 = (0x534c, 0x0001)
|
||||
DEV_TREZOR2 = (0x1209, 0x53c1)
|
||||
DEV_TREZOR2_BL = (0x1209, 0x53c0)
|
||||
|
||||
|
||||
class HidHandle(object):
|
||||
class HidHandle:
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
@ -79,9 +77,6 @@ class HidTransport(Transport):
|
|||
self.hid = hid_handle
|
||||
self.hid_version = None
|
||||
|
||||
def __str__(self):
|
||||
return self.get_path()
|
||||
|
||||
def get_path(self):
|
||||
return "%s:%s" % (self.PATH_PREFIX, self.device['path'].decode())
|
||||
|
||||
|
@ -100,17 +95,6 @@ class HidTransport(Transport):
|
|||
devices.append(HidTransport(dev))
|
||||
return devices
|
||||
|
||||
@classmethod
|
||||
def find_by_path(cls, path):
|
||||
if isinstance(path, str):
|
||||
path = path.encode()
|
||||
path = path.replace(b'%s:' % cls.PATH_PREFIX.encode(), b'')
|
||||
|
||||
for transport in HidTransport.enumerate():
|
||||
if path is None or transport.device['path'] == path:
|
||||
return transport
|
||||
raise TransportException('HID device not found')
|
||||
|
||||
def find_debug(self):
|
||||
if isinstance(self.protocol, ProtocolV2):
|
||||
# For v2 protocol, lets use the same HID interface, but with a different session
|
|
@ -16,14 +16,12 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import socket
|
||||
|
||||
from .protocol_v1 import ProtocolV1
|
||||
from .protocol_v2 import ProtocolV2
|
||||
from .transport import Transport, TransportException
|
||||
from ..protocol_v1 import ProtocolV1
|
||||
from ..protocol_v2 import ProtocolV2
|
||||
from . import Transport, TransportException
|
||||
|
||||
|
||||
class UdpTransport(Transport):
|
||||
|
@ -48,26 +46,41 @@ class UdpTransport(Transport):
|
|||
self.protocol = protocol
|
||||
self.socket = None
|
||||
|
||||
def __str__(self):
|
||||
return self.get_path()
|
||||
|
||||
def get_path(self):
|
||||
return "%s:%s:%s" % ((self.PATH_PREFIX,) + self.device)
|
||||
|
||||
@staticmethod
|
||||
def enumerate():
|
||||
devices = []
|
||||
d = UdpTransport("%s:%d" % (UdpTransport.DEFAULT_HOST, UdpTransport.DEFAULT_PORT))
|
||||
d.open()
|
||||
if d._ping():
|
||||
devices.append(d)
|
||||
d.close()
|
||||
return devices
|
||||
def find_debug(self):
|
||||
host, port = self.device
|
||||
return UdpTransport('{}:{}'.format(host, port + 1), self.protocol)
|
||||
|
||||
@classmethod
|
||||
def find_by_path(cls, path):
|
||||
path = path.replace('%s:' % cls.PATH_PREFIX, '')
|
||||
return UdpTransport(path)
|
||||
def _try_path(cls, path):
|
||||
d = cls(path)
|
||||
try:
|
||||
d.open()
|
||||
if d._ping():
|
||||
return d
|
||||
else:
|
||||
raise TransportException('No TREZOR device found at address {}'.format(path))
|
||||
finally:
|
||||
d.close()
|
||||
|
||||
@classmethod
|
||||
def enumerate(cls):
|
||||
devices = []
|
||||
default_path = '{}:{}'.format(cls.DEFAULT_HOST, cls.DEFAULT_PORT)
|
||||
try:
|
||||
return [cls._try_path(default_path)]
|
||||
except TransportException:
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def find_by_path(cls, path, prefix_search=False):
|
||||
if prefix_search:
|
||||
return super().find_by_path(path, prefix_search)
|
||||
else:
|
||||
path = path.replace('{}:'.format(cls.PATH_PREFIX), '')
|
||||
return cls._try_path(path)
|
||||
|
||||
def open(self):
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
@ -16,16 +16,14 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import time
|
||||
import os
|
||||
import atexit
|
||||
import usb1
|
||||
|
||||
from .protocol_v1 import ProtocolV1
|
||||
from .protocol_v2 import ProtocolV2
|
||||
from .transport import Transport, TransportException
|
||||
from ..protocol_v1 import ProtocolV1
|
||||
from ..protocol_v2 import ProtocolV2
|
||||
from . import Transport, TransportException
|
||||
|
||||
DEV_TREZOR1 = (0x534c, 0x0001)
|
||||
DEV_TREZOR2 = (0x1209, 0x53c1)
|
||||
|
@ -37,7 +35,7 @@ DEBUG_INTERFACE = 1
|
|||
DEBUG_ENDPOINT = 2
|
||||
|
||||
|
||||
class WebUsbHandle(object):
|
||||
class WebUsbHandle:
|
||||
|
||||
def __init__(self, device):
|
||||
self.device = device
|
||||
|
@ -88,9 +86,6 @@ class WebUsbTransport(Transport):
|
|||
self.handle = handle
|
||||
self.debug = debug
|
||||
|
||||
def __str__(self):
|
||||
return self.get_path()
|
||||
|
||||
def get_path(self):
|
||||
return "%s:%s" % (self.PATH_PREFIX, dev_to_str(self.device))
|
||||
|
||||
|
@ -106,17 +101,18 @@ class WebUsbTransport(Transport):
|
|||
continue
|
||||
if not is_vendor_class(dev):
|
||||
continue
|
||||
try:
|
||||
# workaround for issue #223:
|
||||
# on certain combinations of Windows USB drivers and libusb versions,
|
||||
# Trezor is returned twice (possibly because Windows know it as both
|
||||
# a HID and a WebUSB device), and one of the returned devices is
|
||||
# non-functional.
|
||||
dev.getProduct()
|
||||
devices.append(WebUsbTransport(dev))
|
||||
except usb1.USBErrorNotSupported:
|
||||
pass
|
||||
return devices
|
||||
|
||||
@classmethod
|
||||
def find_by_path(cls, path):
|
||||
path = path.replace('%s:' % cls.PATH_PREFIX, '') # Remove prefix from __str__()
|
||||
for transport in WebUsbTransport.enumerate():
|
||||
if path is None or dev_to_str(transport.device) == path:
|
||||
return transport
|
||||
raise TransportException('WebUSB device not found')
|
||||
|
||||
def find_debug(self):
|
||||
if isinstance(self.protocol, ProtocolV2):
|
||||
# TODO test this
|
|
@ -190,7 +190,9 @@ TxApiLitecoin = TxApiInsight(network='insight_litecoin', url='https://ltc-bitcor
|
|||
TxApiDash = TxApiInsight(network='insight_dash', url='https://dash-bitcore1.trezor.io/api/')
|
||||
TxApiZcash = TxApiInsight(network='insight_zcash', url='https://zec-bitcore1.trezor.io/api/', zcash=True)
|
||||
TxApiBcash = TxApiInsight(network='insight_bcash', url='https://bch-bitcore2.trezor.io/api/')
|
||||
TxApiBitcoinGold = TxApiInsight(network='insight_bitcoin_gold', url='https://btg-bitcore2.trezor.io/api/')
|
||||
TxApiDecredTestnet = TxApiInsight(network='insight_decred_testnet', url='https://testnet.decred.org/api/')
|
||||
TxApiDogecoin = TxApiBlockCypher(network='blockcypher_dogecoin', url='https://api.blockcypher.com/v1/doge/main/')
|
||||
TxApiSegnet = TxApiSmartbit(network='smartbit_segnet', url='https://segnet-api.smartbit.com.au/v1/blockchain/')
|
||||
TxApiMonacoin = TxApiInsight(network='insight_monacoin', url='https://mona.insight.monaco-ex.org/insight-api-monacoin/')
|
||||
TxApiBitcoinPrivate = TxApiInsight(network='insight_bitcoin_private', url='https://explorer.btcprivate.org/api/', zcash=True)
|
||||
|
|
Loading…
Reference in New Issue